Skip to content

Commit 14af5e9

Browse files
committed
[fix] RSA key DER format to closely follow OpenSSL
1 parent bdf4b60 commit 14af5e9

File tree

5 files changed

+153
-14
lines changed

5 files changed

+153
-14
lines changed

src/main/java/org/jruby/ext/openssl/PKey.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ static SecureRandom getSecureRandom(final Ruby runtime) {
280280
return OpenSSL.getSecureRandom(runtime);
281281
}
282282

283-
// shared Helpers for PKeyRSA / PKEyDSA :
283+
// shared Helpers for PKeyRSA / PKeyDSA :
284284

285285
protected PrivateKey tryPKCS8EncodedKey(final Ruby runtime, final KeyFactory keyFactory, final byte[] encodedKey) {
286286
try {

src/main/java/org/jruby/ext/openssl/impl/PKey.java

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,16 @@
5555
import javax.crypto.spec.DHParameterSpec;
5656

5757
import org.bouncycastle.asn1.ASN1EncodableVector;
58+
import org.bouncycastle.asn1.ASN1Encoding;
5859
import org.bouncycastle.asn1.ASN1InputStream;
5960
import org.bouncycastle.asn1.ASN1Integer;
6061
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
6162
import org.bouncycastle.asn1.ASN1Primitive;
6263
import org.bouncycastle.asn1.ASN1Sequence;
64+
import org.bouncycastle.asn1.DERNull;
65+
import org.bouncycastle.asn1.DERSequence;
6366
import org.bouncycastle.asn1.DLSequence;
67+
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
6468
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
6569
import org.bouncycastle.asn1.sec.ECPrivateKeyStructure;
6670
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
@@ -69,6 +73,7 @@
6973
import org.bouncycastle.jce.ECNamedCurveTable;
7074
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
7175
import org.bouncycastle.jce.spec.ECPublicKeySpec;
76+
import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
7277

7378
import org.jruby.ext.openssl.SecurityHelper;
7479

@@ -285,23 +290,26 @@ public static KeyPair readECPrivateKey(final KeyFactory ecFactory, final byte[]
285290
}
286291

287292
public static byte[] toDerRSAKey(RSAPublicKey pubKey, RSAPrivateCrtKey privKey) throws IOException {
288-
ASN1EncodableVector vec = new ASN1EncodableVector();
289293
if ( pubKey != null && privKey == null ) {
294+
ASN1EncodableVector vec = new ASN1EncodableVector();
290295
vec.add(new ASN1Integer(pubKey.getModulus()));
291296
vec.add(new ASN1Integer(pubKey.getPublicExponent()));
297+
// pubKey.getEncoded() :
298+
return KeyUtil.getEncodedSubjectPublicKeyInfo(
299+
new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new DERSequence(vec)
300+
);
292301
}
293-
else {
294-
vec.add(new ASN1Integer(BigInteger.ZERO));
295-
vec.add(new ASN1Integer(privKey.getModulus()));
296-
vec.add(new ASN1Integer(privKey.getPublicExponent()));
297-
vec.add(new ASN1Integer(privKey.getPrivateExponent()));
298-
vec.add(new ASN1Integer(privKey.getPrimeP()));
299-
vec.add(new ASN1Integer(privKey.getPrimeQ()));
300-
vec.add(new ASN1Integer(privKey.getPrimeExponentP()));
301-
vec.add(new ASN1Integer(privKey.getPrimeExponentQ()));
302-
vec.add(new ASN1Integer(privKey.getCrtCoefficient()));
303-
}
304-
return new DLSequence(vec).getEncoded();
302+
ASN1EncodableVector vec = new ASN1EncodableVector();
303+
vec.add(new ASN1Integer(BigInteger.ZERO));
304+
vec.add(new ASN1Integer(privKey.getModulus()));
305+
vec.add(new ASN1Integer(privKey.getPublicExponent()));
306+
vec.add(new ASN1Integer(privKey.getPrivateExponent()));
307+
vec.add(new ASN1Integer(privKey.getPrimeP()));
308+
vec.add(new ASN1Integer(privKey.getPrimeQ()));
309+
vec.add(new ASN1Integer(privKey.getPrimeExponentP()));
310+
vec.add(new ASN1Integer(privKey.getPrimeExponentQ()));
311+
vec.add(new ASN1Integer(privKey.getCrtCoefficient()));
312+
return new DERSequence(vec).toASN1Primitive().getEncoded(ASN1Encoding.DER);
305313
}
306314

307315
public static byte[] toDerDSAKey(DSAPublicKey pubKey, DSAPrivateKey privKey) throws IOException {

src/test/ruby/fixtures/pkey/rsa1024

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
MIICXgIBAAKBgQDLwsSw1ECnPtT+PkOgHhcGA71nwC2/nL85VBGnRqDxOqjVh7Cx
3+
aKPERYHsk4BPCkE3brtThPWc9kjHEQQ7uf9Y1rbCz0layNqHyywQEVLFmp1cpIt/
4+
Q3geLv8ZD9pihowKJDyMDiN6ArYUmZczvW4976MU3+l54E6lF/JfFEU5hwIDAQAB
5+
AoGBAKSl/MQarye1yOysqX6P8fDFQt68VvtXkNmlSiKOGuzyho0M+UVSFcs6k1L0
6+
maDE25AMZUiGzuWHyaU55d7RXDgeskDMakD1v6ZejYtxJkSXbETOTLDwUWTn618T
7+
gnb17tU1jktUtU67xK/08i/XodlgnQhs6VoHTuCh3Hu77O6RAkEA7+gxqBuZR572
8+
74/akiW/SuXm0SXPEviyO1MuSRwtI87B02D0qgV8D1UHRm4AhMnJ8MCs1809kMQE
9+
JiQUCrp9mQJBANlt2ngBO14us6NnhuAseFDTBzCHXwUUu1YKHpMMmxpnGqaldGgX
10+
sOZB3lgJsT9VlGf3YGYdkLTNVbogQKlKpB8CQQDiSwkb4vyQfDe8/NpU5Not0fII
11+
8jsDUCb+opWUTMmfbxWRR3FBNu8wnym/m19N4fFj8LqYzHX4KY0oVPu6qvJxAkEA
12+
wa5snNekFcqONLIE4G5cosrIrb74sqL8GbGb+KuTAprzj5z1K8Bm0UW9lTjVDjDi
13+
qRYgZfZSL+x1P/54+xTFSwJAY1FxA/N3QPCXCjPh5YqFxAMQs2VVYTfg+t0MEcJD
14+
dPMQD5JX6g5HKnHFg2mZtoXQrWmJSn7p8GJK8yNTopEErA==
15+
-----END RSA PRIVATE KEY-----

src/test/ruby/rsa/test_rsa.rb

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,101 @@ def test_read_private_key_with_password
103103
assert key.is_a?(OpenSSL::PKey::RSA)
104104
end
105105

106+
def test_RSAPrivateKey_encrypted
107+
rsa1024 = Fixtures.pkey("rsa1024")
108+
# key = abcdef
109+
pem = <<~EOF
110+
-----BEGIN RSA PRIVATE KEY-----
111+
Proc-Type: 4,ENCRYPTED
112+
DEK-Info: AES-128-CBC,733F5302505B34701FC41F5C0746E4C0
113+
114+
zgJniZZQfvv8TFx3LzV6zhAQVayvQVZlAYqFq2yWbbxzF7C+IBhKQle9IhUQ9j/y
115+
/jkvol550LS8vZ7TX5WxyDLe12cdqzEvpR6jf3NbxiNysOCxwG4ErhaZGP+krcoB
116+
ObuL0nvls/+3myy5reKEyy22+0GvTDjaChfr+FwJjXMG+IBCLscYdgZC1LQL6oAn
117+
9xY5DH3W7BW4wR5ttxvtN32TkfVQh8xi3jrLrduUh+hV8DTiAiLIhv0Vykwhep2p
118+
WZA+7qbrYaYM8GLLgLrb6LfBoxeNxAEKiTpl1quFkm+Hk1dKq0EhVnxHf92x0zVF
119+
jRGZxAMNcrlCoE4f5XK45epVZSZvihdo1k73GPbp84aZ5P/xlO4OwZ3i4uCQXynl
120+
jE9c+I+4rRWKyPz9gkkqo0+teJL8ifeKt/3ab6FcdA0aArynqmsKJMktxmNu83We
121+
YVGEHZPeOlyOQqPvZqWsLnXQUfg54OkbuV4/4mWSIzxFXdFy/AekSeJugpswMXqn
122+
oNck4qySNyfnlyelppXyWWwDfVus9CVAGZmJQaJExHMT/rQFRVchlmY0Ddr5O264
123+
gcjv90o1NBOc2fNcqjivuoX7ROqys4K/YdNQ1HhQ7usJghADNOtuLI8ZqMh9akXD
124+
Eqp6Ne97wq1NiJj0nt3SJlzTnOyTjzrTe0Y+atPkVKp7SsjkATMI9JdhXwGhWd7a
125+
qFVl0owZiDasgEhyG2K5L6r+yaJLYkPVXZYC/wtWC3NEchnDWZGQcXzB4xROCQkD
126+
OlWNYDkPiZioeFkA3/fTMvG4moB2Pp9Q4GU5fJ6k43Ccu1up8dX/LumZb4ecg5/x
127+
-----END RSA PRIVATE KEY-----
128+
EOF
129+
key = OpenSSL::PKey::RSA.new(pem, "abcdef")
130+
assert_same_rsa rsa1024, key
131+
key = OpenSSL::PKey::RSA.new(pem) { "abcdef" }
132+
assert_same_rsa rsa1024, key
133+
assert_predicate key, :private?
134+
135+
##
136+
der = "0\x82\x02^\x02\x01\x00\x02\x81\x81\x00\xCB\xC2\xC4\xB0\xD4@\xA7>\xD4\xFE>C\xA0\x1E\x17\x06\x03\xBDg\xC0-\xBF\x9C\xBF9T\x11\xA7F\xA0\xF1:\xA8\xD5\x87\xB0\xB1h\xA3\xC4E\x81\xEC\x93\x80O\nA7n\xBBS\x84\xF5\x9C\xF6H\xC7\x11\x04;\xB9\xFFX\xD6\xB6\xC2\xCFIZ\xC8\xDA\x87\xCB,\x10\x11R\xC5\x9A\x9D\\\xA4\x8B\x7FCx\x1E.\xFF\x19\x0F\xDAb\x86\x8C\n$<\x8C\x0E#z\x02\xB6\x14\x99\x973\xBDn=\xEF\xA3\x14\xDF\xE9y\xE0N\xA5\x17\xF2_\x14E9\x87\x02\x03\x01\x00\x01\x02\x81\x81\x00\xA4\xA5\xFC\xC4\x1A\xAF'\xB5\xC8\xEC\xAC\xA9~\x8F\xF1\xF0\xC5B\xDE\xBCV\xFBW\x90\xD9\xA5J\"\x8E\x1A\xEC\xF2\x86\x8D\f\xF9ER\x15\xCB:\x93R\xF4\x99\xA0\xC4\xDB\x90\feH\x86\xCE\xE5\x87\xC9\xA59\xE5\xDE\xD1\\8\x1E\xB2@\xCCj@\xF5\xBF\xA6^\x8D\x8Bq&D\x97lD\xCEL\xB0\xF0Qd\xE7\xEB_\x13\x82v\xF5\xEE\xD55\x8EKT\xB5N\xBB\xC4\xAF\xF4\xF2/\xD7\xA1\xD9`\x9D\bl\xE9Z\aN\xE0\xA1\xDC{\xBB\xEC\xEE\x91\x02A\x00\xEF\xE81\xA8\e\x99G\x9E\xF6\xEF\x8F\xDA\x92%\xBFJ\xE5\xE6\xD1%\xCF\x12\xF8\xB2;S.I\x1C-#\xCE\xC1\xD3`\xF4\xAA\x05|\x0FU\aFn\x00\x84\xC9\xC9\xF0\xC0\xAC\xD7\xCD=\x90\xC4\x04&$\x14\n\xBA}\x99\x02A\x00\xD9m\xDAx\x01;^.\xB3\xA3g\x86\xE0,xP\xD3\a0\x87_\x05\x14\xBBV\n\x1E\x93\f\x9B\x1Ag\x1A\xA6\xA5th\x17\xB0\xE6A\xDEX\t\xB1?U\x94g\xF7`f\x1D\x90\xB4\xCDU\xBA @\xA9J\xA4\x1F\x02A\x00\xE2K\t\e\xE2\xFC\x90|7\xBC\xFC\xDAT\xE4\xDA-\xD1\xF2\b\xF2;\x03P&\xFE\xA2\x95\x94L\xC9\x9Fo\x15\x91GqA6\xEF0\x9F)\xBF\x9B_M\xE1\xF1c\xF0\xBA\x98\xCCu\xF8)\x8D(T\xFB\xBA\xAA\xF2q\x02A\x00\xC1\xAEl\x9C\xD7\xA4\x15\xCA\x8E4\xB2\x04\xE0n\\\xA2\xCA\xC8\xAD\xBE\xF8\xB2\xA2\xFC\x19\xB1\x9B\xF8\xAB\x93\x02\x9A\xF3\x8F\x9C\xF5+\xC0f\xD1E\xBD\x958\xD5\x0E0\xE2\xA9\x16 e\xF6R/\xECu?\xFEx\xFB\x14\xC5K\x02@cQq\x03\xF3w@\xF0\x97\n3\xE1\xE5\x8A\x85\xC4\x03\x10\xB3eUa7\xE0\xFA\xDD\f\x11\xC2Ct\xF3\x10\x0F\x92W\xEA\x0EG*q\xC5\x83i\x99\xB6\x85\xD0\xADi\x89J~\xE9\xF0bJ\xF3#S\xA2\x91\x04\xAC"
137+
pp OpenSSL::ASN1.decode(key.to_der) if $DEBUG
138+
assert_equal der, key.to_der
139+
140+
cipher = OpenSSL::Cipher.new("aes-128-cbc")
141+
exported = rsa1024.to_pem(cipher, "abcdef\0\1")
142+
assert_same_rsa rsa1024, OpenSSL::PKey::RSA.new(exported, "abcdef\0\1")
143+
assert_raise(OpenSSL::PKey::RSAError) {
144+
OpenSSL::PKey::RSA.new(exported, "abcdef")
145+
}
146+
end
147+
148+
def test_RSAPublicKey
149+
rsa1024 = Fixtures.pkey("rsa1024")
150+
151+
asn1 = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Integer(rsa1024.n), OpenSSL::ASN1::Integer(rsa1024.e) ])
152+
153+
key = OpenSSL::PKey::RSA.new(asn1.to_der)
154+
assert_not_predicate key, :private?
155+
n = 143085709396403084580358323862163416700436550432664688288860593156058579474547937626086626045206357324274536445865308750491138538454154232826011964045825759324933943290377903384882276841880081931690695505836279972214003660451338124170055999155993192881685495391496854691199517389593073052473319331505702779271
156+
assert_equal n, key.n
157+
assert_same_rsa dup_public(rsa1024), key
158+
159+
##
160+
der = "0\x81\x9F0\r\x06\t*\x86H\x86\xF7\r\x01\x01\x01\x05\x00\x03\x81\x8D\x000\x81\x89\x02\x81\x81\x00\xCB\xC2\xC4\xB0\xD4@\xA7>\xD4\xFE>C\xA0\x1E\x17\x06\x03\xBDg\xC0-\xBF\x9C\xBF9T\x11\xA7F\xA0\xF1:\xA8\xD5\x87\xB0\xB1h\xA3\xC4E\x81\xEC\x93\x80O\nA7n\xBBS\x84\xF5\x9C\xF6H\xC7\x11\x04;\xB9\xFFX\xD6\xB6\xC2\xCFIZ\xC8\xDA\x87\xCB,\x10\x11R\xC5\x9A\x9D\\\xA4\x8B\x7FCx\x1E.\xFF\x19\x0F\xDAb\x86\x8C\n$<\x8C\x0E#z\x02\xB6\x14\x99\x973\xBDn=\xEF\xA3\x14\xDF\xE9y\xE0N\xA5\x17\xF2_\x14E9\x87\x02\x03\x01\x00\x01"
161+
pp OpenSSL::ASN1.decode(key.to_der) if $DEBUG
162+
assert_equal der, key.to_der
163+
164+
pem = <<~EOF
165+
-----BEGIN RSA PUBLIC KEY-----
166+
MIGJAoGBAMvCxLDUQKc+1P4+Q6AeFwYDvWfALb+cvzlUEadGoPE6qNWHsLFoo8RF
167+
geyTgE8KQTduu1OE9Zz2SMcRBDu5/1jWtsLPSVrI2ofLLBARUsWanVyki39DeB4u
168+
/xkP2mKGjAokPIwOI3oCthSZlzO9bj3voxTf6XngTqUX8l8URTmHAgMBAAE=
169+
-----END RSA PUBLIC KEY-----
170+
EOF
171+
key = OpenSSL::PKey::RSA.new(pem)
172+
assert_not_predicate key, :private?
173+
assert_same_rsa dup_public(rsa1024), key
174+
175+
##
176+
assert_equal der, key.to_der
177+
178+
expected = "b48c0b2bbd35b906c5af4e46ed7355e4aaeadc99"
179+
assert_equal expected, OpenSSL::Digest::SHA1.hexdigest(key.to_der)
180+
end
181+
182+
private
183+
184+
def assert_same_rsa(expected, key)
185+
check_component(expected, key, [:n, :e, :d, :p, :q, :dmp1, :dmq1, :iqmp])
186+
end
187+
188+
def check_component(base, test, keys)
189+
keys.each { |comp| assert_equal base.send(comp), test.send(comp) }
190+
end
191+
192+
def dup_public(key)
193+
case key
194+
when OpenSSL::PKey::RSA
195+
rsa = OpenSSL::PKey::RSA.new
196+
rsa.set_key(key.n, key.e, nil)
197+
rsa
198+
else
199+
raise "unknown key type: #{key.class}"
200+
end
201+
end
202+
106203
end

src/test/ruby/test_helper.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,25 @@ def get_subject_key_id(cert)
212212
OpenSSL::Digest::SHA1.hexdigest(pkvalue).scan(/../).join(":").upcase
213213
end
214214

215+
module Fixtures
216+
module_function
217+
218+
def pkey(name)
219+
OpenSSL::PKey.read(read_file("pkey", name))
220+
end
221+
222+
def pkey_dh(name)
223+
# DH parameters can be read by OpenSSL::PKey.read atm
224+
OpenSSL::PKey::DH.new(read_file("pkey", name))
225+
end
226+
227+
@@__fixtures_cache = {}
228+
229+
def read_file(category, name)
230+
@@__fixtures_cache[ [category, name] ] ||=
231+
File.read(File.join(File.dirname(__FILE__), "fixtures", category, name))
232+
end
233+
end
215234
end
216235

217236
begin

0 commit comments

Comments
 (0)