RubyのOpenSSLライブラリでOpenSSH形式のRSA秘密鍵を扱えるように鍵をパースしてビルドする

どうも、公開鍵暗号方式大好き芸人です。
かれこれ3年くらい前だけど、ssh-keygenが出力する秘密鍵のデフォルト形式が変わってOpenSSLライブラリで読み込めなくなった。
それを解消するべく、秘密鍵をパースしてOpenSSLライブラリで読み込める形でビルドする。

事前知識

まずはおさらいから。
手元のMacに入ってるOpenSSH_8.1p1を前提に。

KeyFormat

ファイル形式。
これらの形式問わず拡張子がどれも.pemだったりするのでなんて呼べば良いのかややこしい。

鍵生成は以下。
-mオプションでファイル形式を指定する。

% ssh-keygen -m {keyformat}

鍵のファイル形式変換は以下。
-mオプションで変換後のファイル形式を指定する。
鍵は上書きされる。

% ssh-keygen -p -m {keyformat} -f private.pem

対応してる形式は以下。

The supported key formats are: ``RFC4716'' (RFC 4716/SSH2 public or private key), ``PKCS8'' (PKCS8 public or private key) or ``PEM'' (PEM public key).

・RFC4716 / SSH2
OpenSSH 7.8以降のデフォルトはこれになった。
これがOpenSSLライブラリでは開けなくて困る。

正式名称なのかはわからないけどOpenSSH形式と言ったらこれ。
ちなみにRFC4716というのは公開鍵ファイル形式の定義なので秘密鍵に対して言うのは違うんじゃないかなと思うけど詳しいことはよく知らない。

見た目。

-----BEGIN OPENSSH PRIVATE KEY-----
〜 Base64文字列(横幅70文字で改行) 〜
-----END OPENSSH PRIVATE KEY-----

RFC 4716 - The Secure Shell (SSH) Public Key File Format

・PEM
OpenSSH 7.8より前のデフォルトはこれだった。
これならOpenSSLライブラリで普通に開ける。

見た目。

-----BEGIN RSA PRIVATE KEY-----
〜 Base64文字列(横幅64文字で改行) 〜
-----END RSA PRIVATE KEY-----

PKCS#8
今回の話題とはあまり関係ないけど一応書くだけ書いておく。

見た目。

-----BEGIN PRIVATE KEY-----
〜 Base64文字列(横幅64文字で改行) 〜
-----END PRIVATE KEY-----
KeyType

暗号方式。
この方式によってssh-keygenではデフォルトの出力ファイル名が、~/.ssh/id_{keytype}になるのでファイル名に関してはKeyFormatの拡張子問題よりもわかりやすい。
ただしKeyTypeを問わずBEGINほげほげの行はどれも同じになるのでわかりづらい。
ssh-keygenだとOpenSSH形式、openssl genpkeyだとPKCS#8形式で出力される。
なのでopenssl asn1parseとかして鍵構造のパースをしないKeyTypeはわからない。(鍵の長さからなんとなく察することも出来なくはないけど特殊な訓練が必要)

対応してる方式は以下。

-t dsa | ecdsa | ed25519 | rsa
Specifies the type of key to create. The possible values are ``dsa'', ``ecdsa'', ``ed25519'', or ``rsa''.

強度は、ed25519 > rsa > ecdsa > dsaだと思う。
デフォルトはrsa
ed25519は現代最強っぽい。
ecdsaとdsaをわざわざ新規に使う人は現代にはいないのではなかろうか。

詳しくはこちら。
SSHの公開鍵暗号には「RSA」「DSA」「ECDSA」「EdDSA」のどれを使えばよいのか? - GIGAZINE

ed25519で出来ることは署名のみで、暗号化が出来ない。
個人的には~/.ssh/id_rsaを暗号化するための鍵として使っているので、それが理由でed25519に移行出来ずにいる。
まぁSSHとかの署名はed25519で暗号化用はRSAと、鍵を使い分ければいいといえばそれだけの話なのだけれども。

やりたいこと

さてここから本題。

Yamlファイルの特定の要素を暗号化する、結構昔に作ったGemの利便性を上げるために。
そもそもこれは何なのかっていうと、 ~/.ssh/id_rsa を鍵として暗号化/復号化を行うライブラリ。
Workspace内で鍵生成するのが面倒だったり、gitignoreに鍵を指定し忘れて誤コミットしてしまったり、そういうのを解消するのに役立つ。

GitHub - yoshida-eth0/ruby-secure_conf

ssh-keygenが生成する秘密鍵のデフォルトKeyFormatがOpenSSH形式に変更されたことによってこのライブラリが使えない状況が起こっていた。
鍵の扱いを楽にするライブラリを使う人なんて、ssh-keygenでわざわざKeyFormatを指定してPEM形式で鍵生成なんてしない訳で。
だからもうこっちでOpenSSH形式の鍵に対応してしまおう、と。

要件としてはこんな。
・OpenSSH形式の秘密鍵を使いたい。
ssh-keygenで変換しちゃうのが楽だとは思うけど、popenしたくない。
・変換するために鍵を不用意にコピーしたりファイルシステムに書き込みしたくない。

ということでRubyで自力でバイナリ弄ってOpenSSH形式の秘密鍵をパースしてPEM形式にビルドすることに。

やった

秘密鍵をPEM形式に変換してOpenSSLライブラリで読み込めるようにした。
secure_conf v2.0.0で、OpenSSH形式のRSA秘密鍵に対応済み。
RSA以外に対応したい場合、SecureConf::OpenSSH::Keytype 以下にクラス追加してpullreqください。

特に面白みのある解説はないので後述の参考資料を。
OpenSSH形式からPEM形式に変換する処理は以下。

ruby-secure_conf/openssh.rb at master · yoshida-eth0/ruby-secure_conf · GitHub

あとがき

こういう事象で困っている、原因と事前知識を説明、こうしたい、やりました、リリースしました、ソースどうぞ、解説は他所のサイトで…。
という中身があるのかないのかわからない記事になってしまった。