背景
OpenSSH 6.8 以降と以前では ssh-keygen で生成される private key の形式が変わっている。
Add FingerprintHash option to ssh(1) and sshd(8), and equivalent command-line flags to the other tools to control algorithm used for key fingerprints. The default changes from MD5 to SHA256 and format from hex to base64.
FingerprintHash オプションを ssh(1) と sshd(8) に追加する. また, 鍵の指紋のために使われるアルゴリズムを制御する他のツールに 同様の指定をするコマンドラインフラグも追加する. デフォルトが MD5 から SHA256 に, 形式が hex から base64 になる.
問題
社内の内製ツールで、CircleCI の sshkey や環境変数をコード管理して適用するツールがある。そのツールが sshkey gem を使っているが、上記新形式の private key を読み込むときに以下のエラーで失敗する。
irb(main):004:0> f = File.read(File.expand_path("./openssh")) => "-----BEGIN OPENSSH PRIVATE KEY-----\nbbbbbbbbb (snip) ssssssssssecret\n-----END OPENSSH PRIVATE KEY-----\n" irb(main):005:0> k = SSHKey.new(f, comment: "foo@bar.com") Traceback (most recent call last): 9: from /Users/chaspy/.rbenv/versions/2.6.5/bin/irb:23:in `<main>' 8: from /Users/chaspy/.rbenv/versions/2.6.5/bin/irb:23:in `load' 7: from /Users/chaspy/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>' 6: from (irb):5 5: from (irb):5:in `new' 4: from /Users/chaspy/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sshkey-1.9.0/lib/sshkey.rb:214:in `initialize' 3: from /Users/chaspy/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sshkey-1.9.0/lib/sshkey.rb:218:in `rescue in initialize' 2: from /Users/chaspy/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sshkey-1.9.0/lib/sshkey.rb:218:in `new' 1: from /Users/chaspy/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sshkey-1.9.0/lib/sshkey.rb:218:in `initialize' OpenSSL::PKey::DSAError (Neither PUB key nor PRIV key)
上記 key は以下のコマンドで生成している。
ssh-keygen -t rsa -b 4096
なお、旧形式では成功する。
$ ssh-keygen -t rsa -b 4096 -m pem
irb(main):002:0> f = File.read(File.expand_path("./pem")) => "-----BEGIN RSA PRIVATE KEY-----\nbbbbbbb (snip) aaaaaaa\n-----END RSA PRIVATE KEY-----\n" irb(main):003:0> k = SSHKey.new(f, comment: "foo@bar.com") => #<SSHKey:0x00007fa9ef87e6a0 @passphrase=nil, @comment="foo@bar.com", @directives=[], @key_object=#<OpenSSL::PKey::RSA:0x00007fa9ef87e5b0>, @type="rsa">
バージョン
- Ruby 2.6.5
- sshkey 1.9.0
調査
sshkey gem
エラーが発生している箇所は以下。
# Create a new SSHKey object # # ==== Parameters # * private_key - Existing RSA or DSA private key # * options<~Hash> # * :comment<~String> - Comment to use for the public key, defaults to "" # * :passphrase<~String> - If the key is encrypted, supply the passphrase # * :directives<~Array> - Options prefixed to the public key # def initialize(private_key, options = {}) @passphrase = options[:passphrase] @comment = options[:comment] || "" self.directives = options[:directives] || [] begin @key_object = OpenSSL::PKey::RSA.new(private_key, passphrase) @type = "rsa" rescue @key_object = OpenSSL::PKey::DSA.new(private_key, passphrase) @type = "dsa" end end
最初の begin block でエラーとなり、resque でキャッチして DSA のところでこけてスタックトレースに出ている箇所が表示されているのだと思う。
OpenSSL::PKey::RSA.new
は Ruby 標準ライブラリの openssl である。
が、issue を見ても似たような現象を見つけられず、そもそも Ruby のどのバージョンが openssl のどのバージョンを使っているかわかっていない。
Ruby の Version を変えてみる
- Ruby 2.7.2
irb(main):001:0> require 'sshkey' => true irb(main):002:0> f = File.read(File.expand_path("./openssh")) => "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn\nNhAAAAAwEAAQAAAgE... irb(main):003:0> k = SSHKey.new(f, comment: "foo@bar.com") Traceback (most recent call last): 9: from /Users/chaspy/.rbenv/versions/2.7.2/bin/irb:23:in `<main>' 8: from /Users/chaspy/.rbenv/versions/2.7.2/bin/irb:23:in `load' 7: from /Users/chaspy/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/irb-1.2.6/exe/irb:11:in `<top (required)>' 6: from (irb):3 5: from (irb):3:in `new' 4: from /Users/chaspy/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/sshkey-1.9.0/lib/sshkey.rb:214:in `initialize' 3: from /Users/chaspy/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/sshkey-1.9.0/lib/sshkey.rb:218:in `rescue in initialize' 2: from /Users/chaspy/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/sshkey-1.9.0/lib/sshkey.rb:218:in `new' 1: from /Users/chaspy/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/sshkey-1.9.0/lib/sshkey.rb:218:in `initialize' OpenSSL::PKey::DSAError (Neither PUB key nor PRIV key)
再現する。
- Ruby 2.7.2 / sshkey 2.0.0
irb(main):001:0> require 'sshkey' => true irb(main):002:0> f = File.read(File.expand_path("./openssh")) => "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn\nNhAAAAAwEAAQAAAgE... irb(main):003:0> k = SSHKey.new(f, comment: "foo@bar.com") Traceback (most recent call last): 9: from /Users/chaspy/.rbenv/versions/2.7.2/bin/irb:23:in `<main>' 8: from /Users/chaspy/.rbenv/versions/2.7.2/bin/irb:23:in `load' 7: from /Users/chaspy/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/irb-1.2.6/exe/irb:11:in `<top (required)>' 6: from (irb):3 5: from (irb):3:in `new' 4: from /Users/chaspy/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/sshkey-2.0.0/lib/sshkey.rb:246:in `initialize' 3: from /Users/chaspy/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/sshkey-2.0.0/lib/sshkey.rb:250:in `rescue in initialize' 2: from /Users/chaspy/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/sshkey-2.0.0/lib/sshkey.rb:250:in `new' 1: from /Users/chaspy/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/sshkey-2.0.0/lib/sshkey.rb:250:in `initialize' OpenSSL::PKey::DSAError (Neither PUB key nor PRIV key)
再現する。
- Ruby 3.0.0 / sshkey 2.0.0
irb(main):001:0> require 'sshkey' => true irb(main):002:0> f = File.read(File.expand_path("./openssh")) => "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn\nNhAAAAAwEAAQAAAgE... irb(main):003:0> k = SSHKey.new(f, comment: "foo@bar.com") Traceback (most recent call last): 9: from /Users/chaspy/.rbenv/versions/3.0.0/bin/irb:23:in `<main>' 8: from /Users/chaspy/.rbenv/versions/3.0.0/bin/irb:23:in `load' 7: from /Users/chaspy/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.0/exe/irb:11:in `<top (required)>' 6: from (irb):3:in `<main>' 5: from (irb):3:in `new' 4: from /Users/chaspy/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/sshkey-2.0.0/lib/sshkey.rb:246:in `initialize' 3: from /Users/chaspy/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/sshkey-2.0.0/lib/sshkey.rb:250:in `rescue in initialize' 2: from /Users/chaspy/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/sshkey-2.0.0/lib/sshkey.rb:250:in `new' 1: from /Users/chaspy/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/sshkey-2.0.0/lib/sshkey.rb:250:in `initialize' OpenSSL::PKey::DSAError (Neither PUB key nor PRIV key)
再現する。
sshkey に類似 issue があったのでコメントしておいた。
openssl library
さて最新の Ruby でも再現するので、openssl library の問題の可能性が高い。こっちでも再現させる。
使っているのはこれである。
irb(main):001:0' require 'openssl' => true irb(main):002:0> f = File.read(File.expand_path("./openssh")) => "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn\nNhAAAAAwEAAQAAAgE... irb(main):003:0> OpenSSL::PKey::RSA.new(f) Traceback (most recent call last): 6: from /Users/chaspy/.rbenv/versions/3.0.0/bin/irb:23:in `<main>' 5: from /Users/chaspy/.rbenv/versions/3.0.0/bin/irb:23:in `load' 4: from /Users/chaspy/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.0/exe/irb:11:in `<top (required)>' 3: from (irb):3:in `<main>' 2: from (irb):3:in `new' 1: from (irb):3:in `initialize' OpenSSL::PKey::RSAError (Neither PUB key nor PRIV key: nested asn1 error)
再現する。
さてここからは C の世界である。
トレースが出てないので、推測だが、初期化はここではないか。
raise されてるところはここではないか。
ただ、 nested asn1 error
というのがどこで出ているのかわかっていない。
repository を検索すると別の部分で同じエラーが出ているようだ。
ここでお手上げ。