Browse Source

Add support for RSA-SHA1 signing.

Tim Fletcher 16 years ago
parent
commit
96bb209e53

+ 5 - 2
Makefile

@@ -25,8 +25,11 @@ clean:
 test: beam_files ebin/oauth_unit.beam
 	@$(ERL) -noshell -s oauth_unit test -s init stop
 
-termie: beam_files ebin/oauth_termie.beam
-	@$(ERL) -noshell -s inets -s oauth_termie test -s init stop
+termie_hmac: beam_files ebin/oauth_termie.beam
+	@$(ERL) -noshell -s inets -s oauth_termie test_hmac -s init stop
+
+termie_rsa: beam_files ebin/oauth_termie.beam
+	@$(ERL) -noshell -s inets -s oauth_termie test_rsa -s init stop
 
 shell: beam_files
 	@$(ERL) -s inets

+ 1 - 1
README.txt

@@ -55,7 +55,7 @@ would be similar to the following:
 
 
 Calling oauth:get or oauth:post returns an HTTP response tuple, as returned
-from http:request/4. Type "make termie", or look at the oauth_termie module
+from http:request/4. Type "make termie_hmac", or look at test/oauth_termie.erl
 for a working example. Thanks Andy!
 
 Alternatively, you can use oauth_request:header/6 to generate an HTTP

+ 9 - 1
src/oauth_consumer.erl

@@ -1,6 +1,6 @@
 -module(oauth_consumer).
 
--export([key/1, new/3, secret/1, signature_method/1]).
+-export([key/1, new/3, secret/1, signature_method/1, signature_method_string/1]).
 
 
 new(Key, Secret, SignatureMethod) ->
@@ -14,3 +14,11 @@ secret(Consumer) ->
 
 signature_method(Consumer) ->
   element(4, Consumer).
+
+signature_method_string(Consumer) ->
+  method_string(signature_method(Consumer)).
+
+method_string({Method, _}) ->
+  Method;
+method_string(Method) ->
+  Method.

+ 8 - 1
src/oauth_crypto.erl

@@ -1,6 +1,6 @@
 -module(oauth_crypto).
 
--export([plaintext_signature/2, hmac_signature/3]).
+-export([plaintext_signature/2, hmac_signature/3, rsa_signature/2]).
 
 
 plaintext_signature(ConsumerSecret, TokenSecret) ->
@@ -12,3 +12,10 @@ hmac_signature(BaseString, ConsumerSecret, TokenSecret) ->
   TS = fmt:percent_encode(TokenSecret),
   Key = fmt:sprintf("%s&%s", [CS, TS]),
   base64:encode_to_string(crypto:sha_mac(Key, BaseString)).
+
+rsa_signature(BaseString, Path) when is_list(Path) ->
+  {ok, [Info]} = public_key:pem_to_der(Path),
+  {ok, PrivateKey} = public_key:decode_private_key(Info),
+  rsa_signature(list_to_binary(BaseString), PrivateKey);
+rsa_signature(BaseString, PrivateKey) ->
+  base64:encode_to_string(public_key:sign(BaseString, PrivateKey)).

+ 5 - 2
src/oauth_request.erl

@@ -43,7 +43,10 @@ signature(Params, Request, Consumer, TokenSecret) ->
       oauth_crypto:plaintext_signature(ConsumerSecret, TokenSecret);
     "HMAC-SHA1" ->
       BaseString = oauth_base:string(method(Request), url(Request), Params),
-      oauth_crypto:hmac_signature(BaseString, ConsumerSecret, TokenSecret)
+      oauth_crypto:hmac_signature(BaseString, ConsumerSecret, TokenSecret);
+    {"RSA-SHA1", PrivateKey} ->
+      BaseString = oauth_base:string(method(Request), url(Request), Params),
+      oauth_crypto:rsa_signature(BaseString, PrivateKey)
   end.
 
 oauth_params(Request, Consumer, Token) ->
@@ -54,7 +57,7 @@ set_consumer_key(Params, Consumer, Token) ->
   set_signature_method([Param|Params], Consumer, Token).
 
 set_signature_method(Params, Consumer, Token) ->
-  Method = oauth_consumer:signature_method(Consumer),
+  Method = oauth_consumer:signature_method_string(Consumer),
   set_token([{oauth_signature_method, Method}|Params], Token).
 
 set_token(Params, []) ->

+ 8 - 2
test/oauth_termie.erl

@@ -5,8 +5,11 @@
 % cf. http://term.ie/oauth/example/
 
 
-test() ->
-  test(oauth_consumer:new("key", "secret", "HMAC-SHA1")).
+test_hmac() ->
+  test(consumer("HMAC-SHA1")).
+
+test_rsa() ->
+  test(consumer({"RSA-SHA1", "test/rsa_private_key.pem"})).
 
 test(Consumer) ->
   RequestTokenURL = "http://term.ie/oauth/example/request_token.php",
@@ -22,5 +25,8 @@ test(Consumer, AccessTokenPair, EchoParams) ->
   {ok, {_,_,Data}} = tee(oauth:get(EchoURL, Consumer, AccessTokenPair, EchoParams)),
   tee(lists:keysort(1, oauth_params:from_string(Data))).
 
