Skip to content

Commit de0d96c

Browse files
[fix] OpenSSL::ASN1::ASN1Data encoding/decoding compatibility (#265)
* to_der on ASN1Data should convert ruby strings into java strings before encoding * handle string in asn1data * unify how taggedobjects get decoded
1 parent 050856c commit de0d96c

File tree

2 files changed

+116
-14
lines changed

2 files changed

+116
-14
lines changed

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

Lines changed: 86 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,15 +1071,14 @@ else if ( obj instanceof ASN1GraphicString ) {
10711071
break;
10721072
}
10731073

1074-
if (taggedObj.getTagClass() == BERTags.APPLICATION) {
1074+
try {
10751075
final ASN1Sequence sequence = (ASN1Sequence) taggedObj.getBaseUniversal(false, SEQUENCE);
10761076
@SuppressWarnings("unchecked")
10771077
final RubyArray valArr = decodeObjects(context, ASN1, sequence.getObjects());
10781078
return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { valArr, tag, tag_class }, Block.NULL_BLOCK);
1079-
} else {
1080-
IRubyObject val = decodeObject(context, ASN1, taggedObj.getBaseObject());
1081-
final RubyArray valArr = runtime.newArray(val);
1082-
return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { valArr, tag, tag_class }, Block.NULL_BLOCK);
1079+
} catch (IllegalStateException e) {
1080+
IRubyObject val = decodeObject(context, ASN1, taggedObj.getBaseObject()).callMethod(context, "value");
1081+
return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { val, tag, tag_class }, Block.NULL_BLOCK);
10831082
}
10841083
}
10851084

@@ -1357,6 +1356,10 @@ boolean isEOC() {
13571356
return "EndOfContent".equals( getClassBaseName() );
13581357
}
13591358

1359+
boolean isUniversal(final ThreadContext context) {
1360+
return "ASN1Data".equals(getClassBaseName()) && getTagClass(context) == 0;
1361+
}
1362+
13601363
IRubyObject tagging() {
13611364
return getInstanceVariable("@tagging");
13621365
}
@@ -1395,22 +1398,51 @@ final ASN1TaggedObject toASN1TaggedObject(final ThreadContext context) {
13951398

13961399
final IRubyObject value = callMethod(context, "value");
13971400
if (value instanceof RubyArray) {
1401+
// Cruby openssl joins elements of array and casts to string
13981402
final RubyArray arr = (RubyArray) value;
1399-
assert ! arr.isEmpty();
14001403

1404+
StringBuilder values = new StringBuilder();
14011405
ASN1EncodableVector vec = new ASN1EncodableVector();
1406+
14021407
for (final IRubyObject obj : arr.toJavaArray()) {
1403-
ASN1Encodable data = ((ASN1Data) obj).toASN1(context);
1404-
if ( data == null ) break;
1405-
vec.add( data );
1408+
if (obj instanceof ASN1Data) {
1409+
ASN1Encodable data = ((ASN1Data) obj).toASN1(context);
1410+
if (data == null) break;
1411+
vec.add(data);
1412+
} else {
1413+
final IRubyObject string = obj.checkStringType();
1414+
if (string instanceof RubyString) {
1415+
values.append(string.asJavaString());
1416+
} else {
1417+
throw context.runtime.newTypeError(
1418+
"no implicit conversion of " + obj.getMetaClass().getBaseName() + " into String");
1419+
}
1420+
}
14061421
}
1407-
return new DERTaggedObject(isExplicitTagging(), tag, new DERSequence(vec));
1408-
}
14091422

1410-
if (!(value instanceof ASN1Data)) {
1411-
throw new UnsupportedOperationException("toASN1 " + inspect() + " value: " + value.inspect() + " (" + value.getMetaClass() + ")");
1423+
if (vec.size() > 0) {
1424+
// array of asn1 objects as value
1425+
return new DERTaggedObject(isExplicitTagging(), tag, new DERSequence(vec));
1426+
}
1427+
1428+
// array of strings as value (default)
1429+
return new DERTaggedObject(isExplicitTagging(), tagClass, tag,
1430+
new DERGeneralString(values.toString()));
1431+
} else if (value instanceof ASN1Data) {
1432+
return new DERTaggedObject(isExplicitTagging(), tagClass, tag, ((ASN1Data) value).toASN1(context));
1433+
} else if (value instanceof RubyObject) {
1434+
final IRubyObject string = value.checkStringType();
1435+
if (string instanceof RubyString) {
1436+
return new DERTaggedObject(isExplicitTagging(), tagClass, tag,
1437+
new DERGeneralString(string.asJavaString()));
1438+
} else {
1439+
throw context.runtime.newTypeError(
1440+
"no implicit conversion of " + value.getMetaClass().getBaseName() + " into String");
1441+
}
1442+
} else {
1443+
throw context.runtime.newTypeError(
1444+
"no implicit conversion of " + value.getMetaClass().getBaseName() + " into String");
14121445
}
1413-
return new DERTaggedObject(isExplicitTagging(), tagClass, tag, ((ASN1Data) value).toASN1(context));
14141446
}
14151447

