Skip to content

Commit b852b51

Browse files
roadrunner2kares
authored andcommitted
subject alt name parsing fixes (#140)
* Fix formatting of IPv6 addresses. They now correctly show as 1234:abcd:... instead of 18::52::171::205::... * Fix subjectAltName parsing to always generate GeneralNames. It now handles arbitrary lists of names and always produces a GeneralNames as required by the definition. Additionally, leading and trailing whitespace around separators and labels is properly removed. This fixes #134.
1 parent 3c30b34 commit b852b51

File tree

3 files changed

+72
-48
lines changed

3 files changed

+72
-48
lines changed

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -661,11 +661,16 @@ private static boolean formatGeneralName(final GeneralName name, final ByteList
661661
append(':');
662662
final byte[] ip = ((ASN1OctetString) name.getName()).getOctets();
663663
int len = ip.length; boolean ip4 = len == 4;
664-
for ( int i = 0; i < ip.length; i++ ) {
665-
out.append( ConvertBytes.intToCharBytes( ((int) ip[i]) & 0xff ) );
666-
if ( i != len - 1 ) {
667-
if ( ip4 ) out.append('.');
668-
else out.append(':').append(':');
664+
if ( ip4 ) {
665+
for ( int i = 0; i < ip.length; i++ ) {
666+
out.append( ConvertBytes.intToCharBytes( ((int) ip[i]) & 0xff ) );
667+
if ( i != len - 1 ) out.append('.');
668+
}
669+
}
670+
else {
671+
for ( int i = 0; i < ip.length; i += 2 ) {
672+
out.append( ConvertBytes.intToHexBytes( ((ip[i] & 0xff) << 8 | (ip[i+1] & 0xff)) ) );
673+
if ( i != len - 2 ) out.append(':');
669674
}
670675
}
671676
break;

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

Lines changed: 30 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -490,69 +490,62 @@ private ASN1Encodable parseIssuerAltName(final ThreadContext context, final Stri
490490

491491
private static final String DNS_ = "DNS:";
492492
private static final String DNS_Name_ = "DNS Name:";
493+
private static final String IP_ = "IP:";
494+
private static final String IP_Address_ = "IP Address:";
493495
private static final String URI_ = "URI:";
494496
private static final String RID_ = "RID:";
495497
private static final String email_ = "email:";
496498
private static final String dirName_ = "dirName:";
497499
private static final String otherName_ = "otherName:";
498500

499-
private static ASN1Encodable parseSubjectAltName(final String valuex) throws IOException {
501+
private static GeneralNames parseSubjectAltName(final String valuex) throws IOException {
502+
final String[] vals = valuex.split("(?<!(^|[^\\\\])((\\\\\\\\)?\\\\\\\\)?\\\\),"); // allow up to three levels of escaping of ','
503+
final GeneralName[] names = new GeneralName[vals.length];
504+
for ( int i = 0; i < vals.length; i++ ) {
505+
names[i] = parseGeneralName(vals[i].replaceAll("\\\\([,\\\\])", "$1").trim());
506+
}
507+
return new GeneralNames(names);
508+
}
509+
510+
private static GeneralName parseGeneralName(final String valuex) throws IOException {
500511
if ( valuex.startsWith(DNS_) ) {
501-
final String[] vals = valuex.split(",");
502-
final GeneralName[] names = new GeneralName[vals.length];
503-
for ( int i = 0; i < vals.length; i++ ) {
504-
final String dns = vals[i].substring(DNS_.length());
505-
names[i] = new GeneralName(GeneralName.dNSName, dns);
506-
}
507-
return new GeneralNames(names);
512+
final String dns = valuex.substring(DNS_.length()).trim();
513+
return new GeneralName(GeneralName.dNSName, dns);
508514
}
509515
if ( valuex.startsWith(DNS_Name_) ) {
510-
final String dns = valuex.substring(DNS_Name_.length());
516+
final String dns = valuex.substring(DNS_Name_.length()).trim();
511517
return new GeneralName(GeneralName.dNSName, dns);
512518
}
513519
if ( valuex.startsWith(URI_) ) {
514-
final String uri = valuex.substring(URI_.length());
520+
final String uri = valuex.substring(URI_.length()).trim();
515521
return new GeneralName(GeneralName.uniformResourceIdentifier, uri);
516522
}
517523
if ( valuex.startsWith(RID_) ) {
518-
final String rid = valuex.substring(RID_.length());
524+
final String rid = valuex.substring(RID_.length()).trim();
519525
return new GeneralName(GeneralName.registeredID, rid);
520526
}
521527
if ( valuex.startsWith(email_) ) {
522-
final String[] vals = valuex.split(",");
523-
final GeneralName[] names = new GeneralName[vals.length];
524-
for ( int i = 0; i < vals.length; i++ ) {
525-
if (vals[i].startsWith(email_)) {
526-
String mail = vals[i].substring(email_.length());
527-
names[i] = new GeneralName(GeneralName.rfc822Name, mail);
528-
}
529-
else {
530-
ASN1Encodable name = parseSubjectAltName(vals[i]);
531-
names[i] = name instanceof GeneralNames ? ((GeneralNames) name).getNames()[0] : (GeneralName) name;
532-
}
533-
}
534-
return new GeneralNames(names);
535-
}
536-
if ( valuex.startsWith("IP:") || valuex.startsWith("IP Address:") ) {
537-
final int idx = valuex.charAt(2) == ':' ? 3 : 11;
538-
String[] vals = valuex.substring(idx).split("\\.|::");
539-
final byte[] ip = new byte[vals.length];
540-
for ( int i = 0; i < vals.length; i++ ) {
541-
ip[i] = (byte) (Integer.parseInt(vals[i]) & 0xff);
542-
}
543-
return new GeneralName(GeneralName.iPAddress, new DEROctetString(ip));
528+
String mail = valuex.substring(email_.length()).trim();
529+
return new GeneralName(GeneralName.rfc822Name, mail);
530+
}
531+
if ( valuex.startsWith(IP_) ) {
532+
final String ip = valuex.substring(IP_.length()).trim();
533+
return new GeneralName(GeneralName.iPAddress, ip);
534+
}
535+
if ( valuex.startsWith(IP_Address_) ) {
536+
final String ip = valuex.substring(IP_Address_.length()).trim();
537+
return new GeneralName(GeneralName.iPAddress, ip);
544538
}
545539
if ( valuex.startsWith("other") ) { // otherName || othername
546-
final String other = valuex.substring(otherName_.length());
540+
final String other = valuex.substring(otherName_.length()).trim();
547541
return new GeneralName(GeneralName.otherName, other);
548542
}
549543
if ( valuex.startsWith("dir") ) { // dirName || dirname
550-
final String dir = valuex.substring(dirName_.length());
544+
final String dir = valuex.substring(dirName_.length()).trim();
551545
return new GeneralName(GeneralName.directoryName, dir);
552546
}
553547

554-
throw new IOException("could not parse SubjectAltName: " + valuex);
555-
548+
throw new IOException("could not parse SubjectAltName part: " + valuex);
556549
}
557550

558551
private DEROctetString parseSubjectKeyIdentifier(final ThreadContext context, final String oid, final String valuex) {

src/test/ruby/x509/test_x509ext.rb

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,38 @@ def test_subject_alt_name_sign_to_pem
151151
end
152152

153153
def test_subject_alt_name_sequence
154+
tests = [
155+
{
156+
:input => "email:[email protected],DNS:a.b.com,email:[email protected]",
157+
:output => "email:[email protected], DNS:a.b.com, email:[email protected]",
158+
:der => "0,\x06\x03U\x1D\x11\x04%0#\x81\v[email protected]\x82\aa.b.com\x81\v[email protected]",
159+
},
160+
{
161+
:input => "DNS:a.b.com, email:[email protected]",
162+
:der => "0\x1f\x06\x03U\x1d\x11\x04\x180\x16\x82\x07a.b.com\x81\x0b[email protected]",
163+
},
164+
{
165+
:input => "URI:https://a.b.com/, DNS:a.b.com",
166+
:der => "0$\x06\x03U\x1d\x11\x04\x1d0\x1b\x86\x10https://a.b.com/\x82\x07a.b.com",
167+
},
168+
{
169+
:input => "IP:1.2.3.4,IP: fe80::12:345:5678, email:[email protected], dirName: CN=John Doe+CN=Doe\\\\\\, John\\,O=Acme",
170+
:output => "IP:1.2.3.4, IP:fe80:0:0:0:0:12:345:5678, email:[email protected], DirName:CN=John Doe+CN=Doe\\, John,O=Acme",
171+
:der => "0f\x06\x03U\x1d\x11\x04_0]\x87\x04\x01\x02\x03\x04\x87\x10\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12\x03EVx\x81\x0b[email protected]\xa46041#0\x0f\x06\x03U\x04\x03\x0c\x08John Doe0\x10\x06\x03U\x04\x03\x0c\x09Doe, John1\x0d0\x0b\x06\x03U\x04\x0a\x0c\x04Acme"
172+
},
173+
{
174+
:input => "RID:1.3.6.1.3.100.200",
175+
:der => "0\x12\x06\x03U\x1d\x11\x04\x0b0\x09\x88\x07+\x06\x01\x03d\x81H",
176+
},
177+
]
178+
154179
extensions = OpenSSL::X509::ExtensionFactory.new
155-
ext = extensions.create_extension("subjectAltName", "email:[email protected],DNS:a.b.com,email:[email protected]")
156-
assert_equal 'subjectAltName', ext.oid
157-
assert_equal 'email:[email protected], DNS:a.b.com, email:[email protected]', ext.value
158-
mri_der = "0,\x06\x03U\x1D\x11\x04%0#\x81\v[email protected]\x82\aa.b.com\x81\v[email protected]"
159-
assert_equal mri_der, ext.to_der
180+
tests.each { |test|
181+
ext = extensions.create_extension("subjectAltName", test[:input])
182+
assert_equal 'subjectAltName', ext.oid
183+
assert_equal (test[:output] || test[:input]), ext.value
184+
assert_equal test[:der], ext.to_der
185+
}
160186
end
161187

162188
def subject_alt_name(domains)
@@ -165,4 +191,4 @@ def subject_alt_name(domains)
165191
end
166192
private :subject_alt_name
167193

168-
end
194+
end

0 commit comments

Comments
 (0)