にゃみかんてっくろぐ

猫か百合を見守る壁になりたい

PKCS #1(RFC8017)のRSA鍵フォーマット 日本語訳

PKCS #1 / PKCS #8に準拠した,.NET向けRSA暗号鍵エンコーダ・デコーダライブラリをNuGetに公開しました.ソースコードや使い方はGitHubにあります.

この記事では,PKCS #1での鍵フォーマットについて説明します.

PKCS #1 (RFC8017)

RFC 8017 - PKCS #1: RSA Cryptography Specifications Version 2.2

公開鍵暗号標準であるPKCSのドキュメントのひとつ PKCS #1(RFC8017) は,RSA暗号の仕様を定めたものです.暗号化や署名のアルゴリズムはもちろんですが,鍵の表現方法についてもAppendixに記載があります.

鍵の表現方法を定めた部分を訳してみます.

RFC8017 Appendix A. ASN.1 Syntax

付録 A. ASN.1 構文

A.1. RSA鍵表現

  この章では,RSA公開鍵・秘密鍵のASN.1オブジェクト識別子を定義するとともに, RSAPublicKey と RSAPrivateKey の型を定義します.  
  定義の対象用途には,X.509証明書,PKCS #8(RFC5958),PKCS #12(RFC7292)を含みます.

  オブジェクト識別子 rsaEncryption は, A.1.1 と A.1.2 で定義されるRSA公開鍵・秘密鍵を識別するものです.このオブジェクト識別子(OID)に関連付けられているAlgorithmIdentifier型のパラメタフィールドは,NULL型の値を持つものとします(SHALL).

      rsaEncryption    OBJECT IDENTIFIER ::= { pkcs-1 1 }

  この定義は,multi-prime RSAをサポートするよう拡張されていますが,過去のバージョンと下位互換性があります.

A.1.1. RSA公開鍵構文

  RSA公開鍵は,ASN.1のRSAPublicKey型により表現されます:

      RSAPublicKey ::= SEQUENCE {
          modulus           INTEGER,  -- n
          publicExponent    INTEGER   -- e
      }

  各フィールドは次のような意味を持ちます:

  - modulus: RSA の modulus n
  - publicExponent: RSA の public exponent e

A.1.2. RSA秘密鍵構文

  RSA秘密鍵は,ASN.1のRSAPrivateKey型により表現されます:

      RSAPrivateKey ::= SEQUENCE {
          version           Version,
          modulus           INTEGER,  -- n
          publicExponent    INTEGER,  -- e
          privateExponent   INTEGER,  -- d
          prime1            INTEGER,  -- p
          prime2            INTEGER,  -- q
          exponent1         INTEGER,  -- d mod (p-1)
          exponent2         INTEGER,  -- d mod (q-1)
          coefficient       INTEGER,  -- (inverse of q) mod p
          otherPrimeInfos   OtherPrimeInfos OPTIONAL
      }

  各フィールドは次のような意味を持ちます:

  - version: 将来の改訂を考慮するためのバージョン番号.0とします(SHALL).ただし,multi-primeを使用する場合は1とします(SHALL).
            Version ::= INTEGER { two-prime(0), multi(1) }
               (CONSTRAINED BY
               {-- otherPrimesInfosを含む場合,versionは必ず multi(1) とすべきです --})
  
  - modulus: RSA の modulus n
  - publicExponent: RSA の public exponent e
  - privateExponent: RSA の private exponent d
  - prime1: n の素因数 p
  - prime2: n の素因数 q
  - exponent1: d mod (p - 1)
  - exponent2: d mod (q - 1)
  - coefficient: CRT(中国剰余定理) の coefficient q^(-1) mod p
  - otherPrimeInfos: 追加の素数を r_3, ..., r_u, の順に含みます.version 0の場合は省略する(SHALL).version 1の場合は1つ以上のOtherPrimeInfoを含みます.
            OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo

            OtherPrimeInfo ::= SEQUENCE {
                prime             INTEGER,  -- ri
                exponent          INTEGER,  -- di
                coefficient       INTEGER   -- ti
            }
  
    OtherPrimeInfoのフィールドは次のような意味を持ちます:
      - prime: n の素因数 r_i (i >= 3)
      - exponent: d_i = d mod (r_i - 1)
      - coefficient: CRT(中国剰余定理)の coefficient t_i = (r_1 * r_2 * ... * r_(i-1))^(-1) mod r_i

  注意: RSA秘密鍵は,流出や改ざんからの保護が重要です.保護方法についてはこの文書の範囲外です.秘密鍵やその他の暗号データを保存・配布する方法については,PKCS #12やPKCS #15で記述しています.

