diff --git a/lib/oauth.js b/lib/oauth.js index 2998ab4..b4873b2 100644 --- a/lib/oauth.js +++ b/lib/oauth.js @@ -13,6 +13,9 @@ exports.OAuth= function(requestUrl, accessUrl, consumerKey, consumerSecret, vers this._accessUrl= accessUrl; this._consumerKey= consumerKey; this._consumerSecret= this._encodeData( consumerSecret ); + if (signatureMethod == "RSA-SHA1") { + this._privateKey = consumerSecret; + } this._version= version; if( authorize_callback === undefined ) { this._authorize_callback= "oob"; @@ -21,7 +24,7 @@ exports.OAuth= function(requestUrl, accessUrl, consumerKey, consumerSecret, vers this._authorize_callback= authorize_callback; } - if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1") + if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1" && signatureMethod != "RSA-SHA1") throw new Error("Un-supported signature method: " + signatureMethod ) this._signatureMethod= signatureMethod; this._nonceSize= nonceSize || 32; @@ -41,9 +44,12 @@ exports.OAuthEcho= function(realm, verify_credentials, consumerKey, consumerSecr this._verifyCredentials = verify_credentials; this._consumerKey= consumerKey; this._consumerSecret= this._encodeData( consumerSecret ); + if (signatureMethod == "RSA-SHA1") { + this._privateKey = consumerSecret; + } this._version= version; - if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1") + if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1" && signatureMethod != "RSA-SHA1") throw new Error("Un-supported signature method: " + signatureMethod ); this._signatureMethod= signatureMethod; this._nonceSize= nonceSize || 32; @@ -198,6 +204,10 @@ exports.OAuth.prototype._createSignature= function(signatureBase, tokenSecret) { if( this._signatureMethod == "PLAINTEXT" ) { hash= key; } + else if (this._signatureMethod == "RSA-SHA1") { + key = this._privateKey || ""; + hash= crypto.createSign("RSA-SHA1").update(signatureBase).sign(key, 'base64'); + } else { if( crypto.Hmac ) { hash = crypto.createHmac("sha1", key).update(signatureBase).digest("base64"); diff --git a/tests/oauth.js b/tests/oauth.js index 99e5bb6..c551e6f 100644 --- a/tests/oauth.js +++ b/tests/oauth.js @@ -2,7 +2,8 @@ var vows = require('vows'), assert = require('assert'), events = require('events'), OAuth= require('../lib/oauth').OAuth, - OAuthEcho= require('../lib/oauth').OAuthEcho; + OAuthEcho= require('../lib/oauth').OAuthEcho, + crypto = require('crypto'); var DummyResponse =function( statusCode ) { this.statusCode= statusCode; @@ -23,6 +24,30 @@ DummyRequest.prototype.end= function(){ this.response.emit('end'); } +//Valid RSA keypair used to test RSA-SHA1 signature method +var RsaPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\n" + +"MIICXQIBAAKBgQDizE4gQP5nPQhzof/Vp2U2DDY3UY/Gxha2CwKW0URe7McxtnmE\n" + +"CrZnT1n/YtfrrCNxY5KMP4o8hMrxsYEe05+1ZGFT68ztms3puUxilU5E3BQMhz1t\n" + +"JMJEGcTt8nZUlM4utli7fHgDtWbhvqvYjRMGn3AjyLOfY8XZvnFkGjipvQIDAQAB\n" + +"AoGAKgk6FcpWHOZ4EY6eL4iGPt1Gkzw/zNTcUsN5qGCDLqDuTq2Gmk2t/zn68VXt\n" + +"tVXDf/m3qN0CDzOBtghzaTZKLGhnSewQ98obMWgPcvAsb4adEEeW1/xigbMiaW2X\n" + +"cu6GhZxY16edbuQ40LRrPoVK94nXQpj8p7w4IQ301Sm8PSECQQD1ZlOj4ugvfhEt\n" + +"exi4WyAaM45fylmN290UXYqZ8SYPI/VliDytIlMfyq5Rv+l+dud1XDPrWOQ0ImgV\n" + +"HJn7uvoZAkEA7JhHNmHF9dbdF9Koj86K2Cl6c8KUu7U7d2BAuB6pPkt8+D8+y4St\n" + +"PaCmN4oP4X+sf5rqBYoXywHlqEei2BdpRQJBAMYgR4cZu7wcXGIL8HlnmROObHSK\n" + +"OqN9z5CRtUV0nPW8YnQG+nYOMG6KhRMbjri750OpnYF100kEPmRNI0VKQIECQE8R\n" + +"fQsRleTYz768ahTVQ9WF1ySErMwmfx8gDcD6jjkBZVxZVpURXAwyehopi7Eix/VF\n" + +"QlxjkBwKIEQi3Ks297kCQQCL9by1bueKDMJO2YX1Brm767pkDKkWtGfPS+d3xMtC\n" + +"KJHHCqrS1V+D5Q89x5wIRHKxE5UMTc0JNa554OxwFORX\n" + +"-----END RSA PRIVATE KEY-----"; + +var RsaPublicKey = "-----BEGIN PUBLIC KEY-----\n" + +"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDizE4gQP5nPQhzof/Vp2U2DDY3\n" + +"UY/Gxha2CwKW0URe7McxtnmECrZnT1n/YtfrrCNxY5KMP4o8hMrxsYEe05+1ZGFT\n" + +"68ztms3puUxilU5E3BQMhz1tJMJEGcTt8nZUlM4utli7fHgDtWbhvqvYjRMGn3Aj\n" + +"yLOfY8XZvnFkGjipvQIDAQAB\n" + +"-----END PUBLIC KEY-----"; + vows.describe('OAuth').addBatch({ 'When newing OAuth': { topic: new OAuth(null, null, null, null, null, null, "PLAINTEXT"), @@ -38,6 +63,20 @@ vows.describe('OAuth').addBatch({ assert.equal( result, "GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal"); } }, + 'When generating the signature with RSA-SHA1': { + topic: new OAuth(null, null, null, RsaPrivateKey, null, null, "RSA-SHA1"), + 'we get a valid oauth signature': function (oa) { + var signatureBase = "GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DRSA-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal"; + var oauthSignature = oa._createSignature(signatureBase, "xyz4992k83j47x0b"); + + assert.equal( oauthSignature, "qS4rhWog7GPgo4ZCJvUdC/1ZAax/Q4Ab9yOBvgxSopvmKUKp5rso+Zda46GbyN2hnYDTiA/g3P/d/YiPWa454BEBb/KWFV83HpLDIoqUUhJnlXX9MqRQQac0oeope4fWbGlfTdL2PXjSFJmvfrzybERD/ZufsFtVrQKS3QBpYiw="); + + //now check that given the public key we can verify this signature + var verifier = crypto.createVerify("RSA-SHA1").update(signatureBase); + var valid = verifier.verify(RsaPublicKey, oauthSignature, 'base64'); + assert.ok( valid, "Signature could not be verified with RSA public key"); + } + }, 'When generating the signature base string with PLAINTEXT': { topic: new OAuth(null, null, null, null, null, null, "PLAINTEXT"), 'we get the expected result string': function (oa) {