Skip to content

Commit 32369c4

Browse files
committed
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 142c0a0 commit 32369c4

File tree

2 files changed

+62
-43
lines changed

2 files changed

+62
-43
lines changed

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

+30-37
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 one level 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].replace("\\,", ",").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

+32-6
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\\,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,O=Acme",
171+
:der => "0T\x06\x03U\x1d\x11\x04M0K\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]\xa4$0\"1\x110\x0f\x06\x03U\x04\x03\x0c\x08John Doe1\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)