+consumer(SignatureMethod) ->
+  oauth_consumer:new("key", "secret", SignatureMethod).
+
 tee(X) ->
   error_logger:info_msg("~p~n~n", [X]), X.

+ 15 - 0
test/oauth_unit.erl

@@ -78,3 +78,18 @@ hmac_signature_test_() -> [
     "oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal"
   ])
 ].
+
+rsa_signature_test() ->
+  BaseString = lists:concat([
+    "GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacaction.jpg",
+    "%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3D13917289812797014437",
+    "%26oauth_signature_method%3DRSA-SHA1%26oauth_timestamp%3D1196666512",
+    "%26oauth_version%3D1.0%26size%3Doriginal"
+  ]),
+  ExpectedSignature = lists:concat([
+    "jvTp/wX1TYtByB1m+Pbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2/",
+    "9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW//",
+    "e+RinhejgCuzoH26dyF8iY2ZZ/5D1ilgeijhV/vBka5twt399mXwaYdCwFYE="
+  ]),
+  Signature = oauth_crypto:rsa_signature(BaseString, "test/rsa_private_key.pem"),
+  ?assertEqual(ExpectedSignature, Signature).

+ 15 - 0
test/rsa_private_key.pem

@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQC0YjCwIfYoprq/FQO6lb3asXrxLlJFuCvtinTF5p0GxvQGu5O3
+gYytUvtC2JlYzypSRjVxwxrsuRcP3e641SdASwfrmzyvIgP08N4S0IFzEURkV1wp
+/IpH7kH41EtbmUmrXSwfNZsnQRE5SYSOhh+LcK2wyQkdgcMv11l4KoBkcwIDAQAB
+AoGAWFlbZXlM2r5G6z48tE+RTKLvB1/btgAtq8vLw/5e3KnnbcDD6fZO07m4DRaP
+jRryrJdsp8qazmUdcY0O1oK4FQfpprknDjP+R1XHhbhkQ4WEwjmxPstZMUZaDWF5
+8d3otc23mCzwh3YcUWFu09KnMpzZsK59OfyjtkS44EDWpbECQQDXgN0ODboKsuEA
+VAhAtPUqspU9ivRa6yLai9kCnPb9GcztrsJZQm4NHcKVbmD2F2L4pDRx4Pmglhfl
+V7G/a6T7AkEA1kfU0+DkXc6I/jXHJ6pDLA5s7dBHzWgDsBzplSdkVQbKT3MbeYje
+ByOxzXhulOWLBQW/vxmW4HwU95KTRlj06QJASPoBYY3yb0cN/J94P/lHgJMDCNky
+UEuJ/PoYndLrrN/8zow8kh91xwlJ6HJ9cTiQMmTgwaOOxPuu0eI1df4M2wJBAJJS
+WrKUT1z/O+zbLDOZwGTFNPzvzRgmft4z4A1J6OlmyZ+XKpvDKloVtcRpCJoEZPn5
+AwaroquID4k/PfI7rIECQHeWa6+kPADv9IrK/92mujujS0MSEiynDw5NjTnHAH0v
+8TrXzs+LCWDN/gbOCKPfnWRkgwgOeC8NN3h0zUIIUtA=
+-----END RSA PRIVATE KEY-----

+ 16 - 0
test/rsa_private_key.pkcs8

@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALRiMLAh9iimur8V
+A7qVvdqxevEuUkW4K+2KdMXmnQbG9Aa7k7eBjK1S+0LYmVjPKlJGNXHDGuy5Fw/d
+7rjVJ0BLB+ubPK8iA/Tw3hLQgXMRRGRXXCn8ikfuQfjUS1uZSatdLB81mydBETlJ
+hI6GH4twrbDJCR2Bwy/XWXgqgGRzAgMBAAECgYBYWVtleUzavkbrPjy0T5FMou8H
+X9u2AC2ry8vD/l7cqedtwMPp9k7TubgNFo+NGvKsl2ynyprOZR1xjQ7WgrgVB+mm
+uScOM/5HVceFuGRDhYTCObE+y1kxRloNYXnx3ei1zbeYLPCHdhxRYW7T0qcynNmw
+rn05/KO2RLjgQNalsQJBANeA3Q4Nugqy4QBUCEC09SqylT2K9FrrItqL2QKc9v0Z
+zO2uwllCbg0dwpVuYPYXYvikNHHg+aCWF+VXsb9rpPsCQQDWR9TT4ORdzoj+Nccn
+qkMsDmzt0EfNaAOwHOmVJ2RVBspPcxt5iN4HI7HNeG6U5YsFBb+/GZbgfBT3kpNG
+WPTpAkBI+gFhjfJvRw38n3g/+UeAkwMI2TJQS4n8+hid0uus3/zOjDySH3XHCUno
+cn1xOJAyZODBo47E+67R4jV1/gzbAkEAklJaspRPXP877NssM5nAZMU0/O/NGCZ+
+3jPgDUno6WbJn5cqm8MqWhW1xGkImgRk+fkDBquiq4gPiT898jusgQJAd5Zrr6Q8
+AO/0isr/3aa6O6NLQxISLKcPDk2NOccAfS/xOtfOz4sJYM3+Bs4Io9+dZGSDCA54
+Lw03eHTNQghS0A==
+-----END PRIVATE KEY-----