RSA SHA-1 Signature for OAuth

RSA SHA-1 Signature for OAuth

https://www.rsa.com/img/banner.png

GoogleのOAuthが署名方法ににRSA-SHA1を取り入れていることは前から知っていましたが、内容までは見ていなかったので、ここで確認しておきます。
というのも、OpenSocialのGadgetがmakeRequestでOAuth APIにアクセスする際に、RSA署名を利用するSNSもあるらしいということを聞いたからです。

■ OAuthにおけるRSA-SHA1 Signature

  1. Consumerは自らのアプリを登録してConsumerKeyを受け取る際に、SPに自分の公開鍵を預ける
  2. OAuthの署名の際に、Consumerはbase_stringに自分の秘密鍵を用いて署名する
  3. SPは受け取ったリクエストの署名とbase_stringを公開鍵を用いて検証する

■ OAuth Core 1.0の仕様

http://oauth.net/core/1.0/#anchor19

9.3. RSA-SHA1

The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in \[RFC3447\] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for EMSA-PKCS1-v1_5.
It is assumed that the Consumer has provided its RSA public key in a verified way to the Service Provider, in a manner which is beyond the scope of this specification.

キーワード

  • RSASSA-PKCS1-v1_5 signature algorithm
  • SHA-1
  • EMSA-PKCS1-v1_5

9.3.1. Generating Signature

The Signature Base String is signed using the Consumer’s RSA private key per \[RFC3447\] section 8.2.1,
where K is the Consumer’s RSA private key,
M the Signature Base String,
and S is the result signature octet string:

S = RSASSA-PKCS1-V1_5-SIGN (K, M)

oauth_signature is set to S, first base64-encoded per \[RFC2045\] section 6.8, then URL-encoded per Parameter Encoding.

キーワード

  • K : ConsumerのRSA Privete key
  • M : Signature Base String
  • S : RSASSA-PKCS1-V1_5-SIGNした結果
  • 最初にbase64-encoded、それからURL-encodedしてパラメータにセット?

9.3.2. Verifying Signature

The Service Provider verifies the signature per \[RFC3447\] section 8.2.2,
where (n, e) is the Consumer’s RSA public key,
M is the Signature Base String,
and S is the octet string representation of the oauth_signature value:

RSASSA-PKCS1-V1_5-VERIFY ((n, e), M, S)

キーワード

  • (n, e) : ConsumerのRSA Public key
  • M : Signature Base String
  • S : oauth_signatute valueのoctet string

仕様はこうなっています。
が、正直よくわからないので、PHPライブラリの中身を見て、実装内容を確認します。

PHPライブラリ

PHPライブラリのうち、RSA署名に関するものは以下のファイルになります。
http://code.google.com/p/oauth-php/source/browse/trunk/library/signature_method/OAuthSignatureMethod_RSA_SHA1.php

  • Private Key,Public Keyをfetchする関数は未定義
  • signature(署名作成)、verify(署名検証)の2つが定義済み

初めに、signature(署名作成)のロジックをみてみます。

<?php
        /**
         * Calculate the signature using RSA-SHA1
         * This function is copyright Andy Smith, 2008.
         * 
         * @param OAuthRequest request
         * @param string base_string
         * @param string consumer_secret
         * @param string token_secret
         * @return string  
         */
        public function signature ( $request, $base_string, $consumer_secret, $token_secret )
        {
                // Fetch the private key cert based on the request
                // Private Keyの取得(未実装)
                $cert = $this->fetch_private_cert($request);
                
                // Pull the private key ID from the certificate
                // Private KeyからPrivate Key IDを取得
                $privatekeyid = openssl_get_privatekey($cert);
                
                // Sign using the key
                // 署名作成
                $sig = false;
                $ok  = openssl_sign($base_string, $sig, $privatekeyid);   
                
                // Release the key resource
                // リソース解放
                openssl_free_key($privatekeyid);
                
                // base64エンコード、URLエンコードする
                return $request->urlencode(base64_encode($sig));
        }
?>

シンプル。
openssl_signについては、http://php.filearena.net/manual/ja/function.openssl-sign.phpを参照してください。
続いて、verify(署名検証)のロジックを見てみます。

<?php
        /**
         * Check if the request signature is the same as the one calculated for the request.
         * 
         * @param OAuthRequest request
         * @param string base_string
         * @param string consumer_secret
         * @param string token_secret
         * @param string signature
         * @return string  
         */
        public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature )
        {
                // URLデコードし、base64デコードする
                $decoded_sig = base64_decode($request->urldecode($signature));
                  
                // Fetch the public key cert based on the request
                // Public Keyを取得
                $cert = $this->fetch_public_cert($request);
                
                // Pull the public key ID from the certificate
                // Public Key IDを取得
                $publickeyid = openssl_get_publickey($cert);
                
                // Check the computed signature against the one passed in the query
                // 署名を検証
                $ok = openssl_verify($base_string, $decoded_sig, $publickeyid);   
                
                // Release the key resource
                openssl_free_key($publickeyid);
                return $ok == 1;
        }
?>

openssl_verifyについてはhttp://php.filearena.net/manual/ja/function.openssl-verify.phpを参照してください。
ロジックはそれほど難しくないことがわかりました。

■ 署名部分のサンプルコード

RSAペアを作成します。

$ openssl genrsa -out rsa_priv.pem 1024
$ openssl rsa -in rsa_priv.pem -out rsa_pub.pem -outform PEM -pubout
$ ls rsa_*.pem
rsa_priv.pem rsa_pub.pem

署名するだけのコードを書いてみる。
クラスファイルのRSA読み込むところは適当に実装してる。

<?php
// signature test
$request = "request";
$base_string = "testbasestring";
$consumer_secret = "consumersecret";
$token_secret = "tokenseccret";
$test_RSA = new OAuthSignatureMethod_RSA_SHA1();
$signature = $test_RSA->signature ( $request, $base_string, $consumer_secret, $token_secret );
$signature_invalid = "invalidsignature";

$result_success =  $test_RSA->verify ( $request, $base_string, $consumer_secret, $token_secret, $signature );
$result_invalid_signature =  $test_RSA->verify ( $request, $base_string, $consumer_secret, $token_secret, $signature_invalid );
?>

成功のときは、1,失敗のときは空。

■ まとめ

実装も簡単、管理も公開鍵だけなので、OAuthのSPになる際には、RSA-SHA1の実装をお勧めします。
GoogleRSA-SHA1使えるので、OAuthのサンプルもそのうち書いてみます。