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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy