Skip to content

Commit 34a60e8

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 8e5e6ef commit 34a60e8

File tree

1 file changed

+82
-27
lines changed

1 file changed

+82
-27
lines changed

Diff for: src/main/java/org/jruby/ext/openssl/ASN1.java

+82-27
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

@@ -1072,22 +1072,47 @@ else if ( obj instanceof DERBMPString ) {
10721072
return ASN1.getClass("ObjectId").newInstance(context, runtime.newString(objId), Block.NULL_BLOCK);
10731073
}
10741074

1075-
if (obj instanceof ASN1TaggedObject) {
1075+
1076+
if ( obj instanceof ASN1TaggedObject ) {
10761077
final ASN1TaggedObject taggedObj = (ASN1TaggedObject) obj;
1077-
if (taggedObj.getTagClass() == BERTags.APPLICATION) {
1078-
IRubyObject tag = runtime.newFixnum( taggedObj.getTagNo() );
1079-
IRubyObject tag_class = runtime.newSymbol("APPLICATION");
1080-
final ASN1Sequence sequence = (ASN1Sequence) taggedObj.getBaseUniversal(false, SEQUENCE);
1081-
@SuppressWarnings("unchecked")
1078+
// IRubyObject val = decodeObject(context, ASN1, taggedObj.getBaseObject());
1079+
IRubyObject tag = runtime.newFixnum( taggedObj.getTagNo() );
1080+
IRubyObject tag_class;
1081+
switch(taggedObj.getTagClass()) {
1082+
case BERTags.PRIVATE:
1083+
tag_class = runtime.newSymbol("PRIVATE");
1084+
break;
1085+
case BERTags.APPLICATION:
1086+
tag_class = runtime.newSymbol("APPLICATION");
1087+
break;
1088+
case BERTags.CONTEXT_SPECIFIC:
1089+
tag_class = runtime.newSymbol("CONTEXT_SPECIFIC");
1090+
break;
1091+
default:
1092+
tag_class = runtime.newSymbol("UNIVERSAL");
1093+
break;
1094+
}
1095+
1096+
try {
1097+
final ASN1Sequence sequence = (ASN1Sequence) taggedObj.getExplicitBaseObject();
10821098
final RubyArray valArr = decodeObjects(context, ASN1, sequence.getObjects());
10831099
return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { valArr, tag, tag_class }, Block.NULL_BLOCK);
1084-
} else {
1085-
IRubyObject val = decodeObject(context, ASN1, taggedObj.getBaseObject());
1086-
IRubyObject tag = runtime.newFixnum( taggedObj.getTagNo() );
1087-
IRubyObject tag_class = runtime.newSymbol("CONTEXT_SPECIFIC");
1088-
final RubyArray valArr = runtime.newArray(val);
1089-
return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { valArr, tag, tag_class }, Block.NULL_BLOCK);
1100+
} catch(ClassCastException e) {
1101+
final ASN1Encodable taggedBaseObj = taggedObj.getBaseObject();
1102+
return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { decodeObject(context, ASN1, taggedBaseObj).callMethod(context, "value"), tag, tag_class }, Block.NULL_BLOCK);
10901103
}
1104+
1105+
// if ( obj instanceof DLTaggedObject ) { // isConstructed
1106+
// System.out.println("ASN1Data constructive!");
1107+
// final ASN1Sequence sequence = (ASN1Sequence) taggedObj.getBaseUniversal(false, SEQUENCE);
1108+
// @SuppressWarnings("unchecked")
1109+
// final RubyArray valArr = decodeObjects(context, ASN1, sequence.getObjects());
1110+
// return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { valArr, tag, tag_class }, Block.NULL_BLOCK);
1111+
// } else {
1112+
// final ASN1Encodable taggedBaseObj = taggedObj.getBaseObject();
1113+
// System.out.println("ASN1Data primitive!");
1114+
// return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { decodeObject(context, ASN1, taggedBaseObj).callMethod(context, "value"), tag, tag_class }, Block.NULL_BLOCK);
1115+
// }
10911116
}
10921117

10931118
if ( obj instanceof ASN1Sequence ) {
@@ -1384,29 +1409,59 @@ ASN1Encodable toASN1(final ThreadContext context) {
13841409

13851410
final ASN1TaggedObject toASN1TaggedObject(final ThreadContext context) {
13861411
final int tag = getTag(context);
1412+
final RubyModule ASN1 = _ASN1(context.runtime);
1413+
1414+
IRubyObject val = callMethod(context, "value");
1415+
final IRubyObject tagClass = callMethod(context, "tag_class");
1416+
ASN1Encodable obj;
1417+
int berTag;
13871418

1388-
final IRubyObject val = callMethod(context, "value");
13891419
if ( val instanceof RubyArray ) {
13901420
final RubyArray arr = (RubyArray) val;
1391-
if ( arr.size() > 1 ) {
1392-
ASN1EncodableVector vec = new ASN1EncodableVector();
1393-
for ( final IRubyObject obj : arr.toJavaArray() ) {
1394-
ASN1Encodable data = ((ASN1Data) obj).toASN1(context);
1395-
if ( data == null ) break; vec.add( data );
1421+
1422+
if ( arr.size() >= 1 ) {
1423+
ByteArrayOutputStream bytes = new ByteArrayOutputStream( );
1424+
1425+
for ( final IRubyObject o : arr.toJavaArray() ) {
1426+
try {
1427+
bytes.write(((RubyString) o).getBytes());
1428+
} catch (IOException e) {
1429+
throw new IllegalStateException(e.getMessage());
1430+
} catch(ClassCastException e) {
1431+
throw context.runtime.newTypeError(e.getMessage());
1432+
}
13961433
}
1397-
return new DERTaggedObject(isExplicitTagging(), tag, new DERSequence(vec));
1398-
}
1399-
else if ( arr.size() == 1 ) {
1400-
ASN1Encodable data = ((ASN1Data) arr.entry(0)).toASN1(context);
1401-
return new DERTaggedObject(isExplicitTagging(), tag, data);
1402-
}
1403-
else {
1434+
obj = new DEROctetString(bytes.toByteArray());
1435+
} else {
14041436
throw new IllegalStateException("empty array detected");
14051437
}
1438+
} else {
1439+
try {
1440+
obj = new DEROctetString(((RubyString) val).getBytes());
1441+
} catch(ClassCastException e) {
1442+
throw context.runtime.newTypeError("no implicit conversion of " + val.getMetaClass().getName() + " into String");
1443+
}
14061444
}
1407-
return new DERTaggedObject(isExplicitTagging(), tag, ((ASN1Data) val).toASN1(context));
1445+
1446+
switch(tagClass.toString()) {
1447+
case "PRIVATE":
1448+
berTag = BERTags.PRIVATE;
1449+
break;
1450+
case "APPLICATION":
1451+
berTag = BERTags.APPLICATION;
1452+
break;
1453+
case "CONTEXT_SPECIFIC":
1454+
berTag = BERTags.CONTEXT_SPECIFIC;
1455+
break;
1456+
default:
1457+
berTag = BERTags.UNIVERSAL;
1458+
break;
1459+
}
1460+
1461+
return new DERTaggedObject(isExplicitTagging(), berTag, tag, obj);
14081462
}
14091463

1464+
14101465
@JRubyMethod
14111466
public IRubyObject to_der(final ThreadContext context) {
14121467
try {

0 commit comments

Comments
 (0)