最近はさまざまな分野でセキュリティインシデントが起きており、それに対応する形で僕らが業務で関わるデータ基盤関連でも、セキュリティ要件が厳しくなってきました。直近ではSnowflakeでMFA認証が必須化されるなどしています。
さて、人間がアクセスするときはスマホ等を使ってMFA認証を行えば良いのですが、プログラムがアクセスする場合はそうは行きませんので、key-pair認証など別の設定が必要になります。かつてはこれ系の作業はインフラに強い方に丸投げしていましたが、最近は自分でやることになってしまったので、RSAキーペアの生成方法をメモっておきます。
また、少し興味が湧いたので作った鍵の中身を調べたのでそれも書いておきます。
キーペアの生成
RSAキーペアは次のようにしてコマンドで生成できます。一個目のコマンドで秘密鍵を生成して、それを使って2個目のコマンドで公開鍵を生成しています。鍵のファイル名はサンプルなので、実際は誰が何に使う鍵なのかわかる名前をつけることになるでしょう。
% openssl genrsa -out private_key.pem 2048
% openssl rsa -in private_key.pem -pubout -out public_key.pub
.pemの方が秘密鍵で、.pub の方が公開鍵です。人に渡したりサーバーやサービスにセットする方を間違えないように気をつけましょう。
2048 は鍵の長さです。
中身を見ると次のような形式になっています。
% cat private_key.pem
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAwnZIg27PKq6mLwuDVqiz3cOMYJCtqB2tAaL+vOB/VGdM1ds2
iLpPOdNhZigrFoD8LBRFeXrn23Q3opISloFX5LrvtJXsIOk45Zflpub50tkPTflj
- 略 -
WdfGVngs11Gd3OT9CC2V5tVCpDPxUHBviKIOCOKHb5q4IYgBXlX1Xwcsd4KJIx50
bHQjCcYt1wSlAQagSlLfsPRC1y0mXurZOs/F0BGLgQro/jeiKgqo
-----END RSA PRIVATE KEY-----
% cat public_key.pub
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwnZIg27PKq6mLwuDVqiz
3cOMYJCtqB2tAaL+vOB/VGdM1ds2iLpPOdNhZigrFoD8LBRFeXrn23Q3opISloFX
- 略 -
rDHrhHTAgKDaNrFy1PLRWHmE0w0M/ngchE25k69HoGemT4oIvL+JbDL3V6AFMaDP
hQIDAQAB
-----END PUBLIC KEY-----
中身は秘密鍵の方が長いです。というのも秘密鍵は公開鍵の情報を丸々含んでいます。
鍵の中身を見てみる
せっかくどこでも使う予定のない鍵が手に入りましたのでこの鍵の中身を見てみましょう。
実際に使う鍵の中身をこんな風にブログに書いたらダメですよ。僕はここで紹介した鍵は削除しました。
Pythonの cryptography というライブラリを使うと情報を取り出すことができます。
さっそく秘密鍵の中身を見てみましょう。
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
# 秘密鍵を読み込む
with open("private_key.pem", "rb") as f:
private_key = serialization.load_pem_private_key(
f.read(),
password=None, # パスフレーズ付きの場合はここにバイト列で指定
backend=default_backend()
)
# 秘密鍵の数値情報を取得
numbers = private_key.private_numbers()
# 各パラメータを取り出す
p = numbers.p
q = numbers.q
d = numbers.d
e = numbers.public_numbers.e
n = numbers.public_numbers.n
# 結果表示
print("p =", p)
print("q =", q)
print("N =", n)
print("e =", e)
print("d =", d)
# 以下結果
p = 174133000333614936251362999206227860029444673295141932972756327545187355887637580675845610664732281565651334714513232626572441206440561414170428774362884631860250636232708661138275511162457879412349843835517159363893036855388397226040977706038652364697299640899324687348973169499749667372761674059770673656629
q = 140975913603825512865433959056646382702361935777499472419212522540191330505017933970970762139496160871799290332197347035791494515097734048740629670759835202686652442863083851916085465724371693643565312804882327777405809193107901841163414696083477090821404402929399270028604262684620027839978379930283952219409
N = 24548558810606618461714517881475571186801470737734856289181906135685571436612839039259065775494709490930728278157311520395762849181607242747850831454736180599681279862693238176150540622438912255878978592670111595906897714523616692063251022518366718003120234171604788397519019358925498472209334335726916622936597269506716359074552614787798827874135764437117487596219535422595921699375964590386756870344786648678653088861414353465043760006273962034589357489387236169768178574232749586811613156702272081106349985519890401315163913638436782511526167393949905883529322221683725691489616448816345865842671162340854135312261
e = 65537
d = 13240122499939151056139632017890916806382220519198528545000729911013288578512199544084871110462967428718562833941536693950425399850805975394178606579955746763766028339817475469008242052296385728802878622993277607771504852453824239961544377877484602304900909673370988227827101900910014106492261482291519628163156164158764612915654971280659892167924007771913593516749042459046495321420282068147453755383464469170205891152453336170458271647963007252530827415316811250149784471773288702352832641102529049728530752039357391142463166518174867148426717436503599697894186806424473904750877262018256278182647586659073657018817
簡単に取り出せましたね。
これがちゃんとRSA暗号の鍵になっているのかみておきます。p, qが素数であるかどうかの確認はちょっと大変なので置いといて、 $p\cdot q = N$ と $e \cdot d \equiv 1 \pmod{(p – 1)(q – 1)}$を見てみます。
print(p*q-n)
# 0
print((e*d)%((p-1)*(q-1)))
# 1
バッチリですね。
最後にもう一つ、鍵の長さ2048を確認しておきましょう。これはNのビット長です。ビット長を返す専用メソッドがあるのでそれを使うこともできますし、概算値にはなりますが、2を底とする対数で見ることもできます。(数が大きすぎてnumpyのlogでは扱えないので、mathモジュールのlog2を使います)
print(n.bit_length())
# 2048
import math
print(math.log2(n))
# 2047.6033447803356
こちらもバッチリですね。
ちなみに、公開鍵も同じようにして情報を取り出せます。こちらはNとeしか含まれておらず、p,q,dはありません。
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
# 公開鍵を読み込む
with open("public_key.pub", "rb") as f:
public_key = serialization.load_pem_public_key(
f.read(),
backend=default_backend()
)
# 数値情報を取得
numbers = public_key.public_numbers()
# N と e を取得
n = numbers.n
e = numbers.e
# 結果表示
print("N =", n)
print("e =", e)
# 以下結果
N = 24548558810606618461714517881475571186801470737734856289181906135685571436612839039259065775494709490930728278157311520395762849181607242747850831454736180599681279862693238176150540622438912255878978592670111595906897714523616692063251022518366718003120234171604788397519019358925498472209334335726916622936597269506716359074552614787798827874135764437117487596219535422595921699375964590386756870344786648678653088861414353465043760006273962034589357489387236169768178574232749586811613156702272081106349985519890401315163913638436782511526167393949905883529322221683725691489616448816345865842671162340854135312261
e = 65537
秘密鍵のうち、公開鍵として必要な部分が含まれていることがわかりました。