読み解くポイント: RSA, ASN.1, CRT

定義は明確ですが,いくつか前提知識を必要とします.

PKCS #1の鍵表現の使われどころ

-----BEGIN RSA PUBLIC KEY-----で始まる文字列を見たことはあるでしょうか? あれは,PKCS #1形式によってRSA公開鍵が表現されています.

(※BEGIN PUBLIC KEYで始まるのはPKCS #8形式です.そちらはまた別途.)

$ ssh-keygen -f .ssh/id_rsa.pub -e -m pem
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAtH53M23IIXvXnM6d4nrZ9Q3BCU/HSMoYnZHlU6ytPXWZ5Y9oGAdX
4S2cMNOt7ZbiXXT9aRFyQwv80MCd6yMfrkHgFG0V2Oim59R1zypVXm7pvQmNApx3
Qvp+YMeEyLveFvn+wdwkeUSW1ag9D4ZIRwIE3kTqVxEj5BXElaaTW0HxYRDWVbrA
MU9RSekhTq2bHRSKIfMcbqtu/tFj9SS/46XiCPd05wolorAYTY3y2ntPVc9hbepf
IbxoiADm75pjxM1wg8ESOOmxSTQJO4hmdOEFRkMfRFFB65o2anN89Ubd1WJARwP5
+MYnliqPN3PO7eSqBw7qEzj+wqmRoX2YQwIDAQAB
-----END RSA PUBLIC KEY-----

この公開鍵は,PKCS #1で定める RSAPublicKey を,DERとBASE64で符号化したものです.

これを,Asn1PKCSでデコードしてみましょう.

var key = "MIIBCgKCAQEAtH53M23IIXvXnM6d4nrZ9Q3BCU/HSMoYnZHlU6ytPXWZ5Y9oGAdX4S2cMNOt7ZbiXXT9aRFyQwv80MCd6yMfrkHgFG0V2Oim59R1zypVXm7pvQmNApx3Qvp+YMeEyLveFvn+wdwkeUSW1ag9D4ZIRwIE3kTqVxEj5BXElaaTW0HxYRDWVbrAMU9RSekhTq2bHRSKIfMcbqtu/tFj9SS/46XiCPd05wolorAYTY3y2ntPVc9hbepfIbxoiADm75pjxM1wg8ESOOmxSTQJO4hmdOEFRkMfRFFB65o2anN89Ubd1WJARwP5+MYnliqPN3PO7eSqBw7qEzj+wqmRoX2YQwIDAQAB";
var rsaParams = PKCS1DERDecoder.DecodePublicKey(key);

Console.WriteLine("n(modulus)       : 0x" + BitConverter.ToString(rsaParams.Modulus).Replace("-", string.Empty));
Console.WriteLine("e(publicExponent): 0x" + BitConverter.ToString(rsaParams.Exponent).Replace("-", string.Empty));
n(modulus)       : 0xB47E77336DC8217BD79CCE9DE27AD9F50DC1094FC748CA189D91E553ACAD3D7599E58F68180757E12D9C30D3ADED96E25D74FD691172430BFCD0C09DEB231FAE41E0146D15D8E8A6E7D475CF2A555E6EE9BD098D029C7742FA7E60C784C8BBDE16F9FEC1DC24794496D5A83D0F8648470204DE44EA571123E415C495A6935B41F16110D655BAC0314F5149E9214EAD9B1D148A21F31C6EAB6EFED163F524BFE3A5E208F774E70A25A2B0184D8DF2DA7B4F55CF616DEA5F21BC688800E6EF9A63C4CD7083C11238E9B14934093B886674E10546431F445141EB9A366A737CF546DDD562404703F9F8C627962A8F3773CEEDE4AA070EEA1338FEC2A991A17D9843
e(publicExponent): 0x010001

できました.