来自模数和指数的SSH SubjectPublicKeyInfo

我正在从公共SSH密钥中提取模数和指数,目的是生成PEM公钥。 这是我到目前为止的代码:

require "base64" require "openssl" def unpacked_byte_array(ssh_type, encoded_key) prefix = [7].pack("N") + ssh_type decoded = Base64.decode64(encoded_key) # Base64 decoding is too permissive, so we should validate if encoding is correct unless Base64.encode64(decoded).gsub("\n", "") == encoded_key && decoded.slice!(0, prefix.length) == prefix raise PublicKeyError, "validation error" end data = [] until decoded.empty? front = decoded.slice!(0,4) size = front.unpack("N").first segment = decoded.slice!(0, size) unless front.length == 4 && segment.length == size raise PublicKeyError, "byte array too short" end data << OpenSSL::BN.new(segment, 2) end return data end module OpenSSL module PKey class RSA def self.new_from_parameters(n, e) a = self.new # self.new(64) for ruby < 1.8.2 an = n # converted to OpenSSL::BN automatically ae = e a end end end end e, n = unpacked_byte_array('ssh-rsa', 'AAAAB3NzaC1yc2EAAAABIwAAAQEA3RC8whKGFx+b7BMTFtnIWl6t/qyvOvnuqIrMNI9J8+1sEYv8Y/pJRh0vAe2RaSKAgB2hyzXwSJ1Fh+ooraUAJ+q7P2gg2kQF1nCFeGVjtV9m4ZrV5kZARcQMhp0Bp67tPo2TCtnthPYZS/YQG6u/6Aco1XZjPvuKujAQMGSgqNskhKBO9zfhhkAMIcKVryjKYHDfqbDUCCSNzlwFLts3nJ0Hfno6Hz+XxuBIfKOGjHfbzFyUQ7smYnzF23jFs4XhvnjmIGQJcZT4kQAsRwQubyuyDuqmQXqa+2SuQfkKTaPOlVqyuEWJdG2weIF8g3YP12czsBgNppz3jsnhEgstnQ==') rsa = OpenSSL::PKey::RSA.new_from_parameters(n, e) puts rsa 

目标是使用ssh-keygen -f -e -m pem执行的纯Ruby实现。

现在,比较结果,它们看起来非常相似,但我的代码在键的开头返回了几个字节:

 $ ssh-keygen -f ~/.ssh/id_rsa_perso.pub -e -m pem -----BEGIN RSA PUBLIC KEY----- MIIBCAKCAQEA3RC8whKGFx+b7BMTFtnIWl6t/qyvOvnuqIrMNI9J8+1sEYv8Y/pJ Rh0vAe2RaSKAgB2hyzXwSJ1Fh+ooraUAJ+q7P2gg2kQF1nCFeGVjtV9m4ZrV5kZA RcQMhp0Bp67tPo2TCtnthPYZS/YQG6u/6Aco1XZjPvuKujAQMGSgqNskhKBO9zfh hkAMIcKVryjKYHDfqbDUCCSNzlwFLts3nJ0Hfno6Hz+XxuBIfKOGjHfbzFyUQ7sm YnzF23jFs4XhvnjmIGQJcZT4kQAsRwQubyuyDuqmQXqa+2SuQfkKTaPOlVqyuEWJ dG2weIF8g3YP12czsBgNppz3jsnhEgstnQIBIw== -----END RSA PUBLIC KEY----- $ ruby ssh2x509.rb -----BEGIN PUBLIC KEY----- MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEA3RC8whKGFx+b7BMTFtnI Wl6t/qyvOvnuqIrMNI9J8+1sEYv8Y/pJRh0vAe2RaSKAgB2hyzXwSJ1Fh+ooraUA J+q7P2gg2kQF1nCFeGVjtV9m4ZrV5kZARcQMhp0Bp67tPo2TCtnthPYZS/YQG6u/ 6Aco1XZjPvuKujAQMGSgqNskhKBO9zfhhkAMIcKVryjKYHDfqbDUCCSNzlwFLts3 nJ0Hfno6Hz+XxuBIfKOGjHfbzFyUQ7smYnzF23jFs4XhvnjmIGQJcZT4kQAsRwQu byuyDuqmQXqa+2SuQfkKTaPOlVqyuEWJdG2weIF8g3YP12czsBgNppz3jsnhEgst nQIBIw== -----END PUBLIC KEY----- 

