これは、「富士通クラウドテクノロジーズ Advent Calendar 2019」の10日目の記事です。
はじめに
ニフクラには、作成されるサーバーに初期登録されるSSHの公開鍵をアップロードする機能があり、コントロールパネル や 公式CLIツールを活用 して、手元で作った公開鍵をサーバーに登録することができます。
SSHキーはリージョン毎に管理されているため、最低でも利用するリージョン数、サーバーによって異なるキーを使う場合はその分だけ管理するキーが増えていき、不要になったカギを整理するうちに、「手元とニフクラコンパネのSSHキーが一致しない!このキーってなんだっけ?」ということになるわけです。(なるわけです)
ニフクラコンパネやAPI取得できる「フィンガープリント」の値を使って、ニフクラに登録されたSSHキーと手元のキーを確認することができるので、その方法をまとめておきたいと思います。
対象読者
(ニフクラユーザー || ニフクラ開発者 ) && ( Linux を CUI で操作できる人 )
罠
「SSH フィンガープリント」で検索すると出てくるであろう下記の方法では、
残念ながら「ニフクラのフィンガープリント」と一致する値は得られません。
ssh-keygen -lf <ssh の公開鍵 (e.g. path/to/id_rsa.pub )>
ssh-keygen -l -E md5 -f <ssh の公開鍵 (e.g. path/to/id_rsa.pub )>
「ニフクラのフィンガープリント」とは?
ニフクラのドキュメントを探すと、 DescribeKeyPairs API のドキュメントページ に記載がありました。
読み解くと実は、「ニフクラフィンガープリント」とは、「DER形式のSSH公開鍵の情報の MD5 Hash値」の事を指すそうです。
DER とは、バイナリファイルのフォーマットの一種で、鍵や証明書をASN.1 というデータ構造で表現し、それをシリアライズしたものだそうです。※出典
鍵や証明書を保存したものとしては他にも「PEM」形式と呼ばれるものがあります。
PEM形式は、「.PEM/.pem」という拡張子に使われており、見覚えのある人も多いのではないでしょうか。
ニフクラ上でSSHキーを作成する機能を使うと、秘密鍵がこのPEM形式でダウンロードできます。
ちなみに PEM とは?
鍵や証明書を保存したものとしては他にも「PEM」形式と呼ばれるものがありますが、こちらは、鍵や証明書を↑と同じように ASN.1 形式で表し、それを Base64 エンコーディングしたものとなります。
こんなの
-----BEGIN RSA PRIVATE KEY-----
6KyO44Gu5paH5a2X5YiX6KyO44Gu5paH5a2X5YiX6KyO44Gu5paH5a2X5YiX
772e5Lit55Wl772e6KyO44Gu5paH5a2X5YiX6KyO44Gu5paH5a2X5YiXCg==
-----END RSA PRIVATE KEY-----
実際にはもっと長いです。最初と最後の行が特徴的。
SSH公開鍵認証では秘密鍵を表現するのに利用されますし、 HTTPS 通信などで利用される SSL 証明書も PEM 形式で取り扱われることもあります。
実装
Linux の Shell コマンドで確認する
秘密鍵から「ニフクラのフィンガープリント」を確認する
DescribeKeyPairs API のドキュメントページ の記載を読み解くと、下記のコマンドで「ニフクラのフィンガープリント」が得られることがわかりました。
$ openssl rsa -in id_rsa -pubout -outform DER | openssl md5 -c
writing RSA key
(stdin)= 70:6c:67:f5:32:be:14:4b:c3:37:fd:bf:49:df:3c:18
コマンドの内容を確認すると、
- OpenSSL コマンドでSSH秘密鍵を読み込み、公開鍵をDER形式で出力
- パイプから受け取った標準出力のmd5値を出力
という処理になっていることがわかります。
パイプの後は、 md5sum
コマンドを使っても問題ありません。
※実験用に鍵にはパスフレーズを設定していません。パスフレーズが設定されている場合は DescribeKeyPairs API のドキュメントページ の通り、
openssl rsa -in <path/to/id_rsa> -pubout -outform DER -passin pass:<password> | openssl md5 -c
としてください。
SSHの認証に利用する秘密鍵から公開鍵の「ニフクラのフィンガープリント」を取り出していますから、たいていの場合は↑で事足りますね。
公開鍵から「ニフクラのフィンガープリント」を確認する
一応、公開鍵から「ニフクラのフィンガープリント」を計算する方法も併記しておきます。
秘密鍵には公開鍵の情報が含まれているため、遊び程度の意味しかありませんが...
SSHで利用される公開鍵には様々な形式があるかと思いますが、 ssh-keygen
で生成される .pub
形式の公開鍵は OpenSSH 独自のフォーマットとなっています。
こういうやつ
ssh-rsa AAAAB~~~~~ root@ubuntu
このままでは openssl コマンドでは読み込めないため、形式を変換して利用します。
ssh-keygen -f id_rsa_pass.pub -e -m PKCS8 | openssl rsa -pubin -pubout -outform DER | openssl md5 -c
用途があるかは不明ですが、公開鍵からも「ニフクラのフィンガープリント」を確認することができます。
Golang のプログラムで確認する
自分が最近 Golang を使ったプロダクトの開発に携わっているので、ちょっと調べてみました。
秘密鍵から「ニフクラのフィンガープリント」を確認
package main
import (
"crypto/md5"
"crypto/rsa"
"crypto/x509"
"fmt"
"io/ioutil"
"os"
"golang.org/x/crypto/ssh"
)
func main() {
filePath := "./id_rsa"
f, err := os.Open(filePath)
if err != nil {
fmt.Println(err)
return
}
defer f.Close()
// PEM 形式の秘密鍵を読み込む
privateKeyPEM, err := ioutil.ReadAll(f)
if err != nil {
fmt.Println(err)
return
}
// PEM 形式から interface{} 型として秘密鍵を取り出す
privateKey, err := ssh.ParseRawPrivateKey(privateKeyPEM)
if err != nil {
fmt.Println(err)
return
}
// interface{} 型から rsa.PrivateKey 型にキャスト
rsaPrivateKey, ok := privateKey.(*rsa.PrivateKey)
if !ok {
fmt.Println("error")
return
}
err = rsaPrivateKey.Validate()
if err != nil {
fmt.Println("error")
return
}
// DER 形式で公開鍵を取り出す
publicKeyDer, _ := x509.MarshalPKIXPublicKey(&rsaPrivateKey.PublicKey)
// DER 形式の公開鍵のMD5ハッシュ値を計算する
publicKeyDerMd5Sum := md5.Sum(publicKeyDer)
fmt.Printf("%x\n", publicKeyDerMd5Sum)
}
openssl コマンドでの手順と同様に、
- SSH秘密鍵を読み込む
- DER形式で公開鍵を取り出す
- MD5ハッシュ値をとる
という手順で値を取り出します。
おわりに
「ニフクラのフィンガープリント」と手元の秘密鍵が一致しているかを調べようとして、「SSH フィンガープリント」と検索して「罠」に嵌ってしまったところからこの記事につながりました。
MD5などのハッシュ関数を利用した、データをユニークに特定する不可逆な値を「フィンガープリント」と呼ぶらしいのですが、SSHの文脈だとすでに使われており混乱してしまいましたが、おかげで SSH の公開鍵認証の仕組みを調べて理解を深めることができました。
同じように困っている方の一助となれば幸いです。
明日は、 @kenta_ojapi が 「営業からエンジニアへ転向して思ひしことを徒然に綴ろうかな」という興味深い記事を書いてくれるみたいです。お楽しみに。