OpenSSLの公開鍵/秘密鍵を使った暗号化/復号の操作を、
鍵の中にある整数n, e, d とPython3のpow関数を使って手作業でやってみます。
(ただし、元の値は1個だけ)
OpenSSLの鍵の作成には、以下の2つの方法があります。
-
ssh-keygenコマンド
リモートマシンを操作する時に使うSSH用の秘密鍵と公開鍵を発行します。
鍵ファイルを開くためのパスフレーズを設定することができます。 -
openssl genrsaコマンド
ブラウザでHTTPS通信を行う時に使うSSL用の秘密鍵と公開鍵を発行します。
どちらの鍵でも、暗号化/復号の実験はできるのですが、
今回はSSL用の鍵で実験してみます。
1. OpenSSLのバージョン
今回の実験で使用したOpenSSLのバージョンは、以下の通りです。
$ openssl version
OpenSSL 1.1.1 11 Sep 2018
2. 秘密鍵ファイルの作成
2-1. SSH用秘密鍵ファイルの生成
SSH用の秘密鍵ファイルを生成するには、ssh-keygen
コマンドを使用します。
デフォルトでは鍵ペアが~/.ssh/
ディレクトリに作成されるため、
誤って既存のファイルに上書きしてしまわないように、
-f
オプションを使って、出力するファイル名を指定しておきます。
また、最近のOpenSSLでは、鍵の形式が**新形式(OPENSSH PRIVATE KEY)**となっているため、
pem形式で出力する場合は、-m pem
オプションを追加しておきます。
途中でパスフレーズを尋ねてきますが、今回は実験なのでパスフレーズ無しにしています。
$ ssh-keygen -f ./id_isa -m pem
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): <--- そのままEnter
Enter same passphrase again: <--- そのままEnter
Your identification has been saved in ./id_isa.
Your public key has been saved in ./id_isa.pub.
The key fingerprint is:
SHA256:hV6oIMp511t1OxxtRsutREQ8EUvde96S7uevqj9n8Yc root@67bdd8cc774c
The key's randomart image is:
+---[RSA 2048]----+
| +B=.|
| o o*.=|
| . . o o. ooBo|
|.... ..o o. o.=o.|
|.o . ...S. +.oo|
| . . o = o|
| . . = |
| . E +|
| .oo*o+=|
+----[SHA256]-----+
この記事では、公開鍵の生成以降の作業をSSL用の鍵を使って解説していますので、
SSHの秘密鍵で作業を行いたい場合は、
秘密鍵ファイルkey-private.pem
の部分をid_isa
に読み替えてください。
2-2. SSL用秘密鍵ファイルの生成
openssl genrsa
コマンドで秘密鍵ファイルを生成します。
デフォルトでは、鍵長は2048ビットになっています。
$ openssl genrsa > key-private.pem
Generating RSA private key, 2048 bit long modulus (2 primes)
.....................................+++++
.............+++++
e is 65537 (0x010001)
3. 秘密鍵ファイルの内容
3-1. catコマンドでの表示
catコマンドで鍵ファイルの中身を見てみます。
デフォルトでは、鍵ファイルはPEM 形式と呼ばれる固定のヘッダ・フッタの間に BASE64 でエンコードされて保存されています。
$ cat key-private.pem
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAl+RPByBaEZjV2Lb8Z4VZ29XxNvRKn777wXHxEIIjJArgTXHS
tRO2Yt15omy17YHt0vv0NYjMJoyV1nFaaGEsMdmmpqh4vbZGHNDgPwPe7IfOMujm
UI9AmTtajnlEj/sxKdWlgQ9EVgSIUkTWxpad1DX7/gE2JfnxwgNXxCxkRD8XIcux
ODCVeq53I/ZOOQcixCPYijD3p1X+H33q8AEtPLV1dakeYtg4O5TFsbo2/E7bent4
fVlZxb/pxB+nbv/QM0CP47lBpT1nXYG4o9C3GU48e1riuPGVgVJ7yjVBfIJXlt6s
Xch+MID/iZPiNp7FbQlD1yfOqMLjkNsF+ZWI4wIDAQABAoIBAAoIebPlzi1FZDLJ
e4i3BUWBL0rK/jbpHaYciajmf728vi4/a4SshaqoKIWzGp1SrMv3+pyiqaGOPcOJ
f0hPyuSMFPcDP96AMMdsgLOI5OvI2LUCL1x46fJ1OjkZB49fL1MtGp6YzJHGAN82
Tt2VS12eJ0QS/mmpxe9j2yNJL2JWknpduzrWRluvIUvgtY6LSOHBrCjxsns30NWA
kVES4R8SJihR9xKRWEaj3EX3BLheNrwQTByEEFYxAMlzv9mX1XRyK+KU1vZef11I
MA/bxZLBZ7xx3tjt+/yBSgr8zMDOsrz69cwG98cv5Yw3tjar2OU9AF460GkoiZeO
QsmNJ4kCgYEAxpUaHoCRtdnxXxh7Er/oqBpuqIvNa4oni/tJEArgxty0k8Ul6DoQ
mcu79MhyKm3hOFPDNeZyZLuGeyODe3zTDXVLE8Qc9k1KA4B5V5XknDR0RCRvcxPQ
Q51oME68n+nnHTUvi6Qyg4C7t2YWOzJjuQAKmHNPfoWeJpp9Qm2h3j8CgYEAw881
lrR04Bp4jm9NfpPboVXhEkVcTrxbro9HMqyUtYCDJROtLMjG+kauH/yK5DPlFglE
o4ZGSxFcFk9xk2nxrlLTlI+wi7r5+E7WRUH4yzTdImTmJ92dc5GkAkForgO0+qbk
IH04ytYcKxiYLdL3e00CH2a7W41a3MWlmAidNF0CgYAkivOPgWFO8Zg1Q7ACN0Z9
CMAsS+21SGsWm1tKlHXgomSofLMJFQZRBujDls9Ld4TmdKOLm6iZWNjaeCKN6t57
r4XtUT1zJa3lDxNFRtQW2qA6menYZ2D/0EuH+DVFyCk7eroRHFofUOU6TpLwuckY
FiXc//s08Sm1OOCsBLiwyQKBgQCIVbrrPqRt8SBllAuyCUMP91qpvQ+DZtSzGuGo
387+/QbTBvs5xmX8lr/gV5dhQtzL1hIrhW9mDyU+B3x99nMnPFZDBzUWZU5s3H+G
Y2PWIO2jZ/t0YHKjqBE43NAE8WHOb+tAz89+M0wTmaFDrrNP75N9x6rGGQrd0uP0
knLapQKBgBoxOwQM1LpaFrWvK8HmZHxfp9sRW+UAVFddJ40Eke3sHg8g2KzThigB
2Nfjafs92N2TJqm7xuy1wUMJA6AbYCXwKj0hfOooMleqkQZjdtpw2HUoPKD7UhGw
NIPNEBzPVT3QFfhpEyQDpwF4nBWhGSvyoZxqPdyeo981twpehFX6
-----END RSA PRIVATE KEY-----
3-2. -text -noout
オプションでの展開
ちなみに、鍵の中身ですが、以下のようなフォーマットになっています。
ITU-T X.690
https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
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
}
以下のコマンドで、鍵ファイルを展開することができます。
$ openssl rsa -in key-private.pem -text -noout
RSA Private-Key: (2048 bit, 2 primes)
modulus:
00:97:e4:4f:07:20:5a:11:98:d5:d8:b6:fc:67:85:
59:db:d5:f1:36:f4:4a:9f:be:fb:c1:71:f1:10:82:
23:24:0a:e0:4d:71:d2:b5:13:b6:62:dd:79:a2:6c:
b5:ed:81:ed:d2:fb:f4:35:88:cc:26:8c:95:d6:71:
5a:68:61:2c:31:d9:a6:a6:a8:78:bd:b6:46:1c:d0:
e0:3f:03:de:ec:87:ce:32:e8:e6:50:8f:40:99:3b:
5a:8e:79:44:8f:fb:31:29:d5:a5:81:0f:44:56:04:
88:52:44:d6:c6:96:9d:d4:35:fb:fe:01:36:25:f9:
f1:c2:03:57:c4:2c:64:44:3f:17:21:cb:b1:38:30:
95:7a:ae:77:23:f6:4e:39:07:22:c4:23:d8:8a:30:
f7:a7:55:fe:1f:7d:ea:f0:01:2d:3c:b5:75:75:a9:
1e:62:d8:38:3b:94:c5:b1:ba:36:fc:4e:db:7a:7b:
78:7d:59:59:c5:bf:e9:c4:1f:a7:6e:ff:d0:33:40:
8f:e3:b9:41:a5:3d:67:5d:81:b8:a3:d0:b7:19:4e:
3c:7b:5a:e2:b8:f1:95:81:52:7b:ca:35:41:7c:82:
57:96:de:ac:5d:c8:7e:30:80:ff:89:93:e2:36:9e:
c5:6d:09:43:d7:27:ce:a8:c2:e3:90:db:05:f9:95:
88:e3
publicExponent: 65537 (0x10001)
privateExponent:
0a:08:79:b3:e5:ce:2d:45:64:32:c9:7b:88:b7:05:
45:81:2f:4a:ca:fe:36:e9:1d:a6:1c:89:a8:e6:7f:
bd:bc:be:2e:3f:6b:84:ac:85:aa:a8:28:85:b3:1a:
9d:52:ac:cb:f7:fa:9c:a2:a9:a1:8e:3d:c3:89:7f:
48:4f:ca:e4:8c:14:f7:03:3f:de:80:30:c7:6c:80:
b3:88:e4:eb:c8:d8:b5:02:2f:5c:78:e9:f2:75:3a:
39:19:07:8f:5f:2f:53:2d:1a:9e:98:cc:91:c6:00:
df:36:4e:dd:95:4b:5d:9e:27:44:12:fe:69:a9:c5:
ef:63:db:23:49:2f:62:56:92:7a:5d:bb:3a:d6:46:
5b:af:21:4b:e0:b5:8e:8b:48:e1:c1:ac:28:f1:b2:
7b:37:d0:d5:80:91:51:12:e1:1f:12:26:28:51:f7:
12:91:58:46:a3:dc:45:f7:04:b8:5e:36:bc:10:4c:
1c:84:10:56:31:00:c9:73:bf:d9:97:d5:74:72:2b:
e2:94:d6:f6:5e:7f:5d:48:30:0f:db:c5:92:c1:67:
bc:71:de:d8:ed:fb:fc:81:4a:0a:fc:cc:c0:ce:b2:
bc:fa:f5:cc:06:f7:c7:2f:e5:8c:37:b6:36:ab:d8:
e5:3d:00:5e:3a:d0:69:28:89:97:8e:42:c9:8d:27:
89
prime1:
00:c6:95:1a:1e:80:91:b5:d9:f1:5f:18:7b:12:bf:
e8:a8:1a:6e:a8:8b:cd:6b:8a:27:8b:fb:49:10:0a:
e0:c6:dc:b4:93:c5:25:e8:3a:10:99:cb:bb:f4:c8:
72:2a:6d:e1:38:53:c3:35:e6:72:64:bb:86:7b:23:
83:7b:7c:d3:0d:75:4b:13:c4:1c:f6:4d:4a:03:80:
79:57:95:e4:9c:34:74:44:24:6f:73:13:d0:43:9d:
68:30:4e:bc:9f:e9:e7:1d:35:2f:8b:a4:32:83:80:
bb:b7:66:16:3b:32:63:b9:00:0a:98:73:4f:7e:85:
9e:26:9a:7d:42:6d:a1:de:3f
prime2:
00:c3:cf:35:96:b4:74:e0:1a:78:8e:6f:4d:7e:93:
db:a1:55:e1:12:45:5c:4e:bc:5b:ae:8f:47:32:ac:
94:b5:80:83:25:13:ad:2c:c8:c6:fa:46:ae:1f:fc:
8a:e4:33:e5:16:09:44:a3:86:46:4b:11:5c:16:4f:
71:93:69:f1:ae:52:d3:94:8f:b0:8b:ba:f9:f8:4e:
d6:45:41:f8:cb:34:dd:22:64:e6:27:dd:9d:73:91:
a4:02:41:68:ae:03:b4:fa:a6:e4:20:7d:38:ca:d6:
1c:2b:18:98:2d:d2:f7:7b:4d:02:1f:66:bb:5b:8d:
5a:dc:c5:a5:98:08:9d:34:5d
exponent1:
24:8a:f3:8f:81:61:4e:f1:98:35:43:b0:02:37:46:
7d:08:c0:2c:4b:ed:b5:48:6b:16:9b:5b:4a:94:75:
e0:a2:64:a8:7c:b3:09:15:06:51:06:e8:c3:96:cf:
4b:77:84:e6:74:a3:8b:9b:a8:99:58:d8:da:78:22:
8d:ea:de:7b:af:85:ed:51:3d:73:25:ad:e5:0f:13:
45:46:d4:16:da:a0:3a:99:e9:d8:67:60:ff:d0:4b:
87:f8:35:45:c8:29:3b:7a:ba:11:1c:5a:1f:50:e5:
3a:4e:92:f0:b9:c9:18:16:25:dc:ff:fb:34:f1:29:
b5:38:e0:ac:04:b8:b0:c9
exponent2:
00:88:55:ba:eb:3e:a4:6d:f1:20:65:94:0b:b2:09:
43:0f:f7:5a:a9:bd:0f:83:66:d4:b3:1a:e1:a8:df:
ce:fe:fd:06:d3:06:fb:39:c6:65:fc:96:bf:e0:57:
97:61:42:dc:cb:d6:12:2b:85:6f:66:0f:25:3e:07:
7c:7d:f6:73:27:3c:56:43:07:35:16:65:4e:6c:dc:
7f:86:63:63:d6:20:ed:a3:67:fb:74:60:72:a3:a8:
11:38:dc:d0:04:f1:61:ce:6f:eb:40:cf:cf:7e:33:
4c:13:99:a1:43:ae:b3:4f:ef:93:7d:c7:aa:c6:19:
0a:dd:d2:e3:f4:92:72:da:a5
coefficient:
1a:31:3b:04:0c:d4:ba:5a:16:b5:af:2b:c1:e6:64:
7c:5f:a7:db:11:5b:e5:00:54:57:5d:27:8d:04:91:
ed:ec:1e:0f:20:d8:ac:d3:86:28:01:d8:d7:e3:69:
fb:3d:d8:dd:93:26:a9:bb:c6:ec:b5:c1:43:09:03:
a0:1b:60:25:f0:2a:3d:21:7c:ea:28:32:57:aa:91:
06:63:76:da:70:d8:75:28:3c:a0:fb:52:11:b0:34:
83:cd:10:1c:cf:55:3d:d0:15:f8:69:13:24:03:a7:
01:78:9c:15:a1:19:2b:f2:a1:9c:6a:3d:dc:9e:a3:
df:35:b7:0a:5e:84:55:fa
3-3. asn1parse
オプションでの展開
openssl asn1parse
コマンドでも展開できます。
$ openssl asn1parse -in key-private.pem
0:d=0 hl=4 l=1187 cons: SEQUENCE
4:d=1 hl=2 l= 1 prim: INTEGER :00
7:d=1 hl=4 l= 257 prim: INTEGER :97E44F07205A1198D5D8B6FC678559DBD5F136F44A9FBEFBC171F1108223240AE04D71D2B513B662DD79A26CB5ED81EDD2FBF43588CC268C95D6715A68612C31D9A6A6A878BDB6461CD0E03F03DEEC87CE32E8E6508F40993B5A8E79448FFB3129D5A5810F445604885244D6C6969DD435FBFE013625F9F1C20357C42C64443F1721CBB13830957AAE7723F64E390722C423D88A30F7A755FE1F7DEAF0012D3CB57575A91E62D8383B94C5B1BA36FC4EDB7A7B787D5959C5BFE9C41FA76EFFD033408FE3B941A53D675D81B8A3D0B7194E3C7B5AE2B8F19581527BCA35417C825796DEAC5DC87E3080FF8993E2369EC56D0943D727CEA8C2E390DB05F99588E3
268:d=1 hl=2 l= 3 prim: INTEGER :010001
273:d=1 hl=4 l= 256 prim: INTEGER :0A0879B3E5CE2D456432C97B88B70545812F4ACAFE36E91DA61C89A8E67FBDBCBE2E3F6B84AC85AAA82885B31A9D52ACCBF7FA9CA2A9A18E3DC3897F484FCAE48C14F7033FDE8030C76C80B388E4EBC8D8B5022F5C78E9F2753A3919078F5F2F532D1A9E98CC91C600DF364EDD954B5D9E274412FE69A9C5EF63DB23492F6256927A5DBB3AD6465BAF214BE0B58E8B48E1C1AC28F1B27B37D0D580915112E11F12262851F712915846A3DC45F704B85E36BC104C1C8410563100C973BFD997D574722BE294D6F65E7F5D48300FDBC592C167BC71DED8EDFBFC814A0AFCCCC0CEB2BCFAF5CC06F7C72FE58C37B636ABD8E53D005E3AD0692889978E42C98D2789
533:d=1 hl=3 l= 129 prim: INTEGER :C6951A1E8091B5D9F15F187B12BFE8A81A6EA88BCD6B8A278BFB49100AE0C6DCB493C525E83A1099CBBBF4C8722A6DE13853C335E67264BB867B23837B7CD30D754B13C41CF64D4A0380795795E49C347444246F7313D0439D68304EBC9FE9E71D352F8BA4328380BBB766163B3263B9000A98734F7E859E269A7D426DA1DE3F
665:d=1 hl=3 l= 129 prim: INTEGER :C3CF3596B474E01A788E6F4D7E93DBA155E112455C4EBC5BAE8F4732AC94B580832513AD2CC8C6FA46AE1FFC8AE433E5160944A386464B115C164F719369F1AE52D3948FB08BBAF9F84ED64541F8CB34DD2264E627DD9D7391A4024168AE03B4FAA6E4207D38CAD61C2B18982DD2F77B4D021F66BB5B8D5ADCC5A598089D345D
797:d=1 hl=3 l= 128 prim: INTEGER :248AF38F81614EF1983543B00237467D08C02C4BEDB5486B169B5B4A9475E0A264A87CB30915065106E8C396CF4B7784E674A38B9BA89958D8DA78228DEADE7BAF85ED513D7325ADE50F134546D416DAA03A99E9D86760FFD04B87F83545C8293B7ABA111C5A1F50E53A4E92F0B9C9181625DCFFFB34F129B538E0AC04B8B0C9
928:d=1 hl=3 l= 129 prim: INTEGER :8855BAEB3EA46DF12065940BB209430FF75AA9BD0F8366D4B31AE1A8DFCEFEFD06D306FB39C665FC96BFE057976142DCCBD6122B856F660F253E077C7DF673273C5643073516654E6CDC7F866363D620EDA367FB746072A3A81138DCD004F161CE6FEB40CFCF7E334C1399A143AEB34FEF937DC7AAC6190ADDD2E3F49272DAA5
1060:d=1 hl=3 l= 128 prim: INTEGER :1A313B040CD4BA5A16B5AF2BC1E6647C5FA7DB115BE50054575D278D0491EDEC1E0F20D8ACD3862801D8D7E369FB3DD8DD9326A9BBC6ECB5C1430903A01B6025F02A3D217CEA283257AA91066376DA70D875283CA0FB5211B03483CD101CCF553DD015F869132403A701789C15A1192BF2A19C6A3DDC9EA3DF35B70A5E8455FA
4. 公開鍵ファイルの生成
続いて、秘密鍵ファイルから、公開鍵ファイルを生成してみましょう。
デフォルトでは標準出力に出力されます。
$ openssl rsa -in key-private.pem -pubout
writing RSA key
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl+RPByBaEZjV2Lb8Z4VZ
29XxNvRKn777wXHxEIIjJArgTXHStRO2Yt15omy17YHt0vv0NYjMJoyV1nFaaGEs
Mdmmpqh4vbZGHNDgPwPe7IfOMujmUI9AmTtajnlEj/sxKdWlgQ9EVgSIUkTWxpad
1DX7/gE2JfnxwgNXxCxkRD8XIcuxODCVeq53I/ZOOQcixCPYijD3p1X+H33q8AEt
PLV1dakeYtg4O5TFsbo2/E7bent4fVlZxb/pxB+nbv/QM0CP47lBpT1nXYG4o9C3
GU48e1riuPGVgVJ7yjVBfIJXlt6sXch+MID/iZPiNp7FbQlD1yfOqMLjkNsF+ZWI
4wIDAQAB
-----END PUBLIC KEY-----
ここでは、key-public.pem
というファイルに書き出しておきます。
$ openssl rsa -in key-private.pem -pubout > key-public.pem
writing RSA key
5. 公開鍵ファイルの内容
5-1. catコマンドでの表示
catコマンドで公開鍵ファイルの中身を確認してみます。
$ cat key-public.pem
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl+RPByBaEZjV2Lb8Z4VZ
29XxNvRKn777wXHxEIIjJArgTXHStRO2Yt15omy17YHt0vv0NYjMJoyV1nFaaGEs
Mdmmpqh4vbZGHNDgPwPe7IfOMujmUI9AmTtajnlEj/sxKdWlgQ9EVgSIUkTWxpad
1DX7/gE2JfnxwgNXxCxkRD8XIcuxODCVeq53I/ZOOQcixCPYijD3p1X+H33q8AEt
PLV1dakeYtg4O5TFsbo2/E7bent4fVlZxb/pxB+nbv/QM0CP47lBpT1nXYG4o9C3
GU48e1riuPGVgVJ7yjVBfIJXlt6sXch+MID/iZPiNp7FbQlD1yfOqMLjkNsF+ZWI
4wIDAQAB
-----END PUBLIC KEY-----
5-2. -text -noout
オプションでの表示
公開鍵ファイルを展開してみてみましょう。
公開鍵ファイルを読み込ませる場合は、
-pubin
のオプションを追加する必要があります。
$ openssl rsa -in key-public.pem -pubin -text -noout
RSA Public-Key: (2048 bit)
Modulus:
00:97:e4:4f:07:20:5a:11:98:d5:d8:b6:fc:67:85:
59:db:d5:f1:36:f4:4a:9f:be:fb:c1:71:f1:10:82:
23:24:0a:e0:4d:71:d2:b5:13:b6:62:dd:79:a2:6c:
b5:ed:81:ed:d2:fb:f4:35:88:cc:26:8c:95:d6:71:
5a:68:61:2c:31:d9:a6:a6:a8:78:bd:b6:46:1c:d0:
e0:3f:03:de:ec:87:ce:32:e8:e6:50:8f:40:99:3b:
5a:8e:79:44:8f:fb:31:29:d5:a5:81:0f:44:56:04:
88:52:44:d6:c6:96:9d:d4:35:fb:fe:01:36:25:f9:
f1:c2:03:57:c4:2c:64:44:3f:17:21:cb:b1:38:30:
95:7a:ae:77:23:f6:4e:39:07:22:c4:23:d8:8a:30:
f7:a7:55:fe:1f:7d:ea:f0:01:2d:3c:b5:75:75:a9:
1e:62:d8:38:3b:94:c5:b1:ba:36:fc:4e:db:7a:7b:
78:7d:59:59:c5:bf:e9:c4:1f:a7:6e:ff:d0:33:40:
8f:e3:b9:41:a5:3d:67:5d:81:b8:a3:d0:b7:19:4e:
3c:7b:5a:e2:b8:f1:95:81:52:7b:ca:35:41:7c:82:
57:96:de:ac:5d:c8:7e:30:80:ff:89:93:e2:36:9e:
c5:6d:09:43:d7:27:ce:a8:c2:e3:90:db:05:f9:95:
88:e3
Exponent: 65537 (0x10001)
5-3. asn1parse
オプションでの表示
秘密鍵ファイルと同様に、openssl asn1parse
コマンドで内容を展開することができます。
$ openssl asn1parse -in key-public.pem
0:d=0 hl=4 l= 290 cons: SEQUENCE
4:d=1 hl=2 l= 13 cons: SEQUENCE
6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
17:d=2 hl=2 l= 0 prim: NULL
19:d=1 hl=4 l= 271 prim: BIT STRING
最初に作った秘密鍵ファイルと公開鍵ファイルを見比べるとわかりますが、
公開鍵は、秘密鍵から(暗号化処理に必要となる)以下の項目を抽出した内容になっています。
modulus INTEGER, -- n
publicExponent INTEGER, -- e
6. 整数e, d, n とpow関数を用いた暗号化/復号および電子署名/復号の実験
RSA公開鍵暗号化方式では、下記の手順で暗号化/復号を行うことができます。
-
暗号化
元の値をe乗してnで割った余りを得る -
復号
暗号化後の値をd乗してnで割った余りを得る
6-1. 整数n, e, d の読み込み
Python3を起動し、さきほどのopenssl asn1parse
の出力結果から、
modulus INTEGER, -- n
publicExponent INTEGER, -- e
privateExponent INTEGER, -- d
に相当する部分を変数n, e, dに読み込ませます。
openssl asn1parse
の出力結果は16進数ですので、int関数を使って16進数から10進数に変換すると良いでしょう。
int関数の第1引数の' '
の間に、openssl asn1parse
の実行結果のそれぞれ n, e, d に相当する部分をコピー&ペーストします。
int関数の第2引数に基数の16を指定すれば、10進数に変換して代入することができます。
変数n
$ python
>>> n = int('97E44F07205A1198D5D8B6FC678559DBD5F136F44A9FBEFBC171F1108223240AE04D71D2B513B662DD79A26CB5ED81EDD2FBF43588CC268C95D6715A68612C31D9A6A6A878BDB6461CD0E03F03DEEC87CE32E8E6508F40993B5A8E79448FFB3129D5A5810F445604885244D6C6969DD435FBFE013625F9F1C20357C42C64443F1721CBB13830957AAE7723F64E390722C423D88A30F7A755FE1F7DEAF0012D3CB57575A91E62D8383B94C5B1BA36FC4EDB7A7B787D5959C5BFE9C41FA76EFFD033408FE3B941A53D675D81B8A3D0B7194E3C7B5AE2B8F19581527BCA35417C825796DEAC5DC87E3080FF8993E2369EC56D0943D727CEA8C2E390DB05F99588E3', 16)
>>> print(n)
19174567267106562614863777986013190733311720406517163236419798721057651041741132975692437635046772915985388744151381878391739010943167351904934011584525523379391186697776748275394847710370026168707668355363468298465929010753092617924044792285570698575811523066628303754919477816371556100017714944862167735437948517694796555955615103513053419306742815189247527511947589796524356990034768580398382078215058167355632932286761344674894465569796261922954597156699763620980072965273234707300569241109881200233991616854473630970425663335146366570862840409827825897729557509442076004084186075433324566154623231714988236572899
変数e
>>> e = int('010001', 16)
>>> print(e)
65537
変数d
>>> d = int('0A0879B3E5CE2D456432C97B88B70545812F4ACAFE36E91DA61C89A8E67FBDBCBE2E3F6B84AC85AAA82885B31A9D52ACCBF7FA9CA2A9A18E3DC3897F484FCAE48C14F7033FDE8030C76C80B388E4EBC8D8B5022F5C78E9F2753A3919078F5F2F532D1A9E98CC91C600DF364EDD954B5D9E274412FE69A9C5EF63DB23492F6256927A5DBB3AD6465BAF214BE0B58E8B48E1C1AC28F1B27B37D0D580915112E11F12262851F712915846A3DC45F704B85E36BC104C1C8410563100C973BFD997D574722BE294D6F65E7F5D48300FDBC592C167BC71DED8EDFBFC814A0AFCCCC0CEB2BCFAF5CC06F7C72FE58C37B636ABD8E53D005E3AD0692889978E42C98D2789', 16)
>>> print(d)
1266562425794655073618647403778798277072591629763535096975163780207494565813164542956994713247745242432530446517712622664416103550253619579725335858361093591549574243781002232085452427456121630290301605358323607489799757198988936676887710847372256193214338211322366403024954139906807854448276362914206083993922215094114776012124366236002371990952894667648628314166422099626061405451672662307764909200435879249013664134531240274310366361076547379589865534234754212903474203891892270277583495216595050906895706003102832662158785980707445928619686610586861592808732191214885213804349178521403558427718487767558413690761
今回生成した鍵の鍵長は2048ビットですが、
元の値としては 0 ~ n - 1 までの値を指定することができます。
ちなみに、nのビット数を調べると、、、
>>> n > pow(2, 2047)
True
>>> n >= pow(2, 2048)
False
pow(2, 2047) < n < pow(2, 2048) となっていますので、
2048ビットであることが解ります。
6-2. 暗号化および復号の検証
それでは、
Python3のpow関数を使って暗号化/復号の処理を検証してみましょう。
とりあえず今回は、
暗号化したい元の値を999
として、計算してみます。
>>> res = pow(999, e, n)
>>> print(res)
524543469488385391208649776185973445555945805700279654506952471132860336892462605649791076963494809100528555892416120164939727341582216996729091424255089000208944560774832745667292288852130261583027005786502988808264868095073383064006388838941814953270529661005557859554531440610634597104550314977758677359220270332738891641070581370988599952291373443267991140275169462890173229924742294722790318716574816586808362192527012499142400781383842103405401901384801647925304050154989496544788985011577610320345706511051127648308732239235134144620399441561198248345544962316221521339404384225995267012691211102015063665139
>>> pow(res, d, n)
999L
ちゃんと元の値999
が取り戻せた様子が解ります。
(999L
の末尾のL
は、長整数型であることを表します。)
6-3. 整数d, e, n とpow関数を用いた電子署名/復号の実験
同様に、電子署名を生成する際の操作も確かめてみます。
-
電子署名の生成
元の値をd乗してnで割った余りを得る -
復号
生成した値をe乗してnで割った余りを得る
元の値を999
として、計算してみましょう。
>>> res = pow(999, d, n)
>>> print(res)
12515145548924267994063078729711558694795939305875287590392530985593258425241072795063626446654873213977268021736112937973243180699117087520232129972712575525060239166094963345854149835308367216610630688466177263698932768887974675177666169711382251979967291763464839827987347286973550282470918001633660870333020695268293805924000830179825744511427332152587742208811697351651808682552483986509261284189871882421325531674159049062719085809968237649297132520093239037028418224856849358296300721149131689586359427295787997936203041546874958734266989850986012139160164407381286637028534638454642977487380551476319595646032
>>> pow(res, e, n)
999L
こちらの処理についても、元の値999
が取り戻せた様子が解ります。
7. 2つの整数p, qから、秘密鍵のdを求めてみる
ちなみに、整数nは2つの素数p, qの積なのですが、
実際にそうなっているのか確かめてみます。
>>> p = int('C6951A1E8091B5D9F15F187B12BFE8A81A6EA88BCD6B8A278BFB49100AE0C6DCB493C525E83A1099CBBBF4C8722A6DE13853C335E67264BB867B23837B7CD30D754B13C41CF64D4A0380795795E49C347444246F7313D0439D68304EBC9FE9E71D352F8BA4328380BBB766163B3263B9000A98734F7E859E269A7D426DA1DE3F', 16)
>>> q = int('C3CF3596B474E01A788E6F4D7E93DBA155E112455C4EBC5BAE8F4732AC94B580832513AD2CC8C6FA46AE1FFC8AE433E5160944A386464B115C164F719369F1AE52D3948FB08BBAF9F84ED64541F8CB34DD2264E627DD9D7391A4024168AE03B4FAA6E4207D38CAD61C2B18982DD2F77B4D021F66BB5B8D5ADCC5A598089D345D', 16)
>>> n == p*q
True
n == pq の結果がTrueとなり、一致していることが確認できます。
p, q の値が決まれば、整数e, d もRSAの鍵生成の手順で求めることができます。
ここでは公開鍵で使用する整数e は 65537
と決めることとし、
秘密鍵で使用する整数d を求めてみましょう。
下記のようなプログラムで求めることができます。
さきほどの鍵の中の整数p, q, n, e, dを読み込ませておき、
秘密鍵で使用する整数dを、RSAの鍵生成の手順で求めた結果の整数Dと比較してみます。
はたして、OpenSSLの鍵の中身と同じ値になるでしょうか?
# -*- coding: utf-8 -*-
import math
def main():
"""公開鍵と秘密鍵を生成"""
p = int('C6951A1E8091B5D9F15F187B12BFE8A81A6EA88BCD6B8A278BFB49100AE0C6DCB493C525E83A1099CBBBF4C8722A6DE13853C335E67264BB867B23837B7CD30D754B13C41CF64D4A0380795795E49C347444246F7313D0439D68304EBC9FE9E71D352F8BA4328380BBB766163B3263B9000A98734F7E859E269A7D426DA1DE3F', 16)
q = int('C3CF3596B474E01A788E6F4D7E93DBA155E112455C4EBC5BAE8F4732AC94B580832513AD2CC8C6FA46AE1FFC8AE433E5160944A386464B115C164F719369F1AE52D3948FB08BBAF9F84ED64541F8CB34DD2264E627DD9D7391A4024168AE03B4FAA6E4207D38CAD61C2B18982DD2F77B4D021F66BB5B8D5ADCC5A598089D345D', 16)
n = int('97E44F07205A1198D5D8B6FC678559DBD5F136F44A9FBEFBC171F1108223240AE04D71D2B513B662DD79A26CB5ED81EDD2FBF43588CC268C95D6715A68612C31D9A6A6A878BDB6461CD0E03F03DEEC87CE32E8E6508F40993B5A8E79448FFB3129D5A5810F445604885244D6C6969DD435FBFE013625F9F1C20357C42C64443F1721CBB13830957AAE7723F64E390722C423D88A30F7A755FE1F7DEAF0012D3CB57575A91E62D8383B94C5B1BA36FC4EDB7A7B787D5959C5BFE9C41FA76EFFD033408FE3B941A53D675D81B8A3D0B7194E3C7B5AE2B8F19581527BCA35417C825796DEAC5DC87E3080FF8993E2369EC56D0943D727CEA8C2E390DB05F99588E3', 16)
e = int('010001', 16)
d = int('0A0879B3E5CE2D456432C97B88B70545812F4ACAFE36E91DA61C89A8E67FBDBCBE2E3F6B84AC85AAA82885B31A9D52ACCBF7FA9CA2A9A18E3DC3897F484FCAE48C14F7033FDE8030C76C80B388E4EBC8D8B5022F5C78E9F2753A3919078F5F2F532D1A9E98CC91C600DF364EDD954B5D9E274412FE69A9C5EF63DB23492F6256927A5DBB3AD6465BAF214BE0B58E8B48E1C1AC28F1B27B37D0D580915112E11F12262851F712915846A3DC45F704B85E36BC104C1C8410563100C973BFD997D574722BE294D6F65E7F5D48300FDBC592C167BC71DED8EDFBFC814A0AFCCCC0CEB2BCFAF5CC06F7C72FE58C37B636ABD8E53D005E3AD0692889978E42C98D2789', 16)
N, E, D = generate_keys(p, q)
print("=========秘密鍵ファイルの内容=========\n"
"p = {0:d}\n"
"q = {1:d}\n".format(p, q))
print("n = {0:d}\n"
"e = {1:d}\n"
"d = {2:d}\n".format(n, e, d))
print("============再計算後の結果============\n"
"N = {0:d}\n"
"E = {1:d}\n"
"D = {2:d}\n".format(N, E, D))
print("===============照合結果===============\n"
"N == n ... {0}\n"
"E == e ... {1}\n"
"D == d ... {2}\n".format(N == n, E == e, D == d))
def lcm(p, q):
"""
最小公倍数を求める。
"""
return (p * q) // math.gcd(p, q)
def generate_keys(p, q):
"""
与えられた 2 つの素数 p, q から秘密鍵と公開鍵を生成する。
"""
# 2つの素数(p, q)の積nを求める
n = p * q
# p - 1 と q - 1 の最小公倍数を求める
l = lcm(p - 1, q - 1)
#公開鍵で使用するeを算出する
e = 65537
# 秘密鍵で使用するdを算出する
"""
for i in range(2, l):
if (e * i) % l == 1:
d = i
break
"""
i = 0
while True:
if (i * l + 1) % e == 0:
d = (i * l + 1) // e
break
i += 1
return n, e, d
if __name__ == "__main__":
main()
実行例
$ python check_rsa.py
=========秘密鍵ファイルの内容=========
p = 139449324511565356428512286642655130426447197939578874016855236487282995700169310495082360928168536394955618866705187654764138082404125008789612792239952674581230902932322078366069854469858391655850571761770094638349538491773793929426364268166770990245822712042189994565115142224465057857624774455540121198143
q = 137502044805647677820319568086104169136913652271646067931934573159461074702750974762623151274192421826325614093474060956956735386243214387904643057658451082445275672767583056935887807520136033044478794151296665419648229990606602712134466116705834716237284897734851993095963492054499376932390803569802525619293
n = 19174567267106562614863777986013190733311720406517163236419798721057651041741132975692437635046772915985388744151381878391739010943167351904934011584525523379391186697776748275394847710370026168707668355363468298465929010753092617924044792285570698575811523066628303754919477816371556100017714944862167735437948517694796555955615103513053419306742815189247527511947589796524356990034768580398382078215058167355632932286761344674894465569796261922954597156699763620980072965273234707300569241109881200233991616854473630970425663335146366570862840409827825897729557509442076004084186075433324566154623231714988236572899
e = 65537
d = 1266562425794655073618647403778798277072591629763535096975163780207494565813164542956994713247745242432530446517712622664416103550253619579725335858361093591549574243781002232085452427456121630290301605358323607489799757198988936676887710847372256193214338211322366403024954139906807854448276362914206083993922215094114776012124366236002371990952894667648628314166422099626061405451672662307764909200435879249013664134531240274310366361076547379589865534234754212903474203891892270277583495216595050906895706003102832662158785980707445928619686610586861592808732191214885213804349178521403558427718487767558413690761
============再計算後の結果============
N = 19174567267106562614863777986013190733311720406517163236419798721057651041741132975692437635046772915985388744151381878391739010943167351904934011584525523379391186697776748275394847710370026168707668355363468298465929010753092617924044792285570698575811523066628303754919477816371556100017714944862167735437948517694796555955615103513053419306742815189247527511947589796524356990034768580398382078215058167355632932286761344674894465569796261922954597156699763620980072965273234707300569241109881200233991616854473630970425663335146366570862840409827825897729557509442076004084186075433324566154623231714988236572899
E = 65537
D = 1266562425794655073618647403778798277072591629763535096975163780207494565813164542956994713247745242432530446517712622664416103550253619579725335858361093591549574243781002232085452427456121630290301605358323607489799757198988936676887710847372256193214338211322366403024954139906807854448276362914206083993922215094114776012124366236002371990952894667648628314166422099626061405451672662307764909200435879249013664134531240274310366361076547379589865534234754212903474203891892270277583495216595050906895706003102832662158785980707445928619686610586861592808732191214885213804349178521403558427718487767558413690761
===============照合結果===============
N == n ... True
E == e ... True
D == d ... True
整数n, e だけでなく、整数dも一致している様子がわかります。
整数p, qが解れば、***公開鍵(整数e, nの組み合わせ)***だけでなく、
***秘密鍵(整数d, nの組み合わせ)***も、何度でも復元することができます。
8. 最後に
ファイルの内容を読み込んで、上記の 6-2, 6-3 の処理を繰り返せば、
ファイル単位での暗号化/電子署名の生成/復号の処理が作れますね。
だだし、個人で自作することはセキュリティの強度を確保するのが大変なので、あまりお勧めしません。
なお、OpenSSLにもファイル単位での暗号化/電子署名の生成/復号の機能は用意されています。
Python3で、暗号化/復号/電子署名の処理を作成したいのであれば、下記のツールが便利です。
- pycryptodome
- pycryptodomeを利用したファイルの暗号化/復号処理および電子署名の生成/照合処理の作成例
ちなみに、Pythonからは話がそれますが、、
Micrsoft .NET(C#) では、標準でクラスが用意されています。
- RSACryptoServiceProvider クラス