Pebble Coding

ソフトウェアエンジニアによるIT技術、数学の備忘録

opensslでed25519の鍵を作ってみる

openssl 1.1.1にてed25519の鍵が作れるようになったようです。

www.openssl.org

OpenSSL/genpkey - NORK's "HOW TO..." Wiki 略して「のうはうWiki」

macOS10.14.4のbrewではまだバージョンが1.0なので、1.1のバージョンのopensslをインストールします。

$ brew install openssl@1.1

.bash_profile でのopensslのパスも書き換えておきます。

export PATH="/usr/local/opt/openssl@1.1/bin:$PATH"
$ openssl version
OpenSSL 1.1.1b  26 Feb 2019

準備完了です。 まず、秘密鍵を生成します。

$ openssl genpkey -algorithm ed25519 -out priv.pem
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIDpWU4oFD25VMRLch+6swIFmpfduVSSN5MpFUeIJG2At
-----END PRIVATE KEY-----

ASN1フォーマットを解析してみます。

$ openssl asn1parse -in priv.pem 
    0:d=0  hl=2 l=  46 cons: SEQUENCE          
    2:d=1  hl=2 l=   1 prim: INTEGER           :00
    5:d=1  hl=2 l=   5 cons: SEQUENCE          
    7:d=2  hl=2 l=   3 prim: OBJECT            :ED25519
   12:d=1  hl=2 l=  34 prim: OCTET STRING      [HEX DUMP]:04203A56538A050F6E553112DC87EEACC08166A5F76E55248DE4CA4551E2091B602D

鍵部分は34バイトありますが、 先頭の"04"はASN1タグのOCTET STRING を表し、その次の"20"は続く長さが0x20バイトであることを表しています。 "3A56538A050F6E553112DC87EEACC08166A5F76E55248DE4CA4551E2091B602D"の部分は32バイトの秘密鍵です。

秘密鍵から公開鍵を生成します。

$ openssl pkey -in priv.pem -pubout > pub.pem
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA0/dQkRwXSiZKPFxuSQCdGhm1YSrb7JgKH0zVFqk7GzY=
-----END PUBLIC KEY-----
$ openssl asn1parse -in pub.pem 
    0:d=0  hl=2 l=  42 cons: SEQUENCE          
    2:d=1  hl=2 l=   5 cons: SEQUENCE          
    4:d=2  hl=2 l=   3 prim: OBJECT            :ED25519
    9:d=1  hl=2 l=  33 prim: BIT STRING 

ちなみに、
04: OCTET STRING というのは任意のバイナリだが長さが8bit(=1バイト)の倍数、
03: BIT STRING というのは任意のバイナリだが長さが4bitの倍数という意味です。
03もしくは04の後ろは継続するバイト数を表します。

BIT STRING はHEX DUMPしてくれないので、自前でやってみます。

$ head -2 priv.pem|tail -1|base64 -D|hexdump
0000000 30 2e 02 01 00 30 05 06 03 2b 65 70 04 22 04 20
0000010 3a 56 53 8a 05 0f 6e 55 31 12 dc 87 ee ac c0 81
0000020 66 a5 f7 6e 55 24 8d e4 ca 45 51 e2 09 1b 60 2d
0000030

04 20 がOCTETSTRING と長さ0x20バイトを表すのでその後ろ32バイトをBigEndianで整数としたものが秘密鍵です。

$ head -2 pub.pem|tail -1|base64 -D|hexdump
0000000 30 2a 30 05 06 03 2b 65 70 03 21 00 d3 f7 50 91
0000010 1c 17 4a 26 4a 3c 5c 6e 49 00 9d 1a 19 b5 61 2a
0000020 db ec 98 0a 1f 4c d5 16 a9 3b 1b 36            
000002c

03 21 がBITSTRING と長さ0x21バイトを表すのでその後ろ33バイトをBigEndianで整数としたものが公開鍵です。

秘密鍵は 3a56538a050f6e553112dc87eeacc08166a5f76e55248de4ca4551e2091b602d

公開鍵は d3f750911c174a264a3c5c6e49009d1a19b5612adbec980a1f4cd516a93b1b36
ということになります。先頭の0x00は整数にするとなくなるのでその後ろからとなります。

swiftで作ったed25519ライブラリを使ってこれが正常かどうか確認してみます。

func testIsValidKeyPair() {
    let secretKey: [UInt8] = "3A56538A050F6E553112DC87EEACC08166A5F76E55248DE4CA4551E2091B602D".unhexlify()
    let publicKey: [UInt8] = "d3f750911c174a264a3c5c6e49009d1a19b5612adbec980a1f4cd516a93b1b36".unhexlify()
    XCTAssert(Ed25519.isValidKeyPair(publicKey: publicKey, secretKey: secretKey))
}

このテストは無事にパスしました。この理解であっているようです。