From 97fccf91572431778a1a9ea3ce16bf03fa266434 Mon Sep 17 00:00:00 2001 From: kares Date: Thu, 1 Oct 2026 13:23:01 +0200 Subject: [PATCH 1/2] [refactor] review user array conversions --- .../org/jruby/ext/openssl/SSLContext.java | 6 +- .../ext/openssl/X509ExtensionFactory.java | 72 +++++++++++-------- 2 files changed, 46 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/jruby/ext/openssl/SSLContext.java b/src/main/java/org/jruby/ext/openssl/SSLContext.java index 210832e2..18d78430 100644 --- a/src/main/java/org/jruby/ext/openssl/SSLContext.java +++ b/src/main/java/org/jruby/ext/openssl/SSLContext.java @@ -458,10 +458,10 @@ synchronized IRubyObject doSetup(final ThreadContext context) { value = getInstanceVariable("@alpn_protocols"); if ( value != null && ! value.isNil() ) { - IRubyObject[] alpn_protocols = ((RubyArray) value).toJavaArrayMaybeUnsafe(); - String[] protocols = new String[alpn_protocols.length]; + final RubyArray alpnProtocolsAry = value.convertToArray(); + final String[] protocols = new String[alpnProtocolsAry.size()]; for(int i = 0; i < protocols.length; i++) { - protocols[i] = alpn_protocols[i].convertToString().asJavaString(); + protocols[i] = alpnProtocolsAry.eltInternal(i).convertToString().asJavaString(); } alpnProtocols = protocols; } else { diff --git a/src/main/java/org/jruby/ext/openssl/X509ExtensionFactory.java b/src/main/java/org/jruby/ext/openssl/X509ExtensionFactory.java index 4a1e1f19..25539b90 100644 --- a/src/main/java/org/jruby/ext/openssl/X509ExtensionFactory.java +++ b/src/main/java/org/jruby/ext/openssl/X509ExtensionFactory.java @@ -161,7 +161,7 @@ public IRubyObject set_config(IRubyObject arg) { } @JRubyMethod(rest = true) - public IRubyObject create_ext(final ThreadContext context, final IRubyObject[] args) { + public IRubyObject create_ext(final ThreadContext context, final IRubyObject... args) { final Ruby runtime = context.runtime; IRubyObject critical; if (Arity.checkArgumentCount(runtime, args, 2, 3) == 3 && !args[2].isNil()) { @@ -169,67 +169,76 @@ public IRubyObject create_ext(final ThreadContext context, final IRubyObject[] a } else { critical = runtime.getFalse(); } - final String oid = args[0].toString(); - String valuex = args[1].toString(); + return create_ext(context, args[0], args[1], critical); + } + + private X509Extension create_ext(final ThreadContext context, + final IRubyObject oid, + final IRubyObject value, + IRubyObject critical) { + final Ruby runtime = context.runtime; + final String oidString = oid.toString(); + String valuex = value.toString(); final ASN1ObjectIdentifier objectId; try { - objectId = ASN1.getObjectID(runtime, oid); + objectId = ASN1.getObjectID(runtime, oidString); } catch (IllegalArgumentException e) { LOG.debug(runtime, "create_ext getObjectID failed", e); - throw newExtensionError(runtime, "unknown OID `" + oid + "'"); + throw newExtensionError(runtime, "unknown OID `" + oidString + "'"); } final String critical_ = "critical,"; if ( valuex.startsWith(critical_) ) { - critical = runtime.getTrue(); + critical = context.tru; valuex = valuex.substring(critical_.length()).trim(); } - final ASN1Encodable value; + + final ASN1Encodable val; try { final String id = objectId.getId(); if (valuex.startsWith("ASN1:")) { - value = parseASN1ConfigValue(context, valuex); + val = parseASN1ConfigValue(context, valuex); } else if (id.equals("2.5.29.14")) { // subjectKeyIdentifier - value = new DEROctetString(parseSubjectKeyIdentifier(context, oid, valuex)); + val = new DEROctetString(parseSubjectKeyIdentifier(context, oidString, valuex)); } else if (id.equals("2.5.29.35")) { // authorityKeyIdentifier - value = parseAuthorityKeyIdentifier(context, valuex); + val = parseAuthorityKeyIdentifier(context, valuex); } else if (id.equals("2.5.29.17")) { // subjectAltName - value = parseSubjectAltName(valuex); + val = parseSubjectAltName(valuex); } else if (id.equals("2.5.29.18")) { // issuerAltName - value = parseIssuerAltName(context, valuex); + val = parseIssuerAltName(context, valuex); } else if (id.equals("2.5.29.19")) { // basicConstraints - value = parseBasicConstrains(valuex); + val = parseBasicConstrains(valuex); } else if (id.equals("2.5.29.15")) { // keyUsage - value = parseKeyUsage(oid, valuex); + val = parseKeyUsage(oidString, valuex); } else if (id.equals("2.16.840.1.113730.1.1")) { // nsCertType - value = parseNsCertType(oid, valuex); + val = parseNsCertType(oidString, valuex); } else if (id.equals("2.5.29.37")) { // extendedKeyUsage - value = parseExtendedKeyUsage(runtime, oid, valuex, critical.isTrue()); + val = parseExtendedKeyUsage(runtime, oidString, valuex, critical.isTrue()); } else if (id.equals("2.5.29.31")) { // crlDistributionPoints - value = parseCRLDistributionPoints(context, valuex); + val = parseCRLDistributionPoints(context, valuex); } else if (id.equals("1.3.6.1.5.5.7.1.1")) { // authorityInfoAccess - value = parseAuthorityInfoAccess(valuex); + val = parseAuthorityInfoAccess(valuex); } else if (isNetscapeIA5StringExtension(id)) { - value = new DEROctetString(new DERIA5String(valuex).getEncoded(ASN1Encoding.DER)); + val = new DEROctetString(new DERIA5String(valuex).getEncoded(ASN1Encoding.DER)); } else if (id.equals("2.5.29.30")) { // nameConstraints - value = parseNameConstraints(valuex); + val = parseNameConstraints(valuex); } else if (id.equals("2.5.29.32")) { // certificatePolicies - value = parseCertificatePolicies(context, valuex); + val = parseCertificatePolicies(context, valuex); } else { - value = new DEROctetString(new DEROctetString(ByteList.plain(valuex)).getEncoded(ASN1Encoding.DER)); + val = new DEROctetString(new DEROctetString(ByteList.plain(valuex)).getEncoded(ASN1Encoding.DER)); } } catch (IOException e) { @@ -237,10 +246,10 @@ else if (id.equals("2.5.29.32")) { // certificatePolicies throw newExtensionError(runtime, "Unable to create extension: " + e.getMessage()); } catch (IllegalArgumentException e) { - throw newExtensionError(runtime, oid + " = " + valuex + - ": error in extension (name=" + oid + ", value=" + valuex + ")"); + throw newExtensionError(runtime, oidString + " = " + valuex + + ": error in extension (name=" + oidString + ", value=" + valuex + ")"); } - return newExtension(runtime, objectId, value, critical.isNil() ? null : critical.isTrue()); + return newExtension(runtime, objectId, val, critical.isNil() ? null : critical.isTrue()); } private ASN1Encodable parseASN1ConfigValue(final ThreadContext context, final String value) @@ -355,8 +364,13 @@ public IRubyObject create_extension(final ThreadContext context, final IRubyObje @JRubyMethod public IRubyObject create_ext_from_array(final ThreadContext context, final IRubyObject arg) { final RubyArray ary = (RubyArray) arg; - if ( ary.size() > 3 ) throw newExtensionError(context.runtime, "unexpected array form"); - return create_ext(context, ary.toJavaArrayUnsafe()); + final int size = ary.size(); + if ( size > 3 ) throw newExtensionError(context.runtime, "unexpected array form"); + if ( size < 2 ) { // fail with appropriate ArgumentError + return size == 1 ? create_ext(context, ary.eltInternal(0)) : create_ext(context); + } + final IRubyObject critical = size == 3 ? ary.eltInternal(2) : context.fals; + return create_ext(context, ary.eltInternal(0), ary.eltInternal(1), critical); } @JRubyMethod @@ -366,7 +380,7 @@ public IRubyObject create_ext_from_hash(final ThreadContext context, final IRuby final IRubyObject oid = hash.op_aref(context, newStringFrozen(runtime, "oid")); final IRubyObject value = hash.op_aref(context, newStringFrozen(runtime, "value")); final IRubyObject critical = hash.op_aref(context, newStringFrozen(runtime, "critical")); - return create_ext(context, new IRubyObject[]{oid, value, critical}); + return create_ext(context, oid, value, critical); } // "oid = critical, value" @@ -387,7 +401,7 @@ public IRubyObject create_ext_from_string(final ThreadContext context, final IRu value.op_aset19(context, runtime.newFixnum(0), runtime.newFixnum(critical__.length), RubyString.newEmptyString(runtime)); } value.strip_bang19(context); - return create_ext(context, new IRubyObject[]{oid, value, critical}); + return create_ext(context, oid, value, critical); } private DERBitString parseKeyUsage(final String oid, final String valuex) { From 27f9a8f46250b35c70fddecd09a50939e04ab4b3 Mon Sep 17 00:00:00 2001 From: kares Date: Thu, 1 Oct 2026 14:21:58 +0200 Subject: [PATCH 2/2] [compat] copy X509 (internal) collection arguments --- .../java/org/jruby/ext/openssl/X509CRL.java | 43 ++++++++++------- .../org/jruby/ext/openssl/X509Extension.java | 25 ++++++++++ .../org/jruby/ext/openssl/X509Revoked.java | 47 ++++++++++++++++--- .../jruby/ext/openssl/util/RubySupport.java | 8 ++++ test/x509/test_x509crl.rb | 44 +++++++++++++++++ test/x509/test_x509ext.rb | 9 ++++ test/x509/test_x509revoked.rb | 26 ++++++++++ 7 files changed, 177 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/jruby/ext/openssl/X509CRL.java b/src/main/java/org/jruby/ext/openssl/X509CRL.java index 21ca97f3..f86d12ef 100644 --- a/src/main/java/org/jruby/ext/openssl/X509CRL.java +++ b/src/main/java/org/jruby/ext/openssl/X509CRL.java @@ -80,6 +80,7 @@ import org.jruby.exceptions.RaiseException; import org.jruby.ext.openssl.log.Logger; import org.jruby.ext.openssl.shim.ASN1Shim; +import org.jruby.ext.openssl.util.RubySupport; import org.jruby.ext.openssl.x509store.PEMInputOutput; import org.jruby.runtime.Arity; import org.jruby.runtime.Block; @@ -88,7 +89,6 @@ import org.jruby.runtime.builtin.Variable; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.util.ByteList; -import org.jruby.util.TypeConverter; import static org.jruby.ext.openssl.OpenSSL.*; import static org.jruby.ext.openssl.X509._X509; @@ -250,7 +250,7 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a final X509CRLEntry[] revokedSorted = revokedCRLs.toArray(new X509CRLEntry[revokedCRLs.size()]); Arrays.sort(revokedSorted, 0, revokedSorted.length, Comparator.comparing(X509CRLEntry::getRevocationDate)); for (X509CRLEntry entry : revokedSorted) { - revoked().append( X509Revoked.newInstance(context, entry) ); + revokedInternal().append( X509Revoked.newInstance(context, entry) ); } } @@ -544,44 +544,56 @@ public IRubyObject set_next_update(final ThreadContext context, IRubyObject val) } @JRubyMethod - public RubyArray revoked() { + public RubyArray revoked(ThreadContext context) { + return revoked == null ? context.runtime.newEmptyArray() : RubySupport.copyArray(context.runtime, revoked); + } + + private RubyArray revokedInternal() { return revoked == null ? revoked = getRuntime().newArray(4) : revoked; } @JRubyMethod(name="revoked=") public IRubyObject set_revoked(final IRubyObject revoked) { if (!(revoked instanceof RubyArray)) throw getRuntime().newTypeError(revoked, getRuntime().getArray()); + this.revoked = X509Revoked.copyRevoked(getRuntime(), (RubyArray) revoked); this.changed = true; - return this.revoked = (RubyArray) revoked; + return revoked; } @JRubyMethod public IRubyObject add_revoked(final ThreadContext context, IRubyObject val) { - final X509Revoked revoked = asRevoked(context.runtime, val); + final X509Revoked revoked = X509Revoked.asRevoked(context.runtime, val); if (revoked.getTime() == null) throw X509Revoked.newRevokedError(context.runtime, "revocation time not set"); + revokedInternal().append(X509Revoked.copy(context.runtime, revoked)); this.changed = true; - revoked().callMethod(context, "<<", val); return val; + return val; } @JRubyMethod - public RubyArray extensions() { - return this.extensions; + public RubyArray extensions(ThreadContext context) { + return extensions == null ? context.runtime.newEmptyArray() : RubySupport.copyArray(context.runtime, extensions); + } + + private RubyArray extensionsInternal() { + return extensions == null ? extensions = getRuntime().newArray(4) : extensions; } @SuppressWarnings("unchecked") @JRubyMethod(name="extensions=") public IRubyObject set_extensions(final IRubyObject extensions) { if (!(extensions instanceof RubyArray)) throw getRuntime().newTypeError(extensions, getRuntime().getArray()); + this.extensions = X509Extension.copyExtensions(getRuntime(), (RubyArray) extensions); this.changed = true; - return this.extensions = (RubyArray) extensions; + return extensions; } @JRubyMethod public IRubyObject add_extension(final IRubyObject extension) { if (!(extension instanceof X509Extension)) throw getRuntime().newTypeError( extension, X509Extension._Extension(getRuntime())); + extensionsInternal().append(X509Extension.copy(getRuntime(), (X509Extension) extension)); this.changed = true; - extensions().append(extension); return extension; + return extension; } @JRubyMethod @@ -598,13 +610,13 @@ public IRubyObject sign(final ThreadContext context, final IRubyObject key, IRub if ( revoked != null ) { for ( int i = 0; i < revoked.size(); i++ ) { - final X509Revoked rev = asRevoked(runtime, revoked.eltInternal(i)); + final X509Revoked rev = X509Revoked.asRevoked(runtime, revoked.eltInternal(i)); final DateTime revTime = rev.getTime(); if (revTime == null) throw X509Revoked.newRevokedError(runtime, "revocation time not set"); final Extensions revExts; if ( rev.hasExtensions() ) { - final RubyArray exts = rev.extensions(); + final RubyArray exts = rev.extensions(context); final ASN1Encodable[] array = new ASN1Encodable[ exts.size() ]; for ( int j = 0; j < exts.size(); j++ ) { final X509Extension ext = (X509Extension) exts.entry(j); @@ -754,7 +766,7 @@ private ASN1Sequence toASN1Sequence(final Ruby runtime) { final ASN1EncodableVector revokedEntries = new ASN1EncodableVector(revoked.size()); try { for (int i = 0; i < revoked.size(); i++) { - revokedEntries.add(asRevoked(runtime, revoked.eltInternal(i)).toASN1Sequence()); + revokedEntries.add(X509Revoked.asRevoked(runtime, revoked.eltInternal(i)).toASN1Sequence()); } } catch (IOException e) { throw newCRLError(runtime, e); @@ -782,11 +794,6 @@ private ASN1Sequence toASN1Sequence(final Ruby runtime) { }); } - private static X509Revoked asRevoked(final Ruby runtime, final IRubyObject value) { - if (!(value instanceof X509Revoked)) throw runtime.newTypeError(value, X509Revoked._Revoked(runtime)); - return (X509Revoked) value; - } - private ASN1Primitive readCRL(final Ruby runtime) { try { return ASN1.readObject(getEncodedOriginal()); diff --git a/src/main/java/org/jruby/ext/openssl/X509Extension.java b/src/main/java/org/jruby/ext/openssl/X509Extension.java index 5d0ecc7b..be40bfcb 100644 --- a/src/main/java/org/jruby/ext/openssl/X509Extension.java +++ b/src/main/java/org/jruby/ext/openssl/X509Extension.java @@ -164,6 +164,31 @@ static X509Extension newExtension(final Ruby runtime, ASN1ObjectIdentifier objec return ext; } + static X509Extension copy(final Ruby runtime, final X509Extension extension) { + final X509Extension copy = new X509Extension(runtime, _Extension(runtime)); + copy.setRealObjectID(extension.getRealObjectID()); + copy.setRealCritical(extension.isRealCritical()); + try { + copy.value = extension.getRealValueEncoded().clone(); + } catch (IOException e) { + throw newExtensionError(runtime, e); + } + return copy; + } + + static RubyArray copyExtensions(final Ruby runtime, final RubyArray array) { + final int length = array.size(); + final IRubyObject[] copy = new IRubyObject[length]; + for (int i = 0; i < length; i++) { + final IRubyObject extension = array.eltInternal(i); + if (!(extension instanceof X509Extension)) { + throw runtime.newTypeError(extension, X509Extension._Extension(runtime)); + } + copy[i] = copy(runtime, (X509Extension) extension); + } + return RubyArray.newArrayNoCopy(runtime, copy); + } + ASN1ObjectIdentifier getRealObjectID() { return objectID; } diff --git a/src/main/java/org/jruby/ext/openssl/X509Revoked.java b/src/main/java/org/jruby/ext/openssl/X509Revoked.java index 57ae8365..77679122 100644 --- a/src/main/java/org/jruby/ext/openssl/X509Revoked.java +++ b/src/main/java/org/jruby/ext/openssl/X509Revoked.java @@ -53,6 +53,7 @@ import org.jruby.RubyTime; import org.jruby.anno.JRubyMethod; import org.jruby.exceptions.RaiseException; +import org.jruby.ext.openssl.util.RubySupport; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.runtime.Visibility; @@ -124,6 +125,32 @@ public X509Revoked(Ruby runtime, RubyClass type) { super(runtime,type); } + static X509Revoked copy(final Ruby runtime, final X509Revoked revoked) { + final X509Revoked copy = new X509Revoked(runtime, _Revoked(runtime)); + copy.serial = BN.newInstance(runtime, revoked.getSerial().getValue()); + if (revoked.time != null) { + copy.time = RubyTime.newTime(runtime, revoked.time.getJavaDate().getTime()); + } + if (revoked.extensions != null) { + copy.extensions = X509Extension.copyExtensions(runtime, revoked.extensions); + } + return copy; + } + + static RubyArray copyRevoked(final Ruby runtime, final RubyArray array) { + final int length = array.size(); + final IRubyObject[] copy = new IRubyObject[length]; + for (int i = 0; i < length; i++) { + copy[i] = X509Revoked.copy(runtime, X509Revoked.asRevoked(runtime, array.eltInternal(i))); + } + return RubyArray.newArrayNoCopy(runtime, copy); + } + + static X509Revoked asRevoked(final Ruby runtime, final IRubyObject value) { + if (!(value instanceof X509Revoked)) throw runtime.newTypeError(value, _Revoked(runtime)); + return (X509Revoked) value; + } + @JRubyMethod(name = "initialize", rest = true, visibility = Visibility.PRIVATE) public IRubyObject initialize(final ThreadContext context, final IRubyObject[] args) { serial = BN.newInstance(context.runtime, BigInteger.ZERO); @@ -171,24 +198,30 @@ boolean hasExtensions() { } @JRubyMethod - public RubyArray extensions() { + public RubyArray extensions(ThreadContext context) { + return extensions == null ? context.runtime.newEmptyArray() : RubySupport.copyArray(context.runtime, extensions); + } + + private RubyArray extensionsInternal() { return extensions == null ? extensions = RubyArray.newArray(getRuntime(), 4) : extensions; } @JRubyMethod(name = "extensions=") - public IRubyObject set_extensions(final IRubyObject extensions) { + public IRubyObject set_extensions(ThreadContext context, final IRubyObject extensions) { if (!(extensions instanceof RubyArray)) { - throw getRuntime().newTypeError(extensions, getRuntime().getArray()); + throw context.runtime.newTypeError(extensions, context.runtime.getArray()); } - return this.extensions = (RubyArray) extensions; + this.extensions = X509Extension.copyExtensions(context.runtime, (RubyArray) extensions); + return extensions; } @JRubyMethod - public IRubyObject add_extension(final ThreadContext context, final IRubyObject ext) { + public IRubyObject add_extension(ThreadContext context, final IRubyObject ext) { if (!(ext instanceof X509Extension)) { throw context.runtime.newTypeError(ext, X509Extension._Extension(context.runtime)); } - return extensions().append(ext); + extensionsInternal().append(X509Extension.copy(context.runtime, (X509Extension) ext)); + return ext; } @Override @@ -216,7 +249,7 @@ ASN1Sequence toASN1Sequence() throws IOException { vec.add(new ASN1Integer(getSerial().getValue())); vec.add(new Time(revokedTime.toDate())); if (hasExtensions()) { - final RubyArray extensions = this.extensions(); + final RubyArray extensions = this.extensions; final ASN1Encodable[] entries = new ASN1Encodable[extensions.size()]; for (int i = 0; i < extensions.size(); i++) { entries[i] = ((X509Extension) extensions.eltInternal(i)).toASN1Sequence(); diff --git a/src/main/java/org/jruby/ext/openssl/util/RubySupport.java b/src/main/java/org/jruby/ext/openssl/util/RubySupport.java index 00ace55c..59dc84f7 100644 --- a/src/main/java/org/jruby/ext/openssl/util/RubySupport.java +++ b/src/main/java/org/jruby/ext/openssl/util/RubySupport.java @@ -31,6 +31,7 @@ import org.jcodings.specific.ASCIIEncoding; import org.jcodings.specific.UTF8Encoding; import org.jruby.Ruby; +import org.jruby.RubyArray; import org.jruby.RubyBasicObject; import org.jruby.RubyClass; import org.jruby.RubyEncoding; @@ -90,6 +91,13 @@ public static Date timeToJavaDate(final ThreadContext context, final IRubyObject return ((RubyTime) value).getJavaDate(); } + public static RubyArray copyArray(final Ruby runtime, final RubyArray array) { + final int length = array.size(); + final IRubyObject[] copy = new IRubyObject[length]; + for (int i = 0; i < length; i++) copy[i] = array.eltInternal(i); + return RubyArray.newArrayNoCopy(runtime, copy); + } + // error/exception factory helpers public static RaiseException newIOError(Ruby runtime, IOException e) { diff --git a/test/x509/test_x509crl.rb b/test/x509/test_x509crl.rb index bc2e0327..619699d2 100644 --- a/test/x509/test_x509crl.rb +++ b/test/x509/test_x509crl.rb @@ -228,6 +228,50 @@ def test_encoded_crl_tracks_changes assert_equal 1, crl.to_java.getRevokedCertificates.size end + def test_assigned_revoked_and_extensions_are_owned_by_crl + crl = OpenSSL::X509::CRL.new + revoked = OpenSSL::X509::Revoked.new + revoked.serial = 1 + revoked.time = Time.at(1) + extension = OpenSSL::X509::Extension.new('crlNumber', '1') + revoked_entries = [revoked] + extensions = [extension] + crl.revoked = revoked_entries + crl.extensions = extensions + + revoked_entries.clear + extensions.clear + revoked.serial = 2 + extension.oid = 'keyUsage' + + assert_equal 1, crl.revoked.size + assert_equal 1, crl.revoked.first.serial + assert_equal 1, crl.extensions.size + assert_equal 'crlNumber', crl.extensions.first.oid + + crl.revoked.clear + crl.extensions.clear + assert_equal 1, crl.revoked.size + assert_equal 1, crl.extensions.size + + assert_raises(TypeError) { crl.revoked = [Object.new] } + assert_raises(TypeError) { crl.extensions = [Object.new] } + assert_equal 1, crl.revoked.size + assert_equal 1, crl.extensions.size + + crl = OpenSSL::X509::CRL.new + revoked.serial = 1 + extension.oid = 'crlNumber' + + crl.add_revoked(revoked) + crl.add_extension(extension) + + revoked.serial = 2 + extension.oid = 'keyUsage' + assert_equal 1, crl.revoked.first.serial + assert_equal 'crlNumber', crl.extensions.first.oid + end + def test_revoked_requires_time now = Time.now key = OpenSSL::PKey::RSA.new TEST_KEY_RSA2048 diff --git a/test/x509/test_x509ext.rb b/test/x509/test_x509ext.rb index 2acad693..5b1d9eca 100644 --- a/test/x509/test_x509ext.rb +++ b/test/x509/test_x509ext.rb @@ -78,6 +78,15 @@ def test_subject_alt_name_invalid_ip '(name=subjectAltName, value=IP:not.a.valid.ip.address)', error.message end + def test_extension_factory_accepts_shifted_array_arguments + args = [:discard, 'basicConstraints', 'CA:FALSE'] + args.shift + + ext = OpenSSL::X509::ExtensionFactory.new.create_extension(args) + assert_equal 'basicConstraints', ext.oid + assert_equal 'CA:FALSE', ext.value + end + def test_to_a ext = OpenSSL::X509::Extension.new('1.1.1.1.1.1', 'foo') assert_equal [ '1.1.1.1.1.1', 'foo', false ], ext.to_a diff --git a/test/x509/test_x509revoked.rb b/test/x509/test_x509revoked.rb index d98a4027..8462d7ac 100644 --- a/test/x509/test_x509revoked.rb +++ b/test/x509/test_x509revoked.rb @@ -37,6 +37,32 @@ def test_serial= assert_equal '4242', rev.serial.to_s end + def test_assigned_extensions_are_owned_by_revoked + rev = OpenSSL::X509::Revoked.new + extension = OpenSSL::X509::Extension.new('CRLReason', 'keyCompromise') + extensions = [extension] + rev.extensions = extensions + + extensions.clear + extension.oid = 'keyUsage' + assert_equal 1, rev.extensions.size + assert_equal 'CRLReason', rev.extensions.first.oid + + rev.extensions.clear + assert_equal 1, rev.extensions.size + + assert_raises(TypeError) { rev.extensions = [Object.new] } + assert_equal 1, rev.extensions.size + + rev = OpenSSL::X509::Revoked.new + extension.oid = 'CRLReason' + + rev.add_extension(extension) + + extension.oid = 'keyUsage' + assert_equal 'CRLReason', rev.extensions.first.oid + end + def test_to_der rev = OpenSSL::X509::Revoked.new rev.serial = 42