请注意,我的输出具有ssh-keygen输出的内容,但前缀为MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0A

什么可能导致这些额外的字节,我怎么能得到正确的结果?

似乎Ruby OpenSSL中RSA密钥的输出格式在1.9.3中从PKCS#1(由OpenSSH使用)更改为X509(由OpenSSL 1.9.3后使用):

https://redmine.ruby-lang.org/issues/4421

此错误报告中建议的是模拟PKCS#1 :

 ary = [OpenSSL::ASN1::Integer.new(n), OpenSSL::ASN1::Integer.new(e)] pub_key = OpenSSL::ASN1::Sequence.new(ary) base64 = Base64.encode64(pub_key.to_der) #This is the equivalent to the PKCS#1 encoding used before 1.9.3 pem = "-----BEGIN RSA PUBLIC KEY-----\n#{base64}-----END RSA PUBLIC KEY-----" 

因此,不需要修补OpenSSL::PKey::RSA的猴子。

要解决此问题,您可以分析ASN1结构。

对于你的输出,它是

 SEQUENCE(2 elem) SEQUENCE(2 elem) OBJECT IDENTIFIER1.2.840.113549.1.1.1 NULL BIT STRING(1 elem) SEQUENCE(2 elem) INTEGER(2048 bit) 279069188856447290054297383130027286257044344789969750715307012565210… INTEGER35 

对于ssh输出,它是

 SEQUENCE(2 elem) INTEGER(2048 bit) 279069188856447290054297383130027286257044344789969750715307012565210… INTEGER35 

这是什么意思? 这意味着您的RSA密钥结构不同。 在SSH中,它只包含2048位整数的序列。 然而,在您的情况下,它还带有对象识别。

解? 删除可以通过分析ASN1结构计算的起始位。

或者通过hexdump分析从RSA公钥中删除多少字节。

您的RSA公钥:

 30 82 01 20 30 0D 06 09 2A 86 48 86 F7 0D 01 01 01 05 00 03 82 01 0D 00 **30 82 01 08 02 82 01 01 00 DD 10 BC C2** 12 86 17 1F 9B EC 13 13 16 D9 C8 5A 5E AD FE AC AF 3A F9 EE A8 8A CC 34 8F 49 F3 ED 6C 11 8B FC 63 FA 49 46 1D 2F 01 ED 91 69 22 80 80 1D A1 CB 35 F0 48 9D 45 87 EA 28 AD A5 00 27 EA BB 3F 68 20 DA 44 05 D6 70 85 78 65 63 B5 … skipping 160 bytes … 0F D7 67 33 B0 18 0D A6 9C F7 8E C9 E1 12 0B 2D 9D 02 01 23 

SSH RSA公钥

 **30 82 01 08 02 82 01 01 00 DD 10 BC C2** 12 86 17 1F 9B EC 13 13 16 D9 C8 5A 5E AD FE AC AF 3A F9 EE A8 8A CC 34 8F 49 F3 ED 6C 11 8B FC 63 FA 49 46 1D 2F 01 ED 91 69 22 80 80 1D A1 CB 35 F0 48 9D 45 87 EA 28 AD A5 00 27 EA BB 3F 68 20 DA 44 … skipping 160 bytes … 74 6D B0 78 81 7C 83 76 0F D7 67 33 B0 18 0D A6 9C F7 8E C9 E1 12 0B 2D 9D 02 01 23 

通过分析,你可以看到你必须删除这些:

 30 82 01 20 30 0D 06 09 2A 86 48 86 F7 0D 01 01 01 05 00 03 82 01 0D 00 

意味着24个字节。 从密钥中删除24个字节。

或者您可以使用ASN1解析器并只提取序列。