我从公共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
a.n = n # converted to OpenSSL::BN automatically
a.e = 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 <file> -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
。
什么可能导致这些额外的字节,我怎么能得到正确的结果?
答案 0 :(得分:2)
似乎Ruby OpenSSL中RSA密钥的输出格式在1.9.3中已从PKCS#1(由OpenSSH使用)更改为X509(由OpenSSL 1.9.3后使用):
https://redmine.ruby-lang.org/issues/4421
此错误报告中建议的是emulate the 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
的猴子。
答案 1 :(得分:0)
要解决此问题,您可以分析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解析器并只提取序列。