From f9957e2b0fdd72ad4d73af63dedda28bd7640298 Mon Sep 17 00:00:00 2001 From: kares Date: Mon, 12 Feb 2024 14:18:04 +0100 Subject: [PATCH 1/7] [refactor] make sure curveName is set when using PKey.read --- src/main/java/org/jruby/ext/openssl/PKey.java | 2 +- .../java/org/jruby/ext/openssl/PKeyEC.java | 34 ++++++++++++------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/jruby/ext/openssl/PKey.java b/src/main/java/org/jruby/ext/openssl/PKey.java index c121f61e..af0442cf 100644 --- a/src/main/java/org/jruby/ext/openssl/PKey.java +++ b/src/main/java/org/jruby/ext/openssl/PKey.java @@ -132,7 +132,7 @@ public static IRubyObject read(final ThreadContext context, IRubyObject recv, IR } if ( "EC".equals(alg) ) { return new PKeyEC(runtime, _PKey(runtime).getClass("EC"), - (PrivateKey) keyPair.getPrivate(), (PublicKey) keyPair.getPublic() + keyPair.getPrivate(), keyPair.getPublic() ); } } diff --git a/src/main/java/org/jruby/ext/openssl/PKeyEC.java b/src/main/java/org/jruby/ext/openssl/PKeyEC.java index eb69e93d..638ee910 100644 --- a/src/main/java/org/jruby/ext/openssl/PKeyEC.java +++ b/src/main/java/org/jruby/ext/openssl/PKeyEC.java @@ -21,6 +21,7 @@ import java.security.PublicKey; import java.security.SecureRandom; +import java.security.SignatureException; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import java.security.spec.ECGenParameterSpec; @@ -69,11 +70,11 @@ import org.jruby.ext.openssl.impl.CipherSpec; import static org.jruby.ext.openssl.OpenSSL.debug; import static org.jruby.ext.openssl.OpenSSL.debugStackTrace; -import static org.jruby.ext.openssl.PKey._PKey; import org.jruby.ext.openssl.impl.ECPrivateKeyWithName; import static org.jruby.ext.openssl.impl.PKey.readECPrivateKey; import org.jruby.ext.openssl.util.ByteArrayOutputStream; import org.jruby.ext.openssl.x509store.PEMInputOutput; +import org.jruby.util.ByteList; /** * OpenSSL::PKey::EC implementation. @@ -200,8 +201,12 @@ public PKeyEC(Ruby runtime, RubyClass type) { PKeyEC(Ruby runtime, RubyClass type, PrivateKey privKey, PublicKey pubKey) { super(runtime, type); - this.privateKey = privKey; this.publicKey = (ECPublicKey) pubKey; + if (privKey instanceof ECPrivateKey) { + setPrivateKey((ECPrivateKey) privKey); + } else { + this.privateKey = privKey; + } } private transient Group group; @@ -213,9 +218,10 @@ public PKeyEC(Ruby runtime, RubyClass type) { private String getCurveName() { return curveName; } -// private ECNamedCurveParameterSpec getParameterSpec() { -// return ECNamedCurveTable.getParameterSpec( getCurveName() ); -// } + private ECNamedCurveParameterSpec getParameterSpec() { + assert curveName != null; + return ECNamedCurveTable.getParameterSpec(getCurveName()); + } @Override public PublicKey getPublicKey() { return publicKey; } @@ -342,12 +348,10 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a throw newECError(runtime, "Neither PUB key nor PRIV key: (invalid key type " + privKey.getClass().getName() + ")"); } this.publicKey = (ECPublicKey) pubKey; - this.privateKey = (ECPrivateKey) privKey; - unwrapPrivateKeyWithName(); + setPrivateKey((ECPrivateKey) privKey); } else if ( key instanceof ECPrivateKey ) { - this.privateKey = (ECPrivateKey) key; - unwrapPrivateKeyWithName(); + setPrivateKey((ECPrivateKey) key); } else if ( key instanceof ECPublicKey ) { this.publicKey = (ECPublicKey) key; this.privateKey = null; @@ -359,11 +363,15 @@ else if ( key instanceof ECPublicKey ) { if ( publicKey != null ) { publicKey.getParams().getCurve(); } - // TODO set curveName ?!?!?!?!?!?!?! return this; } + void setPrivateKey(final ECPrivateKey key) { + this.privateKey = key; + unwrapPrivateKeyWithName(); + } + private void unwrapPrivateKeyWithName() { final ECPrivateKey privKey = (ECPrivateKey) this.privateKey; if ( privKey instanceof ECPrivateKeyWithName ) { @@ -402,7 +410,7 @@ public PKeyEC generate_key(final ThreadContext context) { @JRubyMethod(name = "dsa_sign_asn1") public IRubyObject dsa_sign_asn1(final ThreadContext context, final IRubyObject data) { try { - ECNamedCurveParameterSpec params = ECNamedCurveTable.getParameterSpec(getCurveName()); + ECNamedCurveParameterSpec params = getParameterSpec(); ASN1ObjectIdentifier oid = getCurveOID(getCurveName()); ECNamedDomainParameters domainParams = new ECNamedDomainParameters(oid, params.getCurve(), params.getG(), params.getN(), params.getH(), params.getSeed() @@ -442,10 +450,10 @@ public IRubyObject dsa_sign_asn1(final ThreadContext context, final IRubyObject return StringHelper.newString(context.runtime, bytes.buffer(), bytes.size()); } catch (IOException ex) { - throw newECError(context.runtime, ex.toString()); + throw newECError(context.runtime, ex.getMessage()); } catch (RuntimeException ex) { - throw newECError(context.runtime, ex.toString()); + throw (RaiseException) newECError(context.runtime, ex.toString()).initCause(ex); } } From bdd1cedfb8cc0d4e0b1a8a4729db54ea62751e7e Mon Sep 17 00:00:00 2001 From: kares Date: Mon, 12 Feb 2024 14:53:49 +0100 Subject: [PATCH 2/7] [feat] implement support for PKey::EC.generate --- .../java/org/jruby/ext/openssl/PKeyEC.java | 21 +++++++++++++++++-- src/test/ruby/ec/test_ec.rb | 9 ++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/jruby/ext/openssl/PKeyEC.java b/src/main/java/org/jruby/ext/openssl/PKeyEC.java index 638ee910..45466e03 100644 --- a/src/main/java/org/jruby/ext/openssl/PKeyEC.java +++ b/src/main/java/org/jruby/ext/openssl/PKeyEC.java @@ -248,8 +248,7 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a IRubyObject arg = args[0]; if ( arg instanceof Group ) { - this.group = (Group) arg; - this.curveName = this.group.getCurveName(); + setGroup((Group) arg); return this; } @@ -380,6 +379,11 @@ private void unwrapPrivateKeyWithName() { } } + private void setGroup(final Group group) { + this.group = group; + this.curveName = this.group.getCurveName(); + } + //private static ECNamedCurveParameterSpec readECParameters(final byte[] input) throws IOException { // ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(input); // return ECNamedCurveTable.getParameterSpec(oid.getId()); @@ -407,6 +411,19 @@ public PKeyEC generate_key(final ThreadContext context) { return this; } + @JRubyMethod(meta = true) + public static IRubyObject generate(final ThreadContext context, final IRubyObject self, final IRubyObject group) { + PKeyEC randomKey = new PKeyEC(context.runtime, (RubyClass) self); + + if (group instanceof Group) { + randomKey.setGroup((Group) group); + } else { + randomKey.curveName = group.convertToString().toString(); + } + + return randomKey.generate_key(context); + } + @JRubyMethod(name = "dsa_sign_asn1") public IRubyObject dsa_sign_asn1(final ThreadContext context, final IRubyObject data) { try { diff --git a/src/test/ruby/ec/test_ec.rb b/src/test/ruby/ec/test_ec.rb index 2ac4a041..bb1b81cc 100644 --- a/src/test/ruby/ec/test_ec.rb +++ b/src/test/ruby/ec/test_ec.rb @@ -203,18 +203,13 @@ def setup super self.class.disable_security_restrictions! - # @data1 = 'foo'; @data2 = 'bar' * 1000 # data too long for DSA sig - @groups = []; @keys = [] OpenSSL::PKey::EC.builtin_curves.each do |curve, comment| next if curve.start_with?("Oakley") # Oakley curves are not suitable for ECDSA - group = OpenSSL::PKey::EC::Group.new(curve) - - key = OpenSSL::PKey::EC.new(group) - key.generate_key - @groups << group; @keys << key + @groups << group = OpenSSL::PKey::EC::Group.new(curve) + @keys << OpenSSL::PKey::EC.generate(group) end end From 281efbbeb0cf2276c3ed2639f7b3284580eb8e83 Mon Sep 17 00:00:00 2001 From: kares Date: Tue, 13 Feb 2024 14:30:26 +0100 Subject: [PATCH 3/7] [feat] implement missing PKey::EC#dsa_verify_asn1 --- .../java/org/jruby/ext/openssl/PKeyEC.java | 92 ++++++++++++------- src/test/ruby/ec/test_ec.rb | 47 +++++++--- src/test/ruby/fixtures/pkey/p256.pem | 5 + 3 files changed, 98 insertions(+), 46 deletions(-) create mode 100644 src/test/ruby/fixtures/pkey/p256.pem diff --git a/src/main/java/org/jruby/ext/openssl/PKeyEC.java b/src/main/java/org/jruby/ext/openssl/PKeyEC.java index 45466e03..a70398a8 100644 --- a/src/main/java/org/jruby/ext/openssl/PKeyEC.java +++ b/src/main/java/org/jruby/ext/openssl/PKeyEC.java @@ -16,6 +16,7 @@ import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; +import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; @@ -31,19 +32,26 @@ import java.security.spec.ECPublicKeySpec; import java.security.spec.EllipticCurve; import java.security.spec.InvalidKeySpecException; +import java.security.spec.InvalidParameterSpecException; import java.util.Collections; import java.util.Enumeration; import java.util.List; import javax.crypto.KeyAgreement; import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1OutputStream; -import org.bouncycastle.asn1.DLSequence; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERSequence; -import org.bouncycastle.crypto.params.ECNamedDomainParameters; +import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.crypto.signers.ECDSASigner; +import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util; import org.bouncycastle.jce.ECNamedCurveTable; import org.bouncycastle.jce.ECPointUtil; import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; @@ -110,10 +118,14 @@ static RubyClass _EC(final Ruby runtime) { return _PKey(runtime).getClass("EC"); } - public static RaiseException newECError(Ruby runtime, String message) { + private static RaiseException newECError(Ruby runtime, String message) { return Utils.newError(runtime, _PKey(runtime).getClass("ECError"), message); } + private static RaiseException newECError(Ruby runtime, String message, Exception cause) { + return Utils.newError(runtime, _PKey(runtime).getClass("ECError"), message, cause); + } + @JRubyMethod(meta = true) public static RubyArray builtin_curves(ThreadContext context, IRubyObject self) { final Ruby runtime = context.runtime; @@ -396,7 +408,6 @@ public IRubyObject check_key(final ThreadContext context) { @JRubyMethod(name = "generate_key") public PKeyEC generate_key(final ThreadContext context) { - // final ECDomainParameters params = getDomainParameters(); try { ECGenParameterSpec genSpec = new ECGenParameterSpec(getCurveName()); KeyPairGenerator gen = SecurityHelper.getKeyPairGenerator("EC"); // "BC" @@ -426,54 +437,69 @@ public static IRubyObject generate(final ThreadContext context, final IRubyObjec @JRubyMethod(name = "dsa_sign_asn1") public IRubyObject dsa_sign_asn1(final ThreadContext context, final IRubyObject data) { + if (privateKey == null) { + throw newECError(context.runtime, "Private EC key needed!"); + } try { - ECNamedCurveParameterSpec params = getParameterSpec(); - ASN1ObjectIdentifier oid = getCurveOID(getCurveName()); - ECNamedDomainParameters domainParams = new ECNamedDomainParameters(oid, - params.getCurve(), params.getG(), params.getN(), params.getH(), params.getSeed() - ); + final ECNamedCurveParameterSpec params = getParameterSpec(); final ECDSASigner signer = new ECDSASigner(); - final ECPrivateKey privKey = (ECPrivateKey) this.privateKey; - signer.init(true, new ECPrivateKeyParameters(privKey.getS(), domainParams)); + signer.init(true, new ECPrivateKeyParameters( + ((ECPrivateKey) this.privateKey).getS(), + new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH()) + )); - final byte[] message = data.convertToString().getBytes(); - BigInteger[] signature = signer.generateSignature(message); // [r, s] - -// final byte[] r = signature[0].toByteArray(); -// final byte[] s = signature[1].toByteArray(); -// // ASN.1 encode as: 0x30 len 0x02 rlen (r) 0x02 slen (s) -// final int len = 1 + (1 + r.length) + 1 + (1 + s.length); -// -// final byte[] encoded = new byte[1 + 1 + len]; int i; -// encoded[0] = 0x30; -// encoded[1] = (byte) len; -// encoded[2] = 0x20; -// encoded[3] = (byte) r.length; -// System.arraycopy(r, 0, encoded, i = 4, r.length); i += r.length; -// encoded[i++] = 0x20; -// encoded[i++] = (byte) s.length; -// System.arraycopy(s, 0, encoded, i, s.length); + BigInteger[] signature = signer.generateSignature(data.convertToString().getBytes()); // [r, s] ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - ASN1OutputStream asn1 = ASN1OutputStream.create(bytes); + ASN1OutputStream asn1 = ASN1OutputStream.create(bytes, ASN1Encoding.DER); - ASN1EncodableVector v = new ASN1EncodableVector(); + ASN1EncodableVector v = new ASN1EncodableVector(2); v.add(new ASN1Integer(signature[0])); // r v.add(new ASN1Integer(signature[1])); // s - asn1.writeObject(new DLSequence(v)); + asn1.writeObject(new DERSequence(v)); + asn1.close(); return StringHelper.newString(context.runtime, bytes.buffer(), bytes.size()); } catch (IOException ex) { throw newECError(context.runtime, ex.getMessage()); } - catch (RuntimeException ex) { - throw (RaiseException) newECError(context.runtime, ex.toString()).initCause(ex); + catch (Exception ex) { + throw newECError(context.runtime, ex.toString(), ex); } } + @JRubyMethod(name = "dsa_verify_asn1") + public IRubyObject dsa_verify_asn1(final ThreadContext context, final IRubyObject data, final IRubyObject sign) { + final Ruby runtime = context.runtime; + try { + final ECNamedCurveParameterSpec params = getParameterSpec(); + + final ECDSASigner signer = new ECDSASigner(); + signer.init(false, new ECPublicKeyParameters( + EC5Util.convertPoint(publicKey.getParams(), publicKey.getW()), + new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH()) + )); + + ASN1Primitive vec = new ASN1InputStream(sign.convertToString().getBytes()).readObject(); + + if (!(vec instanceof ASN1Sequence)) { + throw newECError(runtime, "invalid signature (not a sequence)"); + } + + ASN1Sequence seq = (ASN1Sequence) vec; + ASN1Integer r = ASN1Integer.getInstance(seq.getObjectAt(0)); + ASN1Integer s = ASN1Integer.getInstance(seq.getObjectAt(1)); + + boolean verify = signer.verifySignature(data.convertToString().getBytes(), r.getPositiveValue(), s.getPositiveValue()); + return runtime.newBoolean(verify); + } + catch (IOException|IllegalArgumentException|IllegalStateException ex) { + throw newECError(runtime, "invalid signature: " + ex.getMessage(), ex); + } + } @JRubyMethod(name = "dh_compute_key") public IRubyObject dh_compute_key(final ThreadContext context, final IRubyObject point) { diff --git a/src/test/ruby/ec/test_ec.rb b/src/test/ruby/ec/test_ec.rb index bb1b81cc..0cd685e2 100644 --- a/src/test/ruby/ec/test_ec.rb +++ b/src/test/ruby/ec/test_ec.rb @@ -237,15 +237,18 @@ def test_check_key end def test_sign_verify - key_file = File.join(File.dirname(__FILE__), 'private_key.pem') - - key = OpenSSL::PKey::EC.new(File.read(key_file)) - data = 'abcd' - digest = OpenSSL::Digest::SHA256.new - sig = key.sign(digest, data) - assert_true key.verify(digest, sig, data) - - key.sign(OpenSSL::Digest::SHA1.new, data) + p256 = Fixtures.pkey("p256.pem") + data = "Sign me!" + signature = p256.sign("SHA1", data) + assert_equal true, p256.verify("SHA1", signature, data) + + signature0 = (<<~'end;').unpack("m")[0] + MEQCIEOTY/hD7eI8a0qlzxkIt8LLZ8uwiaSfVbjX2dPAvN11AiAQdCYx56Fq + QdBp1B4sxJoA8jvODMMklMyBKVmudboA6A== + end; + assert_equal true, p256.verify("SHA256", signature0, data) + signature1 = signature0.succ + assert_equal false, p256.verify("SHA256", signature1, data) end def test_group_encoding @@ -293,13 +296,31 @@ def test_set_keys end end if false # NOT-IMPLEMENTED TODO - def test_dsa_sign_verify - data1 = 'foo' + def test_dsa_sign_verify_all + data1 = 'hashed-value' for key in @keys + next if key.group.curve_name == 'SM2' + sig = key.dsa_sign_asn1(data1) - assert(key.dsa_verify_asn1(data1, sig)) + assert_equal(true, key.dsa_verify_asn1(data1, sig)) + assert_equal(false, key.dsa_verify_asn1(data1 + 'X', sig)) end - end if false # NOT-IMPLEMENTED + end + + def test_sign_verify_raw + key = Fixtures.pkey("p256.pem") + data1 = "foo" + data2 = "bar" + + malformed_sig = "*" * 30 + + # Sign by #dsa_sign_asn1 + sig = key.dsa_sign_asn1(data1) + + assert_equal true, key.dsa_verify_asn1(data1, sig) + assert_equal false, key.dsa_verify_asn1(data2, sig) + assert_raise(OpenSSL::PKey::ECError) { key.dsa_verify_asn1(data1, malformed_sig) } + end # def test_dh_compute_key # for key in @keys diff --git a/src/test/ruby/fixtures/pkey/p256.pem b/src/test/ruby/fixtures/pkey/p256.pem new file mode 100644 index 00000000..4cdb0a30 --- /dev/null +++ b/src/test/ruby/fixtures/pkey/p256.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIID49FDqcf1O1eO8saTgG70UbXQw9Fqwseliit2aWhH1oAoGCCqGSM49 +AwEHoUQDQgAEFglk2c+oVUIKQ64eZG9bhLNPWB7lSZ/ArK41eGy5wAzU/0G51Xtt +CeBUl+MahZtn9fO1JKdF4qJmS39dXnpENg== +-----END EC PRIVATE KEY----- \ No newline at end of file From 821e8e051add6cea35473d31ceb23b59df699032 Mon Sep 17 00:00:00 2001 From: kares Date: Tue, 13 Feb 2024 14:32:09 +0100 Subject: [PATCH 4/7] [feat] try resolving curve-name from EC public key --- .../java/org/jruby/ext/openssl/PKeyEC.java | 48 +++++++++++++------ 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/jruby/ext/openssl/PKeyEC.java b/src/main/java/org/jruby/ext/openssl/PKeyEC.java index a70398a8..fb051fb0 100644 --- a/src/main/java/org/jruby/ext/openssl/PKeyEC.java +++ b/src/main/java/org/jruby/ext/openssl/PKeyEC.java @@ -11,6 +11,7 @@ import java.io.StringReader; import java.io.StringWriter; import java.math.BigInteger; +import java.security.AlgorithmParameters; import java.security.GeneralSecurityException; import java.security.InvalidKeyException; import java.security.KeyFactory; @@ -36,6 +37,7 @@ import java.util.Collections; import java.util.Enumeration; import java.util.List; +import java.util.Optional; import javax.crypto.KeyAgreement; import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1Encoding; @@ -82,7 +84,6 @@ import static org.jruby.ext.openssl.impl.PKey.readECPrivateKey; import org.jruby.ext.openssl.util.ByteArrayOutputStream; import org.jruby.ext.openssl.x509store.PEMInputOutput; -import org.jruby.util.ByteList; /** * OpenSSL::PKey::EC implementation. @@ -170,24 +171,21 @@ public static RubyArray builtin_curves(ThreadContext context, IRubyObject self) return curves; } - private static ASN1ObjectIdentifier getCurveOID(final String curveName) { + private static Optional getCurveOID(final String curveName) { ASN1ObjectIdentifier id; id = org.bouncycastle.asn1.sec.SECNamedCurves.getOID(curveName); - if ( id != null ) return id; + if ( id != null ) return Optional.of(id); id = org.bouncycastle.asn1.x9.X962NamedCurves.getOID(curveName); - if ( id != null ) return id; + if ( id != null ) return Optional.of(id); id = org.bouncycastle.asn1.nist.NISTNamedCurves.getOID(curveName); - if ( id != null ) return id; + if ( id != null ) return Optional.of(id); id = org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves.getOID(curveName); - if ( id != null ) return id; - throw new IllegalStateException("could not identify curve name: " + curveName); + if ( id != null ) return Optional.of(id); + return Optional.empty(); } private static boolean isCurveName(final String curveName) { - try { - return getCurveOID(curveName) != null; - } - catch (IllegalStateException ex) { return false; } + return getCurveOID(curveName).isPresent(); } private static String getCurveName(final ASN1ObjectIdentifier oid) { @@ -365,14 +363,18 @@ else if ( key instanceof ECPrivateKey ) { setPrivateKey((ECPrivateKey) key); } else if ( key instanceof ECPublicKey ) { - this.publicKey = (ECPublicKey) key; this.privateKey = null; + this.publicKey = (ECPublicKey) key; + this.privateKey = null; } else { throw newECError(runtime, "Neither PUB key nor PRIV key: " + key.getClass().getName()); } - if ( publicKey != null ) { - publicKey.getParams().getCurve(); + if ( curveName == null && publicKey != null ) { + final String oid = getCurveNameObjectIdFromKey(context, publicKey); + if (isCurveName(oid)) { + this.curveName = getCurveName(new ASN1ObjectIdentifier(oid)); + } } return this; @@ -391,6 +393,20 @@ private void unwrapPrivateKeyWithName() { } } + private static String getCurveNameObjectIdFromKey(final ThreadContext context, final ECPublicKey key) { + try { + AlgorithmParameters algParams = AlgorithmParameters.getInstance("EC"); + algParams.init(key.getParams()); + return algParams.getParameterSpec(ECGenParameterSpec.class).getName(); + } + catch (NoSuchAlgorithmException|InvalidParameterSpecException ex) { + throw newECError(context.runtime, ex.getMessage()); + } + catch (Exception ex) { + throw (RaiseException) newECError(context.runtime, ex.toString()).initCause(ex); + } + } + private void setGroup(final Group group) { this.group = group; this.curveName = this.group.getCurveName(); @@ -806,7 +822,9 @@ public RubyString to_pem(final ThreadContext context, final IRubyObject[] args) try { final StringWriter writer = new StringWriter(); - PEMInputOutput.writeECParameters(writer, getCurveOID(getCurveName()), spec, passwd); + final ASN1ObjectIdentifier oid = getCurveOID(getCurveName()) + .orElseThrow(() -> newECError(context.runtime, "invalid curve name: " + getCurveName())); + PEMInputOutput.writeECParameters(writer, oid, spec, passwd); return RubyString.newString(context.runtime, writer.getBuffer()); } catch (IOException ex) { From 1aa85d012811d09a72a0798240fc487848c84715 Mon Sep 17 00:00:00 2001 From: kares Date: Tue, 13 Feb 2024 14:32:41 +0100 Subject: [PATCH 5/7] [refactor] re-use SecureRandom + house-keeping --- src/main/java/org/jruby/ext/openssl/PKeyEC.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/jruby/ext/openssl/PKeyEC.java b/src/main/java/org/jruby/ext/openssl/PKeyEC.java index fb051fb0..3352d095 100644 --- a/src/main/java/org/jruby/ext/openssl/PKeyEC.java +++ b/src/main/java/org/jruby/ext/openssl/PKeyEC.java @@ -17,13 +17,10 @@ import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; -import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; -import java.security.SecureRandom; -import java.security.SignatureException; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import java.security.spec.ECGenParameterSpec; @@ -427,7 +424,7 @@ public PKeyEC generate_key(final ThreadContext context) { try { ECGenParameterSpec genSpec = new ECGenParameterSpec(getCurveName()); KeyPairGenerator gen = SecurityHelper.getKeyPairGenerator("EC"); // "BC" - gen.initialize(genSpec, new SecureRandom()); + gen.initialize(genSpec, OpenSSL.getSecureRandom(context)); KeyPair pair = gen.generateKeyPair(); this.publicKey = (ECPublicKey) pair.getPublic(); this.privateKey = pair.getPrivate(); @@ -537,11 +534,8 @@ public IRubyObject dh_compute_key(final ThreadContext context, final IRubyObject final byte[] secret = agreement.generateSecret(); return StringHelper.newString(context.runtime, secret); } - catch (NoSuchAlgorithmException ex) { - throw newECError(context.runtime, ex.toString()); - } catch (InvalidKeyException ex) { - throw newECError(context.runtime, ex.toString()); + throw newECError(context.runtime, "invalid key: " + ex.getMessage()); } catch (GeneralSecurityException ex) { throw newECError(context.runtime, ex.toString()); From 06f3e2915120f8cb9c921a4eae987f5d16000a9b Mon Sep 17 00:00:00 2001 From: kares Date: Tue, 13 Feb 2024 15:50:00 +0100 Subject: [PATCH 6/7] [build] adjust Gemfile for running (tests) on MRI --- Gemfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 5960c9f7..ed1cc833 100644 --- a/Gemfile +++ b/Gemfile @@ -1,12 +1,12 @@ source 'https://rubygems.org' # Specify your gem's dependencies in the gemspec -gemspec +gemspec if defined? JRUBY_VERSION gem "rake", require: false # for less surprises with newer releases -gem 'jar-dependencies', '>= 0.3.11', require: false +gem 'jar-dependencies', '>= 0.3.11', platform: :jruby, require: false gem 'mocha', '~> 1.4', '< 2.0' From 1b728a919ece3352f58c9c7d73878940cdc7abcc Mon Sep 17 00:00:00 2001 From: kares Date: Tue, 13 Feb 2024 15:54:33 +0100 Subject: [PATCH 7/7] [test] cleanup - no longer tested on Java 7 --- src/test/ruby/test_helper.rb | 7 +------ src/test/ruby/x509/test_x509cert.rb | 9 ++++----- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/test/ruby/test_helper.rb b/src/test/ruby/test_helper.rb index 1b81d67a..b13f3cb1 100644 --- a/src/test/ruby/test_helper.rb +++ b/src/test/ruby/test_helper.rb @@ -101,7 +101,7 @@ def assert_not_same(expected, actual) end end - def self.disable_security_restrictions!; @@security_restrictions = nil end # do nothing on MRI + def self.disable_security_restrictions!; end # do nothing on MRI @@security_restrictions = '' @@ -121,11 +121,6 @@ def self.disable_security_restrictions disable_security_restrictions! if @@security_restrictions.eql?('') end - def self.security_restrictions? - disable_security_restrictions; return @@security_restrictions - end - - def self.java7?; java_version.last.to_i == 7 end def self.java8?; java_version.last.to_i == 8 end def self.java_version diff --git a/src/test/ruby/x509/test_x509cert.rb b/src/test/ruby/x509/test_x509cert.rb index d2429b0d..1528df8b 100644 --- a/src/test/ruby/x509/test_x509cert.rb +++ b/src/test/ruby/x509/test_x509cert.rb @@ -141,11 +141,10 @@ def test_resolve_extensions assert_equal 5, cert.extensions.size # Java 6/7 seems to maintain same order but Java 8 does definitely not : - # TODO there must be something going on under - maybe not BC parsing ?!? - if self.class.java7? - assert_equal '97:39:9D:C3:FB:CD:BA:8F:54:0C:90:7B:46:3F:EA:D6:43:75:B1:CB', cert.extensions[2].value - assert_equal 'email:self@jruby.org, DNS:jruby.org', cert.extensions[4].value - end + #if self.class.java_version.last.to_i == 7 + # assert_equal '97:39:9D:C3:FB:CD:BA:8F:54:0C:90:7B:46:3F:EA:D6:43:75:B1:CB', cert.extensions[2].value + # assert_equal 'email:self@jruby.org, DNS:jruby.org', cert.extensions[4].value + #end exts = cert.extensions.dup