# HG changeset patch # User coffeys # Date 1544031181 0 # Wed Dec 05 17:33:01 2018 +0000 # Node ID 56a6d4af2805951f00d5b936b1ec58c04c0e2110 # Parent 3bb8137f36a8ec8314ca22fa5e5996570d7e0969 8213952: Relax DNSName restriction as per RFC 1123 Reviewed-by: weijun, mullan, chegar diff --git a/src/share/classes/sun/security/x509/DNSName.java b/src/share/classes/sun/security/x509/DNSName.java --- a/src/share/classes/sun/security/x509/DNSName.java +++ b/src/share/classes/sun/security/x509/DNSName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,16 +34,17 @@ * This class implements the DNSName as required by the GeneralNames * ASN.1 object. *

- * [RFC2459] When the subjectAltName extension contains a domain name service + * [RFC5280] When the subjectAltName extension contains a domain name system * label, the domain name MUST be stored in the dNSName (an IA5String). - * The name MUST be in the "preferred name syntax," as specified by RFC - * 1034 [RFC 1034]. Note that while upper and lower case letters are - * allowed in domain names, no signifigance is attached to the case. In + * The name MUST be in the "preferred name syntax", as specified by + * Section 3.5 of [RFC1034] and as modified by Section 2.1 of + * [RFC1123]. Note that while uppercase and lowercase letters are + * allowed in domain names, no significance is attached to the case. In * addition, while the string " " is a legal domain name, subjectAltName - * extensions with a dNSName " " are not permitted. Finally, the use of - * the DNS representation for Internet mail addresses (wpolk.nist.gov - * instead of wpolk@nist.gov) is not permitted; such identities are to - * be encoded as rfc822Name. + * extensions with a dNSName of " " MUST NOT be used. Finally, the use + * of the DNS representation for Internet mail addresses + * (subscriber.example.com instead of subscriber@example.com) MUST NOT + * be used; such identities are to be encoded as rfc822Name. *

