From d2b2384373eac9d56ff5b89b1e5d3cae3bf1ec04 Mon Sep 17 00:00:00 2001 From: HoneyryderChuck Date: Sat, 20 Aug 2022 14:56:06 +0100 Subject: [PATCH 1/3] to_der on ASN1Data should convert ruby strings into java strings before encoding --- src/main/java/org/jruby/ext/openssl/ASN1.java | 87 +++++++++++++++++-- src/test/ruby/test_asn1.rb | 30 +++++++ 2 files changed, 112 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/jruby/ext/openssl/ASN1.java b/src/main/java/org/jruby/ext/openssl/ASN1.java index 8344b0d1..5d2582ce 100644 --- a/src/main/java/org/jruby/ext/openssl/ASN1.java +++ b/src/main/java/org/jruby/ext/openssl/ASN1.java @@ -1357,6 +1357,10 @@ boolean isEOC() { return "EndOfContent".equals( getClassBaseName() ); } + boolean isUniversal(final ThreadContext context) { + return "ASN1Data".equals(getClassBaseName()) && getTagClass(context) == 0; + } + IRubyObject tagging() { return getInstanceVariable("@tagging"); } @@ -1395,16 +1399,50 @@ final ASN1TaggedObject toASN1TaggedObject(final ThreadContext context) { final IRubyObject value = callMethod(context, "value"); if (value instanceof RubyArray) { + // Cruby openssl joins elements of array and casts to string final RubyArray arr = (RubyArray) value; - assert ! arr.isEmpty(); + String values = new String(); ASN1EncodableVector vec = new ASN1EncodableVector(); + for (final IRubyObject obj : arr.toJavaArray()) { - ASN1Encodable data = ((ASN1Data) obj).toASN1(context); - if ( data == null ) break; - vec.add( data ); + if (obj instanceof ASN1Data) { + ASN1Encodable data = ((ASN1Data) obj).toASN1(context); + if (data == null) break; + vec.add(data); + } else { + final IRubyObject string = obj.checkStringType(); + if (string instanceof RubyString) { + values = values.concat(string.asJavaString()); + } else { + throw context.runtime.newTypeError( + "no implicit conversion of " + obj.getMetaClass().getBaseName() + " into String"); + } + } } - return new DERTaggedObject(isExplicitTagging(), tag, new DERSequence(vec)); + + if (vec.size() > 0) { + // array of asn1 objects as value + return new DERTaggedObject(isExplicitTagging(), tag, new DERSequence(vec)); + } + + // array of strings as value (default) + return new DERTaggedObject(isExplicitTagging(), tagClass, tag, + new DERGeneralString(values)); + } else if (value instanceof ASN1Data) { + return new DERTaggedObject(isExplicitTagging(), tagClass, tag, ((ASN1Data) value).toASN1(context)); + } else if (value instanceof RubyObject) { + final IRubyObject string = value.checkStringType(); + if (string instanceof RubyString) { + return new DERTaggedObject(isExplicitTagging(), tagClass, tag, + new DERGeneralString(string.asJavaString())); + } else { + throw context.runtime.newTypeError( + "no implicit conversion of " + value.getMetaClass().getBaseName() + " into String"); + } + } else { + throw context.runtime.newTypeError( + "no implicit conversion of " + value.getMetaClass().getBaseName() + " into String"); } if (!(value instanceof ASN1Data)) { @@ -1426,6 +1464,40 @@ public IRubyObject to_der(final ThreadContext context) { byte[] toDER(final ThreadContext context) throws IOException { if ( isEOC() ) return new byte[] { 0x00, 0x00 }; + + if (isUniversal(context)) { + // handstitch conversion + final java.io.ByteArrayOutputStream out = new ByteArrayOutputStream(); + final IRubyObject value = callMethod(context, "value"); + + final byte[] valueBytes; + if (value instanceof RubyArray) { + String values = new String(); + final RubyArray arr = (RubyArray) value; + for (final IRubyObject obj : arr.toJavaArray()) { + final IRubyObject string = value.checkStringType(); + if (string instanceof RubyString) { + values = values + string; + } else { + throw context.runtime.newTypeError( + "no implicit conversion of " + obj.getMetaClass().getBaseName() + " into String"); + } + } + valueBytes = values.getBytes(); + } else { + final IRubyObject string = value.checkStringType(); + if (string instanceof RubyString) { + valueBytes = ((RubyString) string).getBytes(); + } else { + throw context.runtime.newTypeError( + "no implicit conversion of " + value.getMetaClass().getBaseName() + " into String"); + } + } + out.write(getTag(context)); + out.write(valueBytes.length); + out.write(valueBytes); + return out.toByteArray(); + } return toASN1(context).toASN1Primitive().getEncoded(ASN1Encoding.DER); } @@ -1619,6 +1691,11 @@ boolean isEOC() { return false; } + @Override + boolean isUniversal(final ThreadContext context) { + return false; + } + private boolean isNull() { return "Null".equals(getMetaClass().getRealClass().getBaseName()); } diff --git a/src/test/ruby/test_asn1.rb b/src/test/ruby/test_asn1.rb index 6f8eb955..9544fc73 100644 --- a/src/test/ruby/test_asn1.rb +++ b/src/test/ruby/test_asn1.rb @@ -251,6 +251,36 @@ def test_null } end + def test_encode_asn1_data + ai = OpenSSL::ASN1::ASN1Data.new(i = "bla", 0, :APPLICATION) + ai2 = OpenSSL::ASN1.decode(ai.to_der) + assert_equal :APPLICATION, ai2.tag_class + assert_equal 0, ai2.tag + assert_equal i, ai2.value + + ai = OpenSSL::ASN1::ASN1Data.new(i = "bla", 4, :UNIVERSAL) + ai2 = OpenSSL::ASN1.decode(ai.to_der) + assert_equal :UNIVERSAL, ai2.tag_class + assert_equal 4, ai2.tag + assert_equal i, ai2.value + + ai = OpenSSL::ASN1::ASN1Data.new(i = ["bla"], 0, :APPLICATION) + ai2 = OpenSSL::ASN1.decode(ai.to_der) + assert_equal :APPLICATION, ai2.tag_class + assert_equal 0, ai2.tag + assert_equal "bla", ai2.value + + ai = OpenSSL::ASN1::ASN1Data.new(i = ["bla", "bla"], 0, :APPLICATION) + ai2 = OpenSSL::ASN1.decode(ai.to_der) + assert_equal :APPLICATION, ai2.tag_class + assert_equal 0, ai2.tag + assert_equal "blabla", ai2.value + + assert_raise(ArgumentError) { OpenSSL::ASN1::ASN1Data.new(1).to_der } + assert_raise("no implicit conversion of Integer into String") { OpenSSL::ASN1::ASN1Data.new(1, 0, :APPLICATION).to_der } + assert_raise("no implicit conversion of Integer into String") { OpenSSL::ASN1::ASN1Data.new(1, 0, :CONTEXT_SPECIFIC).to_der } + end + def test_encode_nil #Primitives raise TypeError, Constructives NoMethodError From e20132de8531eb904a4a2145ac9659df6adb2f4a Mon Sep 17 00:00:00 2001 From: HoneyryderChuck Date: Wed, 8 May 2024 18:40:49 +0100 Subject: [PATCH 2/3] handle string in asn1data --- src/main/java/org/jruby/ext/openssl/ASN1.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/jruby/ext/openssl/ASN1.java b/src/main/java/org/jruby/ext/openssl/ASN1.java index 5d2582ce..55295f24 100644 --- a/src/main/java/org/jruby/ext/openssl/ASN1.java +++ b/src/main/java/org/jruby/ext/openssl/ASN1.java @@ -1072,10 +1072,15 @@ else if ( obj instanceof ASN1GraphicString ) { } if (taggedObj.getTagClass() == BERTags.APPLICATION) { - final ASN1Sequence sequence = (ASN1Sequence) taggedObj.getBaseUniversal(false, SEQUENCE); - @SuppressWarnings("unchecked") - final RubyArray valArr = decodeObjects(context, ASN1, sequence.getObjects()); - return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { valArr, tag, tag_class }, Block.NULL_BLOCK); + try { + final ASN1Sequence sequence = (ASN1Sequence) taggedObj.getBaseUniversal(false, SEQUENCE); + @SuppressWarnings("unchecked") + final RubyArray valArr = decodeObjects(context, ASN1, sequence.getObjects()); + return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { valArr, tag, tag_class }, Block.NULL_BLOCK); + } catch (IllegalStateException e) { + IRubyObject val = decodeObject(context, ASN1, taggedObj.getBaseObject()).callMethod(context, "value"); + return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { val, tag, tag_class }, Block.NULL_BLOCK); + } } else { IRubyObject val = decodeObject(context, ASN1, taggedObj.getBaseObject()); final RubyArray valArr = runtime.newArray(val); @@ -1444,11 +1449,6 @@ final ASN1TaggedObject toASN1TaggedObject(final ThreadContext context) { throw context.runtime.newTypeError( "no implicit conversion of " + value.getMetaClass().getBaseName() + " into String"); } - - if (!(value instanceof ASN1Data)) { - throw new UnsupportedOperationException("toASN1 " + inspect() + " value: " + value.inspect() + " (" + value.getMetaClass() + ")"); - } - return new DERTaggedObject(isExplicitTagging(), tagClass, tag, ((ASN1Data) value).toASN1(context)); } @JRubyMethod From 6c1c8824b0141259f9a0c909c8084c4b9769da4a Mon Sep 17 00:00:00 2001 From: HoneyryderChuck Date: Wed, 15 May 2024 14:24:33 +0100 Subject: [PATCH 3/3] unify how taggedobjects get decoded --- src/main/java/org/jruby/ext/openssl/ASN1.java | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/jruby/ext/openssl/ASN1.java b/src/main/java/org/jruby/ext/openssl/ASN1.java index 55295f24..670e2de1 100644 --- a/src/main/java/org/jruby/ext/openssl/ASN1.java +++ b/src/main/java/org/jruby/ext/openssl/ASN1.java @@ -1071,20 +1071,14 @@ else if ( obj instanceof ASN1GraphicString ) { break; } - if (taggedObj.getTagClass() == BERTags.APPLICATION) { - try { - final ASN1Sequence sequence = (ASN1Sequence) taggedObj.getBaseUniversal(false, SEQUENCE); - @SuppressWarnings("unchecked") - final RubyArray valArr = decodeObjects(context, ASN1, sequence.getObjects()); - return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { valArr, tag, tag_class }, Block.NULL_BLOCK); - } catch (IllegalStateException e) { - IRubyObject val = decodeObject(context, ASN1, taggedObj.getBaseObject()).callMethod(context, "value"); - return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { val, tag, tag_class }, Block.NULL_BLOCK); - } - } else { - IRubyObject val = decodeObject(context, ASN1, taggedObj.getBaseObject()); - final RubyArray valArr = runtime.newArray(val); + try { + final ASN1Sequence sequence = (ASN1Sequence) taggedObj.getBaseUniversal(false, SEQUENCE); + @SuppressWarnings("unchecked") + final RubyArray valArr = decodeObjects(context, ASN1, sequence.getObjects()); return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { valArr, tag, tag_class }, Block.NULL_BLOCK); + } catch (IllegalStateException e) { + IRubyObject val = decodeObject(context, ASN1, taggedObj.getBaseObject()).callMethod(context, "value"); + return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { val, tag, tag_class }, Block.NULL_BLOCK); } }