Skip to content

Commit 656b3cf

Browse files
author
Alexey Bakhtin
committed
8186143: keytool -ext option doesn't accept wildcards for DNS subject alternative names
Reviewed-by: yan Backport-of: 0c9f8e472f1d452e97b3cbd50b9e5d700fffcd15
1 parent eca7a30 commit 656b3cf

File tree

3 files changed

+109
-13
lines changed

3 files changed

+109
-13
lines changed

jdk/src/share/classes/sun/security/tools/keytool/Main.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3982,9 +3982,10 @@ private static int oneOf(String s, String... list) throws Exception {
39823982
* Create a GeneralName object from known types
39833983
* @param t one of 5 known types
39843984
* @param v value
3985+
* @param exttype X.509 extension type
39853986
* @return which one
39863987
*/
3987-
private GeneralName createGeneralName(String t, String v)
3988+
private GeneralName createGeneralName(String t, String v, int exttype)
39883989
throws Exception {
39893990
GeneralNameInterface gn;
39903991
int p = oneOf(t, "EMAIL", "URI", "DNS", "IP", "OID");
@@ -3995,7 +3996,14 @@ private GeneralName createGeneralName(String t, String v)
39953996
switch (p) {
39963997
case 0: gn = new RFC822Name(v); break;
39973998
case 1: gn = new URIName(v); break;
3998-
case 2: gn = new DNSName(v); break;
3999+
case 2:
4000+
if (exttype == 3) {
4001+
// Allow wildcard only for SAN extension
4002+
gn = new DNSName(v, true);
4003+
} else {
4004+
gn = new DNSName(v);
4005+
}
4006+
break;
39994007
case 3: gn = new IPAddressName(v); break;
40004008
default: gn = new OIDName(v); break; //4
40014009
}
@@ -4249,7 +4257,7 @@ private CertificateExtensions createV3Extensions(
42494257
}
42504258
String t = item.substring(0, colonpos);
42514259
String v = item.substring(colonpos+1);
4252-
gnames.add(createGeneralName(t, v));
4260+
gnames.add(createGeneralName(t, v, exttype));
42534261
}
42544262
if (exttype == 3) {
42554263
ext.set(SubjectAlternativeNameExtension.NAME,
@@ -4305,7 +4313,7 @@ private CertificateExtensions createV3Extensions(
43054313
oid = new ObjectIdentifier("1.3.6.1.5.5.7.48." + p);
43064314
}
43074315
accessDescriptions.add(new AccessDescription(
4308-
oid, createGeneralName(t, v)));
4316+
oid, createGeneralName(t, v, exttype)));
43094317
}
43104318
if (exttype == 5) {
43114319
ext.set(SubjectInfoAccessExtension.NAME,
@@ -4330,7 +4338,7 @@ private CertificateExtensions createV3Extensions(
43304338
}
43314339
String t = item.substring(0, colonpos);
43324340
String v = item.substring(colonpos+1);
4333-
gnames.add(createGeneralName(t, v));
4341+
gnames.add(createGeneralName(t, v, exttype));
43344342
}
43354343
ext.set(CRLDistributionPointsExtension.NAME,
43364344
new CRLDistributionPointsExtension(

jdk/src/share/classes/sun/security/x509/DNSName.java

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -69,9 +69,10 @@ public DNSName(DerValue derValue) throws IOException {
6969
* Create the DNSName object with the specified name.
7070
*
7171
* @param name the DNSName.
72-
* @throws IOException if the name is not a valid DNSName subjectAltName
72+
* @param allowWildcard the flag for wildcard checking.
73+
* @throws IOException if the name is not a valid DNSName
7374
*/
74-
public DNSName(String name) throws IOException {
75+
public DNSName(String name, boolean allowWildcard) throws IOException {
7576
if (name == null || name.length() == 0)
7677
throw new IOException("DNSName must not be null or empty");
7778
if (name.contains(" "))
@@ -91,9 +92,26 @@ public DNSName(String name) throws IOException {
9192
if (endIndex - startIndex < 1)
9293
throw new IOException("DNSName with empty components are not permitted");
9394

94-
// RFC 1123: DNSName components must begin with a letter or digit
95-
if (alphaDigits.indexOf(name.charAt(startIndex)) < 0)
96-
throw new IOException("DNSName components must begin with a letter or digit");
95+
if (allowWildcard) {
96+
// RFC 1123: DNSName components must begin with a letter or digit
97+
// or RFC 4592: the first component of a DNSName can have only a wildcard
98+
// character * (asterisk), i.e. *.example.com. Asterisks at other components
99+
// will not be allowed as a wildcard.
100+
if (alphaDigits.indexOf(name.charAt(startIndex)) < 0) {
101+
// Checking to make sure the wildcard only appears in the first component,
102+
// and it has to be at least 3-char long with the form of *.[alphaDigit]
103+
if ((name.length() < 3) || (name.indexOf('*', 0) != 0) ||
104+
(name.charAt(startIndex+1) != '.') ||
105+
(alphaDigits.indexOf(name.charAt(startIndex+2)) < 0))
106+
throw new IOException("DNSName components must begin with a letter, digit, "
107+
+ "or the first component can have only a wildcard character *");
108+
}
109+
} else {
110+
// RFC 1123: DNSName components must begin with a letter or digit
111+
if (alphaDigits.indexOf(name.charAt(startIndex)) < 0)
112+
throw new IOException("DNSName components must begin with a letter or digit");
113+
}
114+
97115
//nonStartIndex: index for characters in the component beyond the first one
98116
for (int nonStartIndex=startIndex+1; nonStartIndex < endIndex; nonStartIndex++) {
99117
char x = name.charAt(nonStartIndex);
@@ -104,6 +122,15 @@ public DNSName(String name) throws IOException {
104122
this.name = name;
105123
}
106124

125+
/**
126+
* Create the DNSName object with the specified name.
127+
*
128+
* @param name the DNSName.
129+
* @throws IOException if the name is not a valid DNSName
130+
*/
131+
public DNSName(String name) throws IOException {
132+
this(name, false);
133+
}
107134

108135
/**
109136
* Return the type of the GeneralName.

jdk/test/sun/security/x509/GeneralName/DNSNameTest.java

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -24,7 +24,7 @@
2424
/**
2525
* @test
2626
* @summary DNSName parsing tests
27-
* @bug 8213952
27+
* @bug 8213952 8186143
2828
* @modules java.base/sun.security.x509
2929
* @run testng DNSNameTest
3030
*/
@@ -53,6 +53,23 @@ public Object[][] goodNames() {
5353
return data;
5454
}
5555

56+
@DataProvider(name = "goodSanNames")
57+
public Object[][] goodSanNames() {
58+
Object[][] data = {
59+
{"abc.com"},
60+
{"ABC.COM"},
61+
{"a12.com"},
62+
{"a1b2c3.com"},
63+
{"1abc.com"},
64+
{"123.com"},
65+
{"abc.com-"}, // end with hyphen
66+
{"a-b-c.com"}, // hyphens
67+
{"*.domain.com"}, // wildcard in 1st level subdomain
68+
{"*.com"},
69+
};
70+
return data;
71+
}
72+
5673
@DataProvider(name = "badNames")
5774
public Object[][] badNames() {
5875
Object[][] data = {
@@ -65,10 +82,34 @@ public Object[][] badNames() {
6582
{"a."}, // end with .
6683
{""}, // empty
6784
{" "}, // space only
85+
{"*.domain.com"}, // wildcard not allowed
86+
{"a*.com"}, // only allow letter, digit, or hyphen
87+
};
88+
return data;
89+
}
90+
91+
@DataProvider(name = "badSanNames")
92+
public Object[][] badSanNames() {
93+
Object[][] data = {
94+
{" 1abc.com"}, // begin with space
95+
{"1abc.com "}, // end with space
96+
{"1a bc.com "}, // no space allowed
97+
{"-abc.com"}, // begin with hyphen
98+
{"a..b"}, // ..
99+
{".a"}, // begin with .
100+
{"a."}, // end with .
101+
{""}, // empty
102+
{" "}, // space only
103+
{"*"}, // wildcard only
104+
{"*a.com"}, // partial wildcard disallowed
105+
{"abc.*.com"}, // wildcard not allowed in 2nd level
106+
{"*.*.domain.com"}, // double wildcard not allowed
107+
{"a*.com"}, // only allow letter, digit, or hyphen
68108
};
69109
return data;
70110
}
71111

112+
72113
@Test(dataProvider = "goodNames")
73114
public void testGoodDNSName(String dnsNameString) {
74115
try {
@@ -78,6 +119,15 @@ public void testGoodDNSName(String dnsNameString) {
78119
}
79120
}
80121

122+
@Test(dataProvider = "goodSanNames")
123+
public void testGoodSanDNSName(String dnsNameString) {
124+
try {
125+
DNSName dn = new DNSName(dnsNameString, true);
126+
} catch (IOException e) {
127+
fail("Unexpected IOException");
128+
}
129+
}
130+
81131
@Test(dataProvider = "badNames")
82132
public void testBadDNSName(String dnsNameString) {
83133
try {
@@ -88,4 +138,15 @@ public void testBadDNSName(String dnsNameString) {
88138
fail("Unexpeceted message: " + e);
89139
}
90140
}
141+
142+
@Test(dataProvider = "badSanNames")
143+
public void testBadSanDNSName(String dnsNameString) {
144+
try {
145+
DNSName dn = new DNSName(dnsNameString, true);
146+
fail("IOException expected");
147+
} catch (IOException e) {
148+
if (!e.getMessage().contains("DNSName"))
149+
fail("Unexpeceted message: " + e);
150+
}
151+
}
91152
}

0 commit comments

Comments
 (0)