14161448
@JRubyMethod
@@ -1426,6 +1458,41 @@ public IRubyObject to_der(final ThreadContext context) {
14261458

14271459
byte[] toDER(final ThreadContext context) throws IOException {
14281460
if ( isEOC() ) return new byte[] { 0x00, 0x00 };
1461+
1462+
if (isUniversal(context)) {
1463+
// handstitch conversion
1464+
final java.io.ByteArrayOutputStream out = new ByteArrayOutputStream();
1465+
final IRubyObject value = callMethod(context, "value");
1466+
1467+
final byte[] valueBytes;
1468+
if (value instanceof RubyArray) {
1469+
final RubyArray arr = (RubyArray) value;
1470+
final java.io.ByteArrayOutputStream valueOut = new ByteArrayOutputStream();
1471+
1472+
for (final IRubyObject obj : arr.toJavaArray()) {
1473+
final IRubyObject string = value.checkStringType();
1474+
if (string instanceof RubyString) {
1475+
valueOut.write(((RubyString) string).getBytes());
1476+
} else {
1477+
throw context.runtime.newTypeError(
1478+
"no implicit conversion of " + obj.getMetaClass().getBaseName() + " into String");
1479+
}
1480+
}
1481+
valueBytes = valueOut.toByteArray();
1482+
} else {
1483+
final IRubyObject string = value.checkStringType();
1484+
if (string instanceof RubyString) {
1485+
valueBytes = ((RubyString) string).getBytes();
1486+
} else {
1487+
throw context.runtime.newTypeError(
1488+
"no implicit conversion of " + value.getMetaClass().getBaseName() + " into String");
1489+
}
1490+
}
1491+
out.write(getTag(context));
1492+
out.write(valueBytes.length);
1493+
out.write(valueBytes);
1494+
return out.toByteArray();
1495+
}
14291496
return toASN1(context).toASN1Primitive().getEncoded(ASN1Encoding.DER);
14301497
}
14311498

@@ -1619,6 +1686,11 @@ boolean isEOC() {
16191686
return false;
16201687
}
16211688

1689+
@Override
1690+
boolean isUniversal(final ThreadContext context) {
1691+
return false;
1692+
}
1693+
16221694
private boolean isNull() {
16231695
return "Null".equals(getMetaClass().getRealClass().getBaseName());
16241696
}

src/test/ruby/test_asn1.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,36 @@ def test_null
251251
}
252252
end
253253

254+
def test_encode_asn1_data
255+
ai = OpenSSL::ASN1::ASN1Data.new(i = "bla", 0, :APPLICATION)
256+
ai2 = OpenSSL::ASN1.decode(ai.to_der)
257+
assert_equal :APPLICATION, ai2.tag_class
258+
assert_equal 0, ai2.tag
259+
assert_equal i, ai2.value
260+
261+
ai = OpenSSL::ASN1::ASN1Data.new(i = "bla", 4, :UNIVERSAL)
262+
ai2 = OpenSSL::ASN1.decode(ai.to_der)
263+
assert_equal :UNIVERSAL, ai2.tag_class
264+
assert_equal 4, ai2.tag
265+
assert_equal i, ai2.value
266+
267+
ai = OpenSSL::ASN1::ASN1Data.new(i = ["bla"], 0, :APPLICATION)
268+
ai2 = OpenSSL::ASN1.decode(ai.to_der)
269+
assert_equal :APPLICATION, ai2.tag_class
270+
assert_equal 0, ai2.tag
271+
assert_equal "bla", ai2.value
272+
273+
ai = OpenSSL::ASN1::ASN1Data.new(i = ["bla", "bla"], 0, :APPLICATION)
274+
ai2 = OpenSSL::ASN1.decode(ai.to_der)
275+
assert_equal :APPLICATION, ai2.tag_class
276+
assert_equal 0, ai2.tag
277+
assert_equal "blabla", ai2.value
278+
279+
assert_raise(ArgumentError) { OpenSSL::ASN1::ASN1Data.new(1).to_der }
280+
assert_raise("no implicit conversion of Integer into String") { OpenSSL::ASN1::ASN1Data.new(1, 0, :APPLICATION).to_der }
281+
assert_raise("no implicit conversion of Integer into String") { OpenSSL::ASN1::ASN1Data.new(1, 0, :CONTEXT_SPECIFIC).to_der }
282+
end
283+
254284
def test_encode_nil
255285
#Primitives raise TypeError, Constructives NoMethodError
256286

0 commit comments

Comments
 (0)