* @author Amit Kapoor * @author Hemma Prafullchandra @@ -51,9 +52,8 @@ public class DNSName implements GeneralNameInterface { private String name; - private static final String alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - private static final String digitsAndHyphen = "0123456789-"; - private static final String alphaDigitsAndHyphen = alpha + digitsAndHyphen; + private static final String alphaDigits = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; /** * Create the DNSName object from the passed encoded Der value. @@ -73,35 +73,38 @@ */ public DNSName(String name) throws IOException { if (name == null || name.length() == 0) - throw new IOException("DNS name must not be null"); - if (name.indexOf(' ') != -1) - throw new IOException("DNS names or NameConstraints with blank components are not permitted"); - if (name.charAt(0) == '.' || name.charAt(name.length() -1) == '.') - throw new IOException("DNS names or NameConstraints may not begin or end with a ."); - //Name will consist of label components separated by "." - //startIndex is the index of the first character of a component - //endIndex is the index of the last character of a component plus 1 - for (int endIndex,startIndex=0; startIndex < name.length(); startIndex = endIndex+1) { + throw new IOException("DNSName must not be null or empty"); + if (name.contains(" ")) + throw new IOException("DNSName with blank components is not permitted"); + if (name.startsWith(".") || name.endsWith(".")) + throw new IOException("DNSName may not begin or end with a ."); + /* + * Name will consist of label components separated by "." + * startIndex is the index of the first character of a component + * endIndex is the index of the last character of a component plus 1 + */ + for (int endIndex,startIndex = 0; startIndex < name.length(); startIndex = endIndex+1) { endIndex = name.indexOf('.', startIndex); if (endIndex < 0) { endIndex = name.length(); } - if ((endIndex-startIndex) < 1) - throw new IOException("DNSName SubjectAltNames with empty components are not permitted"); + if (endIndex - startIndex < 1) + throw new IOException("DNSName with empty components are not permitted"); - //DNSName components must begin with a letter A-Z or a-z - if (alpha.indexOf(name.charAt(startIndex)) < 0) - throw new IOException("DNSName components must begin with a letter"); + // RFC 1123: DNSName components must begin with a letter or digit + if (alphaDigits.indexOf(name.charAt(startIndex)) < 0) + throw new IOException("DNSName components must begin with a letter or digit"); //nonStartIndex: index for characters in the component beyond the first one for (int nonStartIndex=startIndex+1; nonStartIndex < endIndex; nonStartIndex++) { char x = name.charAt(nonStartIndex); - if ((alphaDigitsAndHyphen).indexOf(x) < 0) + if ((alphaDigits).indexOf(x) < 0 && x != '-') throw new IOException("DNSName components must consist of letters, digits, and hyphens"); } } this.name = name; } + /** * Return the type of the GeneralName. */ @@ -117,7 +120,7 @@ } /** - * Encode the DNS name into the DerOutputStream. + * Encode the DNSName into the DerOutputStream. * * @param out the DER stream to encode the DNSName to. * @exception IOException on encoding errors. @@ -137,7 +140,7 @@ * Compares this name with another, for equality. * * @return true iff the names are equivalent - * according to RFC2459. + * according to RFC5280. */ public boolean equals(Object obj) { if (this == obj) @@ -148,7 +151,7 @@ DNSName other = (DNSName)obj; - // RFC2459 mandates that these names are + // RFC5280 mandates that these names are // not case-sensitive return name.equalsIgnoreCase(other.name); } @@ -172,12 +175,14 @@ * . These results are used in checking NameConstraints during * certification path verification. *

- * RFC2459: DNS name restrictions are expressed as foo.bar.com. Any subdomain - * satisfies the name constraint. For example, www.foo.bar.com would - * satisfy the constraint but bigfoo.bar.com would not. + * RFC5280: DNS name restrictions are expressed as host.example.com. + * Any DNS name that can be constructed by simply adding zero or more + * labels to the left-hand side of the name satisfies the name constraint. + * For example, www.host.example.com would satisfy the constraint but + * host1.example.com would not. *

- * draft-ietf-pkix-new-part1-00.txt: DNS name restrictions are expressed as foo.bar.com. - * Any DNS name that + * draft-ietf-pkix-new-part1-00.txt: DNSName restrictions are expressed as foo.bar.com. + * Any DNSName that * can be constructed by simply adding to the left hand side of the name * satisfies the name constraint. For example, www.foo.bar.com would * satisfy the constraint but foo1.bar.com would not. diff --git a/src/share/classes/sun/security/x509/GeneralName.java b/src/share/classes/sun/security/x509/GeneralName.java --- a/src/share/classes/sun/security/x509/GeneralName.java +++ b/src/share/classes/sun/security/x509/GeneralName.java @@ -112,7 +112,7 @@ encName.resetTag(DerValue.tag_IA5String); name = new DNSName(encName); } else { - throw new IOException("Invalid encoding of DNS name"); + throw new IOException("Invalid encoding of DNSName"); } break; diff --git a/src/share/classes/sun/security/x509/RFC822Name.java b/src/share/classes/sun/security/x509/RFC822Name.java --- a/src/share/classes/sun/security/x509/RFC822Name.java +++ b/src/share/classes/sun/security/x509/RFC822Name.java @@ -246,7 +246,7 @@ subtree=subtree.substring(atNdx+1); } - /* count dots in dnsname, adding one if dnsname preceded by @ */ + /* count dots in DNSName, adding one if DNSName preceded by @ */ for (; subtree.lastIndexOf('.') >= 0; i++) { subtree=subtree.substring(0,subtree.lastIndexOf('.')); } diff --git a/src/share/classes/sun/security/x509/URIName.java b/src/share/classes/sun/security/x509/URIName.java --- a/src/share/classes/sun/security/x509/URIName.java +++ b/src/share/classes/sun/security/x509/URIName.java @@ -131,13 +131,13 @@ try { hostDNS = new DNSName(host); } catch (IOException ioe) { - // Not a valid DNS Name; see if it is a valid IPv4 + // Not a valid DNSName; see if it is a valid IPv4 // IPAddressName try { hostIP = new IPAddressName(host); } catch (Exception ioe2) { throw new IOException("invalid URI name (host " + - "portion is not a valid DNS name, IPv4 address," + + "portion is not a valid DNSName, IPv4 address," + " or IPv6 address):" + name); } } @@ -339,7 +339,7 @@ // If one (or both) is an IP address, only same type constraintType = NAME_SAME_TYPE; } else { - // Both host portions are DNS names. Are they domains? + // Both host portions are DNSNames. Are they domains? boolean thisDomain = (host.charAt(0) == '.'); boolean otherDomain = (otherHost.charAt(0) == '.'); DNSName otherDNS = (DNSName) otherHostObject; diff --git a/src/share/classes/sun/security/x509/X500Name.java b/src/share/classes/sun/security/x509/X500Name.java --- a/src/share/classes/sun/security/x509/X500Name.java +++ b/src/share/classes/sun/security/x509/X500Name.java @@ -1219,7 +1219,7 @@ */ /* - * OID for "DC=" domain component attributes, used with DNS names in DN + * OID for "DC=" domain component attributes, used with DNSNames in DN * format */ DOMAIN_COMPONENT_OID = diff --git a/test/sun/security/tools/keytool/KeyToolTest.java b/test/sun/security/tools/keytool/KeyToolTest.java --- a/test/sun/security/tools/keytool/KeyToolTest.java +++ b/test/sun/security/tools/keytool/KeyToolTest.java @@ -1028,6 +1028,7 @@ testOK("", pre+"san3 -ext san=dns:me.org"); testOK("", pre+"san4 -ext san=ip:192.168.0.1"); testOK("", pre+"san5 -ext san=oid:1.2.3.4"); + testOK("", pre+"san6 -ext san=dns:1abc.com"); //begin with digit testOK("", pre+"san235 -ext san=uri:http://me.org,dns:me.org,oid:1.2.3.4"); ks = loadStore("x.jks", "changeit", "JKS"); diff --git a/test/sun/security/x509/GeneralName/DNSNameTest.java b/test/sun/security/x509/GeneralName/DNSNameTest.java new file mode 100644 --- /dev/null +++ b/test/sun/security/x509/GeneralName/DNSNameTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @summary DNSName parsing tests + * @bug 8213952 + * @modules java.base/sun.security.x509 + * @run testng DNSNameTest + */ + +import java.io.IOException; +import sun.security.x509.DNSName; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.*; + +public class DNSNameTest { + @DataProvider(name = "goodNames") + public Object[][] goodNames() { + Object[][] data = { + {"abc.com"}, + {"ABC.COM"}, + {"a12.com"}, + {"a1b2c3.com"}, + {"1abc.com"}, + {"123.com"}, + {"abc.com-"}, // end with hyphen + {"a-b-c.com"}, // hyphens + }; + return data; + } + + @DataProvider(name = "badNames") + public Object[][] badNames() { + Object[][] data = { + {" 1abc.com"}, // begin with space + {"1abc.com "}, // end with space + {"1a bc.com "}, // no space allowed + {"-abc.com"}, // begin with hyphen + {"a..b"}, // .. + {".a"}, // begin with . + {"a."}, // end with . + {""}, // empty + {" "}, // space only + }; + return data; + } + + @Test(dataProvider = "goodNames") + public void testGoodDNSName(String dnsNameString) { + try { + DNSName dn = new DNSName(dnsNameString); + } catch (IOException e) { + fail("Unexpected IOException"); + } + } + + @Test(dataProvider = "badNames") + public void testBadDNSName(String dnsNameString) { + try { + DNSName dn = new DNSName(dnsNameString); + fail("IOException expected"); + } catch (IOException e) { + if (!e.getMessage().contains("DNSName")) + fail("Unexpeceted message: " + e); + } + } +}