Skip to content

Commit 7dbac3c

Browse files
committed
[compat] implement PKey::RSA public_to_der and public_to_pem
1 parent 888eb19 commit 7dbac3c

File tree

3 files changed

+136
-24
lines changed

3 files changed

+136
-24
lines changed

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

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
import static org.jruby.ext.openssl.impl.PKey.readRSAPublicKey;
7878
import static org.jruby.ext.openssl.impl.PKey.toASN1Primitive;
7979
import static org.jruby.ext.openssl.impl.PKey.toDerRSAKey;
80+
import static org.jruby.ext.openssl.impl.PKey.toDerRSAPublicKey;
8081

8182
/**
8283
* @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
@@ -356,6 +357,21 @@ public RubyBoolean private_p() {
356357
return getRuntime().newBoolean(isPrivateKey());
357358
}
358359

360+
@JRubyMethod(name = "public_to_der")
361+
public RubyString public_to_der(ThreadContext context) {
362+
final byte[] bytes;
363+
try {
364+
bytes = toDerRSAPublicKey(publicKey);
365+
}
366+
catch (NoClassDefFoundError e) {
367+
throw newRSAError(context.runtime, bcExceptionMessage(e));
368+
}
369+
catch (Exception e) {
370+
throw newRSAError(getRuntime(), e.getMessage(), e);
371+
}
372+
return StringHelper.newString(context.runtime, bytes);
373+
}
374+
359375
@Override
360376
@JRubyMethod(name = "to_der")
361377
public RubyString to_der() {
@@ -366,8 +382,8 @@ public RubyString to_der() {
366382
catch (NoClassDefFoundError e) {
367383
throw newRSAError(getRuntime(), bcExceptionMessage(e));
368384
}
369-
catch (IOException e) {
370-
throw newRSAError(getRuntime(), e.getMessage());
385+
catch (Exception e) {
386+
throw newRSAError(getRuntime(), e.getMessage(), e);
371387
}
372388
return StringHelper.newString(getRuntime(), bytes);
373389
}
@@ -470,6 +486,21 @@ public RubyString to_pem(ThreadContext context, final IRubyObject[] args) {
470486
}
471487
}
472488

489+
@JRubyMethod
490+
public RubyString public_to_pem(ThreadContext context) {
491+
try {
492+
final StringWriter writer = new StringWriter();
493+
PEMInputOutput.writeRSAPublicKey(writer, publicKey);
494+
return RubyString.newString(context.runtime, writer.getBuffer());
495+
}
496+
catch (NoClassDefFoundError ncdfe) {
497+
throw newRSAError(context.runtime, bcExceptionMessage(ncdfe));
498+
}
499+
catch (IOException ioe) {
500+
throw newRSAError(context.runtime, ioe.getMessage());
501+
}
502+
}
503+
473504
private String getPadding(final int padding) {
474505
if ( padding < 1 || padding > 4 ) {
475506
throw newRSAError(getRuntime(), "");
@@ -745,6 +776,7 @@ public IRubyObject set_key(final ThreadContext context, IRubyObject n, IRubyObje
745776
this.rsa_n = BN.getBigInteger(n);
746777
this.rsa_e = BN.getBigInteger(e);
747778
this.rsa_d = BN.getBigInteger(d);
779+
generatePublicKeyIfParams(context);
748780
generatePrivateKeyIfParams(context);
749781
return this;
750782
}
@@ -769,8 +801,6 @@ public IRubyObject set_crt_params(final ThreadContext context, IRubyObject dmp1,
769801
private void generatePublicKeyIfParams(final ThreadContext context) {
770802
final Ruby runtime = context.runtime;
771803

772-
if ( publicKey != null ) throw newRSAError(runtime, "illegal modification");
773-
774804
// Don't access the rsa_n and rsa_e fields directly. They may have
775805
// already been consumed and cleared by generatePrivateKeyIfParams.
776806
BigInteger _rsa_n = getModulus();

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

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ public static KeyPair readPrivateKey(final Type type, final PrivateKeyInfo keyIn
138138
}
139139

140140
// d2i_PUBKEY_bio
141-
public static PublicKey readPublicKey(byte[] input) throws IOException {
141+
public static PublicKey readPublicKey(final byte[] input) throws IOException {
142142
try (Reader in = new InputStreamReader(new ByteArrayInputStream(input))) {
143143
Object pemObject = new PEMParser(in).readObject();
144144
SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(pemObject);
@@ -292,10 +292,7 @@ public static KeyPair readECPrivateKey(final KeyFactory keyFactory, final Privat
292292

293293
public static byte[] toDerRSAKey(RSAPublicKey pubKey, RSAPrivateCrtKey privKey) throws IOException {
294294
if ( pubKey != null && privKey == null ) {
295-
// pubKey.getEncoded() :
296-
return KeyUtil.getEncodedSubjectPublicKeyInfo(
297-
new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), toASN1Primitive(pubKey)
298-
);
295+
return toDerRSAPublicKey(pubKey);
299296
}
300297
ASN1EncodableVector vec = new ASN1EncodableVector();
301298
vec.add(new ASN1Integer(BigInteger.ZERO));
@@ -310,6 +307,13 @@ public static byte[] toDerRSAKey(RSAPublicKey pubKey, RSAPrivateCrtKey privKey)
310307
return new DERSequence(vec).toASN1Primitive().getEncoded(ASN1Encoding.DER);
311308
}
312309

310+
public static byte[] toDerRSAPublicKey(final RSAPublicKey pubKey) throws IOException {
311+
// pubKey.getEncoded() :
312+
return KeyUtil.getEncodedSubjectPublicKeyInfo(
313+
new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), toASN1Primitive(pubKey)
314+
);
315+
}
316+
313317
public static ASN1Sequence toASN1Primitive(final RSAPublicKey publicKey) {
314318
assert publicKey != null : "null public key";
315319
ASN1EncodableVector vec = new ASN1EncodableVector();
@@ -320,20 +324,7 @@ public static ASN1Sequence toASN1Primitive(final RSAPublicKey publicKey) {
320324

321325
public static byte[] toDerDSAKey(DSAPublicKey pubKey, DSAPrivateKey privKey) throws IOException {
322326
if ( pubKey != null && privKey == null ) {
323-
// pubKey.getEncoded() :
324-
final DSAParams params = pubKey.getParams();
325-
if (params == null) {
326-
return new SubjectPublicKeyInfo(
327-
new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa),
328-
toASN1Primitive(pubKey)
329-
).getEncoded(ASN1Encoding.DER);
330-
}
331-
return new SubjectPublicKeyInfo(
332-
new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa,
333-
new DSAParameter(params.getP(), params.getQ(), params.getG())
334-
),
335-
toASN1Primitive(pubKey)
336-
).getEncoded(ASN1Encoding.DER);
327+
return toDerDSAPublicKey(pubKey);
337328
}
338329
if ( privKey != null && pubKey != null ) {
339330
ASN1EncodableVector vec = new ASN1EncodableVector();
@@ -357,6 +348,23 @@ public static byte[] toDerDSAKey(DSAPublicKey pubKey, DSAPrivateKey privKey) thr
357348
).getEncoded(ASN1Encoding.DER);
358349
}
359350

351+
public static byte[] toDerDSAPublicKey(final DSAPublicKey pubKey) throws IOException {
352+
// pubKey.getEncoded() :
353+
final DSAParams params = pubKey.getParams();
354+
if (params == null) {
355+
return new SubjectPublicKeyInfo(
356+
new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa),
357+
toASN1Primitive(pubKey)
358+
).getEncoded(ASN1Encoding.DER);
359+
}
360+
return new SubjectPublicKeyInfo(
361+
new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa,
362+
new DSAParameter(params.getP(), params.getQ(), params.getG())
363+
),
364+
toASN1Primitive(pubKey)
365+
).getEncoded(ASN1Encoding.DER);
366+
}
367+
360368
public static ASN1Primitive toASN1Primitive(DSAPublicKey pubKey) {
361369
return new ASN1Integer(pubKey.getY());
362370
}

src/test/ruby/rsa/test_rsa.rb

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ def test_RSAPrivateKey_encrypted
231231
}
232232
end
233233

234-
def test_RSAPublicKey
234+
def test_RSAPublicKey_custom
235235
rsa1024 = Fixtures.pkey("rsa1024")
236236

237237
asn1 = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Integer(rsa1024.n), OpenSSL::ASN1::Integer(rsa1024.e) ])
@@ -265,6 +265,80 @@ def test_RSAPublicKey
265265
assert_equal expected, OpenSSL::Digest::SHA1.hexdigest(key.to_der)
266266
end if !defined?(JRUBY_VERSION) || JRUBY_VERSION > '9.1' # set_key only since Ruby 2.3
267267

268+
def test_RSAPublicKey
269+
rsa1024 = Fixtures.pkey("rsa1024")
270+
rsa1024pub = OpenSSL::PKey::RSA.new(rsa1024.public_to_der)
271+
272+
asn1 = OpenSSL::ASN1::Sequence([
273+
OpenSSL::ASN1::Integer(rsa1024.n),
274+
OpenSSL::ASN1::Integer(rsa1024.e)
275+
])
276+
key = OpenSSL::PKey::RSA.new(asn1.to_der)
277+
assert_not_predicate key, :private?
278+
assert_same_rsa rsa1024pub, key
279+
280+
pem = <<~EOF
281+
-----BEGIN RSA PUBLIC KEY-----
282+
MIGJAoGBAMvCxLDUQKc+1P4+Q6AeFwYDvWfALb+cvzlUEadGoPE6qNWHsLFoo8RF
283+
geyTgE8KQTduu1OE9Zz2SMcRBDu5/1jWtsLPSVrI2ofLLBARUsWanVyki39DeB4u
284+
/xkP2mKGjAokPIwOI3oCthSZlzO9bj3voxTf6XngTqUX8l8URTmHAgMBAAE=
285+
-----END RSA PUBLIC KEY-----
286+
EOF
287+
key = OpenSSL::PKey::RSA.new(pem)
288+
assert_same_rsa rsa1024pub, key
289+
end
290+
291+
def test_export
292+
rsa1024 = Fixtures.pkey("rsa1024")
293+
294+
#pub = OpenSSL::PKey.read(rsa1024.public_to_der) # TODO not supported
295+
pub = OpenSSL::PKey::RSA.new(rsa1024.public_to_der)
296+
assert_not_equal rsa1024.export, pub.export
297+
assert_equal rsa1024.public_to_pem, pub.export
298+
299+
# PKey is immutable in OpenSSL >= 3.0
300+
#if !openssl?(3, 0, 0)
301+
key = OpenSSL::PKey::RSA.new
302+
303+
# key has only n, e and d
304+
key.set_key(rsa1024.n, rsa1024.e, rsa1024.d)
305+
assert_equal rsa1024.public_key.export, key.export
306+
307+
# key has only n, e, d, p and q
308+
key.set_factors(rsa1024.p, rsa1024.q)
309+
assert_equal rsa1024.public_key.export, key.export
310+
311+
# key has n, e, d, p, q, dmp1, dmq1 and iqmp
312+
key.set_crt_params(rsa1024.dmp1, rsa1024.dmq1, rsa1024.iqmp)
313+
#assert_equal rsa1024.export, key.export # TODO does not pass
314+
#end
315+
end
316+
317+
def test_to_der
318+
rsa1024 = Fixtures.pkey("rsa1024")
319+
320+
pub = OpenSSL::PKey::RSA.new(rsa1024.public_to_der)
321+
assert_not_equal rsa1024.to_der, pub.to_der
322+
assert_equal rsa1024.public_to_der, pub.to_der
323+
324+
# PKey is immutable in OpenSSL >= 3.0
325+
#if !openssl?(3, 0, 0)
326+
key = OpenSSL::PKey::RSA.new
327+
328+
# key has only n, e and d
329+
key.set_key(rsa1024.n, rsa1024.e, rsa1024.d)
330+
assert_equal rsa1024.public_key.to_der, key.to_der
331+
332+
# key has only n, e, d, p and q
333+
key.set_factors(rsa1024.p, rsa1024.q)
334+
assert_equal rsa1024.public_key.to_der, key.to_der
335+
336+
# key has n, e, d, p, q, dmp1, dmq1 and iqmp
337+
key.set_crt_params(rsa1024.dmp1, rsa1024.dmq1, rsa1024.iqmp)
338+
#assert_equal rsa1024.to_der, key.to_der # TODO does not pass
339+
#end
340+
end
341+
268342
def test_to_java
269343
key_file = File.join(File.dirname(__FILE__), 'private_key.pem')
270344
pkey = OpenSSL::PKey::RSA.new(File.read(key_file))

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy