Skip to content

Commit 3c5d008

Browse files
Fixing ASN1Data encoding and decoding
`OpenSSL::ASN1::ASN1Data` encoding and decoding hasn't worked well for some time: * it was decoding the value always as a Sequence (when values can be strings as well); * after decoding, the `.tag_class` was always `:CONTEXT_SPECIFIC` (it can be `:UNIVERSAL` as well); * it wasn't correctly encoding `:APPLICATION`-tagged structures; The tests added verify that the behaviour of these operations now matchws what CRuby's `openssl` gem does.
1 parent 823c9a9 commit 3c5d008

File tree

1 file changed

+46
-21
lines changed

1 file changed

+46
-21
lines changed

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

+46-21
Original file line numberDiff line numberDiff line change
@@ -956,7 +956,7 @@ private static RubyString name(final ThreadContext context, IRubyObject value,
956956
} // ObjectId
957957

958958
static IRubyObject decodeObject(final ThreadContext context,
959-
final RubyModule ASN1, final org.bouncycastle.asn1.ASN1Encodable obj)
959+
final RubyModule ASN1, final ASN1Encodable obj)
960960
throws IOException, IllegalArgumentException {
961961
final Ruby runtime = context.runtime;
962962

@@ -1076,19 +1076,22 @@ else if ( obj instanceof DERBMPString ) {
10761076
final ASN1ApplicationSpecific appSpecific = (ASN1ApplicationSpecific) obj;
10771077
IRubyObject tag = runtime.newFixnum( appSpecific.getApplicationTag() );
10781078
IRubyObject tag_class = runtime.newSymbol("APPLICATION");
1079-
final ASN1Sequence sequence = (ASN1Sequence) appSpecific.getObject(SEQUENCE);
1080-
@SuppressWarnings("unchecked")
1081-
final RubyArray valArr = decodeObjects(context, ASN1, sequence.getObjects());
1082-
return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { valArr, tag, tag_class }, Block.NULL_BLOCK);
1079+
final ASN1TaggedObject taggedObj = (ASN1TaggedObject) obj;
1080+
final ASN1Encodable taggedBaseObj = taggedObj.getBaseObject();
1081+
return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { decodeObject(context, ASN1, taggedBaseObj).callMethod(context, "value"), tag, tag_class }, Block.NULL_BLOCK);
10831082
}
10841083

10851084
if ( obj instanceof ASN1TaggedObject ) {
10861085
final ASN1TaggedObject taggedObj = (ASN1TaggedObject) obj;
10871086
IRubyObject val = decodeObject(context, ASN1, taggedObj.getBaseObject());
10881087
IRubyObject tag = runtime.newFixnum( taggedObj.getTagNo() );
1089-
IRubyObject tag_class = runtime.newSymbol("CONTEXT_SPECIFIC");
1090-
final RubyArray valArr = runtime.newArray(val);
1091-
return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { valArr, tag, tag_class }, Block.NULL_BLOCK);
1088+
IRubyObject tag_class = runtime.newSymbol("CONTEXT_SPECIFIC");;
1089+
if (BERTags.UNIVERSAL != taggedObj.getTagClass()) {
1090+
tag_class = runtime.newSymbol("UNIVERSAL");
1091+
}
1092+
final ASN1Encodable taggedBaseObj = taggedObj.getBaseObject();
1093+
System.out.println("decoding " + taggedBaseObj.getClass());
1094+
return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { decodeObject(context, ASN1, taggedBaseObj).callMethod(context, "value"), tag, tag_class }, Block.NULL_BLOCK);
10921095
}
10931096

10941097
if ( obj instanceof ASN1Sequence ) {
@@ -1383,29 +1386,51 @@ ASN1Encodable toASN1(final ThreadContext context) {
13831386

13841387
final ASN1TaggedObject toASN1TaggedObject(final ThreadContext context) {
13851388
final int tag = getTag(context);
1389+
final RubyModule ASN1 = _ASN1(context.runtime);
1390+
1391+
IRubyObject val = callMethod(context, "value");
1392+
final IRubyObject tagClass = callMethod(context, "tag_class");
1393+
ASN1Encodable obj;
13861394

1387-
final IRubyObject val = callMethod(context, "value");
13881395
if ( val instanceof RubyArray ) {
13891396
final RubyArray arr = (RubyArray) val;
1390-
if ( arr.size() > 1 ) {
1391-
ASN1EncodableVector vec = new ASN1EncodableVector();
1392-
for ( final IRubyObject obj : arr.toJavaArray() ) {
1393-
ASN1Encodable data = ((ASN1Data) obj).toASN1(context);
1394-
if ( data == null ) break; vec.add( data );
1397+
1398+
if ( arr.size() >= 1 ) {
1399+
ByteArrayOutputStream bytes = new ByteArrayOutputStream( );
1400+
1401+
for ( final IRubyObject o : arr.toJavaArray() ) {
1402+
try {
1403+
bytes.write(((RubyString) o).getBytes());
1404+
} catch (IOException e) {
1405+
throw new IllegalStateException(e.getMessage());
1406+
} catch(ClassCastException e) {
1407+
throw context.runtime.newTypeError(e.getMessage());
1408+
}
13951409
}
1396-
return new DERTaggedObject(isExplicitTagging(), tag, new DERSequence(vec));
1410+
obj = new DEROctetString(bytes.toByteArray());
1411+
} else {
1412+
throw new IllegalStateException("empty array detected");
13971413
}
1398-
else if ( arr.size() == 1 ) {
1399-
ASN1Encodable data = ((ASN1Data) arr.entry(0)).toASN1(context);
1400-
return new DERTaggedObject(isExplicitTagging(), tag, data);
1414+
} else {
1415+
try {
1416+
obj = new DEROctetString(((RubyString) val).getBytes());
1417+
} catch(ClassCastException e) {
1418+
throw context.runtime.newTypeError("no implicit conversion of " + val.getMetaClass().getName() + " into String");
14011419
}
1402-
else {
1403-
throw new IllegalStateException("empty array detected");
1420+
}
1421+
1422+
if (tagClass.toString().equals("APPLICATION")) {
1423+
try {
1424+
return new DERApplicationSpecific(isExplicitTagging(), tag, obj);
1425+
} catch (IOException e) {
1426+
throw new IllegalStateException(e.getMessage());
14041427
}
1428+
} else {
1429+
return new DERTaggedObject(isExplicitTagging(), tag, obj);
14051430
}
1406-
return new DERTaggedObject(isExplicitTagging(), tag, ((ASN1Data) val).toASN1(context));
14071431
}
14081432

1433+
14091434
@JRubyMethod
14101435
public IRubyObject to_der(final ThreadContext context) {
14111436
try {

0 commit comments

Comments
 (0)