--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/x509/X500Name.java Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,1431 @@
+/*
+ * Copyright (c) 1996, 2014, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package sun.security.x509;
+
+import java.lang.reflect.*;
+import java.io.IOException;
+import java.security.PrivilegedExceptionAction;
+import java.security.AccessController;
+import java.security.Principal;
+import java.util.*;
+import java.util.StringJoiner;
+
+import sun.security.util.*;
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * Note: As of 1.4, the public class,
+ * javax.security.auth.x500.X500Principal,
+ * should be used when parsing, generating, and comparing X.500 DNs.
+ * This class contains other useful methods for checking name constraints
+ * and retrieving DNs by keyword.
+ *
+ * <p> X.500 names are used to identify entities, such as those which are
+ * identified by X.509 certificates. They are world-wide, hierarchical,
+ * and descriptive. Entities can be identified by attributes, and in
+ * some systems can be searched for according to those attributes.
+ * <p>
+ * The ASN.1 for this is:
+ * <pre>
+ * GeneralName ::= CHOICE {
+ * ....
+ * directoryName [4] Name,
+ * ....
+ * Name ::= CHOICE {
+ * RDNSequence }
+ *
+ * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+ *
+ * RelativeDistinguishedName ::=
+ * SET OF AttributeTypeAndValue
+ *
+ * AttributeTypeAndValue ::= SEQUENCE {
+ * type AttributeType,
+ * value AttributeValue }
+ *
+ * AttributeType ::= OBJECT IDENTIFIER
+ *
+ * AttributeValue ::= ANY DEFINED BY AttributeType
+ * ....
+ * DirectoryString ::= CHOICE {
+ * teletexString TeletexString (SIZE (1..MAX)),
+ * printableString PrintableString (SIZE (1..MAX)),
+ * universalString UniversalString (SIZE (1..MAX)),
+ * utf8String UTF8String (SIZE (1.. MAX)),
+ * bmpString BMPString (SIZE (1..MAX)) }
+ * </pre>
+ * <p>
+ * This specification requires only a subset of the name comparison
+ * functionality specified in the X.500 series of specifications. The
+ * requirements for conforming implementations are as follows:
+ * <ol TYPE=a>
+ * <li>attribute values encoded in different types (e.g.,
+ * PrintableString and BMPString) may be assumed to represent
+ * different strings;
+ *
+ * <li>attribute values in types other than PrintableString are case
+ * sensitive (this permits matching of attribute values as binary
+ * objects);
+ *
+ * <li>attribute values in PrintableString are not case sensitive
+ * (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
+ *
+ * <li>attribute values in PrintableString are compared after
+ * removing leading and trailing white space and converting internal
+ * substrings of one or more consecutive white space characters to a
+ * single space.
+ * </ol>
+ * <p>
+ * These name comparison rules permit a certificate user to validate
+ * certificates issued using languages or encodings unfamiliar to the
+ * certificate user.
+ * <p>
+ * In addition, implementations of this specification MAY use these
+ * comparison rules to process unfamiliar attribute types for name
+ * chaining. This allows implementations to process certificates with
+ * unfamiliar attributes in the issuer name.
+ * <p>
+ * Note that the comparison rules defined in the X.500 series of
+ * specifications indicate that the character sets used to encode data
+ * in distinguished names are irrelevant. The characters themselves are
+ * compared without regard to encoding. Implementations of the profile
+ * are permitted to use the comparison algorithm defined in the X.500
+ * series. Such an implementation will recognize a superset of name
+ * matches recognized by the algorithm specified above.
+ * <p>
+ * Note that instances of this class are immutable.
+ *
+ * @author David Brownell
+ * @author Amit Kapoor
+ * @author Hemma Prafullchandra
+ * @see GeneralName
+ * @see GeneralNames
+ * @see GeneralNameInterface
+ */
+
+public class X500Name implements GeneralNameInterface, Principal {
+
+ private String dn; // roughly RFC 1779 DN, or null
+ private String rfc1779Dn; // RFC 1779 compliant DN, or null
+ private String rfc2253Dn; // RFC 2253 DN, or null
+ private String canonicalDn; // canonical RFC 2253 DN or null
+ private RDN[] names; // RDNs (never null)
+ private X500Principal x500Principal;
+ private byte[] encoded;
+
+ // cached immutable list of the RDNs and all the AVAs
+ private volatile List<RDN> rdnList;
+ private volatile List<AVA> allAvaList;
+
+ /**
+ * Constructs a name from a conventionally formatted string, such
+ * as "CN=Dave, OU=JavaSoft, O=Sun Microsystems, C=US".
+ * (RFC 1779, 2253, or 4514 style).
+ *
+ * @param dname the X.500 Distinguished Name
+ */
+ public X500Name(String dname) throws IOException {
+ this(dname, Collections.<String, String>emptyMap());
+ }
+
+ /**
+ * Constructs a name from a conventionally formatted string, such
+ * as "CN=Dave, OU=JavaSoft, O=Sun Microsystems, C=US".
+ * (RFC 1779, 2253, or 4514 style).
+ *
+ * @param dname the X.500 Distinguished Name
+ * @param keywordMap an additional keyword/OID map
+ */
+ public X500Name(String dname, Map<String, String> keywordMap)
+ throws IOException {
+ parseDN(dname, keywordMap);
+ }
+
+ /**
+ * Constructs a name from a string formatted according to format.
+ * Currently, the formats DEFAULT and RFC2253 are supported.
+ * DEFAULT is the default format used by the X500Name(String)
+ * constructor. RFC2253 is the format strictly according to RFC2253
+ * without extensions.
+ *
+ * @param dname the X.500 Distinguished Name
+ * @param format the specified format of the String DN
+ */
+ public X500Name(String dname, String format) throws IOException {
+ if (dname == null) {
+ throw new NullPointerException("Name must not be null");
+ }
+ if (format.equalsIgnoreCase("RFC2253")) {
+ parseRFC2253DN(dname);
+ } else if (format.equalsIgnoreCase("DEFAULT")) {
+ parseDN(dname, Collections.<String, String>emptyMap());
+ } else {
+ throw new IOException("Unsupported format " + format);
+ }
+ }
+
+ /**
+ * Constructs a name from fields common in enterprise application
+ * environments.
+ *
+ * <P><EM><STRONG>NOTE:</STRONG> The behaviour when any of
+ * these strings contain characters outside the ASCII range
+ * is unspecified in currently relevant standards.</EM>
+ *
+ * @param commonName common name of a person, e.g. "Vivette Davis"
+ * @param organizationUnit small organization name, e.g. "Purchasing"
+ * @param organizationName large organization name, e.g. "Onizuka, Inc."
+ * @param country two letter country code, e.g. "CH"
+ */
+ public X500Name(String commonName, String organizationUnit,
+ String organizationName, String country)
+ throws IOException {
+ names = new RDN[4];
+ /*
+ * NOTE: it's only on output that little-endian
+ * ordering is used.
+ */
+ names[3] = new RDN(1);
+ names[3].assertion[0] = new AVA(commonName_oid,
+ new DerValue(commonName));
+ names[2] = new RDN(1);
+ names[2].assertion[0] = new AVA(orgUnitName_oid,
+ new DerValue(organizationUnit));
+ names[1] = new RDN(1);
+ names[1].assertion[0] = new AVA(orgName_oid,
+ new DerValue(organizationName));
+ names[0] = new RDN(1);
+ names[0].assertion[0] = new AVA(countryName_oid,
+ new DerValue(country));
+ }
+
+ /**
+ * Constructs a name from fields common in Internet application
+ * environments.
+ *
+ * <P><EM><STRONG>NOTE:</STRONG> The behaviour when any of
+ * these strings contain characters outside the ASCII range
+ * is unspecified in currently relevant standards.</EM>
+ *
+ * @param commonName common name of a person, e.g. "Vivette Davis"
+ * @param organizationUnit small organization name, e.g. "Purchasing"
+ * @param organizationName large organization name, e.g. "Onizuka, Inc."
+ * @param localityName locality (city) name, e.g. "Palo Alto"
+ * @param stateName state name, e.g. "California"
+ * @param country two letter country code, e.g. "CH"
+ */
+ public X500Name(String commonName, String organizationUnit,
+ String organizationName, String localityName,
+ String stateName, String country)
+ throws IOException {
+ names = new RDN[6];
+ /*
+ * NOTE: it's only on output that little-endian
+ * ordering is used.
+ */
+ names[5] = new RDN(1);
+ names[5].assertion[0] = new AVA(commonName_oid,
+ new DerValue(commonName));
+ names[4] = new RDN(1);
+ names[4].assertion[0] = new AVA(orgUnitName_oid,
+ new DerValue(organizationUnit));
+ names[3] = new RDN(1);
+ names[3].assertion[0] = new AVA(orgName_oid,
+ new DerValue(organizationName));
+ names[2] = new RDN(1);
+ names[2].assertion[0] = new AVA(localityName_oid,
+ new DerValue(localityName));
+ names[1] = new RDN(1);
+ names[1].assertion[0] = new AVA(stateName_oid,
+ new DerValue(stateName));
+ names[0] = new RDN(1);
+ names[0].assertion[0] = new AVA(countryName_oid,
+ new DerValue(country));
+ }
+
+ /**
+ * Constructs a name from an array of relative distinguished names
+ *
+ * @param rdnArray array of relative distinguished names
+ * @throws IOException on error
+ */
+ public X500Name(RDN[] rdnArray) throws IOException {
+ if (rdnArray == null) {
+ names = new RDN[0];
+ } else {
+ names = rdnArray.clone();
+ for (int i = 0; i < names.length; i++) {
+ if (names[i] == null) {
+ throw new IOException("Cannot create an X500Name");
+ }
+ }
+ }
+ }
+
+ /**
+ * Constructs a name from an ASN.1 encoded value. The encoding
+ * of the name in the stream uses DER (a BER/1 subset).
+ *
+ * @param value a DER-encoded value holding an X.500 name.
+ */
+ public X500Name(DerValue value) throws IOException {
+ //Note that toDerInputStream uses only the buffer (data) and not
+ //the tag, so an empty SEQUENCE (OF) will yield an empty DerInputStream
+ this(value.toDerInputStream());
+ }
+
+ /**
+ * Constructs a name from an ASN.1 encoded input stream. The encoding
+ * of the name in the stream uses DER (a BER/1 subset).
+ *
+ * @param in DER-encoded data holding an X.500 name.
+ */
+ public X500Name(DerInputStream in) throws IOException {
+ parseDER(in);
+ }
+
+ /**
+ * Constructs a name from an ASN.1 encoded byte array.
+ *
+ * @param name DER-encoded byte array holding an X.500 name.
+ */
+ public X500Name(byte[] name) throws IOException {
+ DerInputStream in = new DerInputStream(name);
+ parseDER(in);
+ }
+
+ /**
+ * Return an immutable List of all RDNs in this X500Name.
+ */
+ public List<RDN> rdns() {
+ List<RDN> list = rdnList;
+ if (list == null) {
+ list = Collections.unmodifiableList(Arrays.asList(names));
+ rdnList = list;
+ }
+ return list;
+ }
+
+ /**
+ * Return the number of RDNs in this X500Name.
+ */
+ public int size() {
+ return names.length;
+ }
+
+ /**
+ * Return an immutable List of the AVAs contained in all the
+ * RDNs of this X500Name.
+ */
+ public List<AVA> allAvas() {
+ List<AVA> list = allAvaList;
+ if (list == null) {
+ list = new ArrayList<>();
+ for (int i = 0; i < names.length; i++) {
+ list.addAll(names[i].avas());
+ }
+ list = Collections.unmodifiableList(list);
+ allAvaList = list;
+ }
+ return list;
+ }
+
+ /**
+ * Return the total number of AVAs contained in all the RDNs of
+ * this X500Name.
+ */
+ public int avaSize() {
+ return allAvas().size();
+ }
+
+ /**
+ * Return whether this X500Name is empty. An X500Name is not empty
+ * if it has at least one RDN containing at least one AVA.
+ */
+ public boolean isEmpty() {
+ int n = names.length;
+ for (int i = 0; i < n; i++) {
+ if (names[i].assertion.length != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Calculates a hash code value for the object. Objects
+ * which are equal will also have the same hashcode.
+ */
+ public int hashCode() {
+ return getRFC2253CanonicalName().hashCode();
+ }
+
+ /**
+ * Compares this name with another, for equality.
+ *
+ * @return true iff the names are identical.
+ */
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof X500Name == false) {
+ return false;
+ }
+ X500Name other = (X500Name)obj;
+ // if we already have the canonical forms, compare now
+ if ((this.canonicalDn != null) && (other.canonicalDn != null)) {
+ return this.canonicalDn.equals(other.canonicalDn);
+ }
+ // quick check that number of RDNs and AVAs match before canonicalizing
+ int n = this.names.length;
+ if (n != other.names.length) {
+ return false;
+ }
+ for (int i = 0; i < n; i++) {
+ RDN r1 = this.names[i];
+ RDN r2 = other.names[i];
+ if (r1.assertion.length != r2.assertion.length) {
+ return false;
+ }
+ }
+ // definite check via canonical form
+ String thisCanonical = this.getRFC2253CanonicalName();
+ String otherCanonical = other.getRFC2253CanonicalName();
+ return thisCanonical.equals(otherCanonical);
+ }
+
+ /*
+ * Returns the name component as a Java string, regardless of its
+ * encoding restrictions.
+ */
+ private String getString(DerValue attribute) throws IOException {
+ if (attribute == null)
+ return null;
+ String value = attribute.getAsString();
+
+ if (value == null)
+ throw new IOException("not a DER string encoding, "
+ + attribute.tag);
+ else
+ return value;
+ }
+
+ /**
+ * Return type of GeneralName.
+ */
+ public int getType() {
+ return (GeneralNameInterface.NAME_DIRECTORY);
+ }
+
+ /**
+ * Returns a "Country" name component. If more than one
+ * such attribute exists, the topmost one is returned.
+ *
+ * @return "C=" component of the name, if any.
+ */
+ public String getCountry() throws IOException {
+ DerValue attr = findAttribute(countryName_oid);
+
+ return getString(attr);
+ }
+
+
+ /**
+ * Returns an "Organization" name component. If more than
+ * one such attribute exists, the topmost one is returned.
+ *
+ * @return "O=" component of the name, if any.
+ */
+ public String getOrganization() throws IOException {
+ DerValue attr = findAttribute(orgName_oid);
+
+ return getString(attr);
+ }
+
+
+ /**
+ * Returns an "Organizational Unit" name component. If more
+ * than one such attribute exists, the topmost one is returned.
+ *
+ * @return "OU=" component of the name, if any.
+ */
+ public String getOrganizationalUnit() throws IOException {
+ DerValue attr = findAttribute(orgUnitName_oid);
+
+ return getString(attr);
+ }
+
+
+ /**
+ * Returns a "Common Name" component. If more than one such
+ * attribute exists, the topmost one is returned.
+ *
+ * @return "CN=" component of the name, if any.
+ */
+ public String getCommonName() throws IOException {
+ DerValue attr = findAttribute(commonName_oid);
+
+ return getString(attr);
+ }
+
+
+ /**
+ * Returns a "Locality" name component. If more than one
+ * such component exists, the topmost one is returned.
+ *
+ * @return "L=" component of the name, if any.
+ */
+ public String getLocality() throws IOException {
+ DerValue attr = findAttribute(localityName_oid);
+
+ return getString(attr);
+ }
+
+ /**
+ * Returns a "State" name component. If more than one
+ * such component exists, the topmost one is returned.
+ *
+ * @return "S=" component of the name, if any.
+ */
+ public String getState() throws IOException {
+ DerValue attr = findAttribute(stateName_oid);
+
+ return getString(attr);
+ }
+
+ /**
+ * Returns a "Domain" name component. If more than one
+ * such component exists, the topmost one is returned.
+ *
+ * @return "DC=" component of the name, if any.
+ */
+ public String getDomain() throws IOException {
+ DerValue attr = findAttribute(DOMAIN_COMPONENT_OID);
+
+ return getString(attr);
+ }
+
+ /**
+ * Returns a "DN Qualifier" name component. If more than one
+ * such component exists, the topmost one is returned.
+ *
+ * @return "DNQ=" component of the name, if any.
+ */
+ public String getDNQualifier() throws IOException {
+ DerValue attr = findAttribute(DNQUALIFIER_OID);
+
+ return getString(attr);
+ }
+
+ /**
+ * Returns a "Surname" name component. If more than one
+ * such component exists, the topmost one is returned.
+ *
+ * @return "SURNAME=" component of the name, if any.
+ */
+ public String getSurname() throws IOException {
+ DerValue attr = findAttribute(SURNAME_OID);
+
+ return getString(attr);
+ }
+
+ /**
+ * Returns a "Given Name" name component. If more than one
+ * such component exists, the topmost one is returned.
+ *
+ * @return "GIVENNAME=" component of the name, if any.
+ */
+ public String getGivenName() throws IOException {
+ DerValue attr = findAttribute(GIVENNAME_OID);
+
+ return getString(attr);
+ }
+
+ /**
+ * Returns an "Initials" name component. If more than one
+ * such component exists, the topmost one is returned.
+ *
+ * @return "INITIALS=" component of the name, if any.
+ */
+ public String getInitials() throws IOException {
+ DerValue attr = findAttribute(INITIALS_OID);
+
+ return getString(attr);
+ }
+
+ /**
+ * Returns a "Generation Qualifier" name component. If more than one
+ * such component exists, the topmost one is returned.
+ *
+ * @return "GENERATION=" component of the name, if any.
+ */
+ public String getGeneration() throws IOException {
+ DerValue attr = findAttribute(GENERATIONQUALIFIER_OID);
+
+ return getString(attr);
+ }
+
+ /**
+ * Returns an "IP address" name component. If more than one
+ * such component exists, the topmost one is returned.
+ *
+ * @return "IP=" component of the name, if any.
+ */
+ public String getIP() throws IOException {
+ DerValue attr = findAttribute(ipAddress_oid);
+
+ return getString(attr);
+ }
+
+ /**
+ * Returns a string form of the X.500 distinguished name.
+ * The format of the string is from RFC 1779. The returned string
+ * may contain non-standardised keywords for more readability
+ * (keywords from RFCs 1779, 2253, and 5280).
+ */
+ public String toString() {
+ if (dn == null) {
+ generateDN();
+ }
+ return dn;
+ }
+
+ /**
+ * Returns a string form of the X.500 distinguished name
+ * using the algorithm defined in RFC 1779. Only standard attribute type
+ * keywords defined in RFC 1779 are emitted.
+ */
+ public String getRFC1779Name() {
+ return getRFC1779Name(Collections.<String, String>emptyMap());
+ }
+
+ /**
+ * Returns a string form of the X.500 distinguished name
+ * using the algorithm defined in RFC 1779. Attribute type
+ * keywords defined in RFC 1779 are emitted, as well as additional
+ * keywords contained in the OID/keyword map.
+ */
+ public String getRFC1779Name(Map<String, String> oidMap)
+ throws IllegalArgumentException {
+ if (oidMap.isEmpty()) {
+ // return cached result
+ if (rfc1779Dn != null) {
+ return rfc1779Dn;
+ } else {
+ rfc1779Dn = generateRFC1779DN(oidMap);
+ return rfc1779Dn;
+ }
+ }
+ return generateRFC1779DN(oidMap);
+ }
+
+ /**
+ * Returns a string form of the X.500 distinguished name
+ * using the algorithm defined in RFC 2253. Only standard attribute type
+ * keywords defined in RFC 2253 are emitted.
+ */
+ public String getRFC2253Name() {
+ return getRFC2253Name(Collections.<String, String>emptyMap());
+ }
+
+ /**
+ * Returns a string form of the X.500 distinguished name
+ * using the algorithm defined in RFC 2253. Attribute type
+ * keywords defined in RFC 2253 are emitted, as well as additional
+ * keywords contained in the OID/keyword map.
+ */
+ public String getRFC2253Name(Map<String, String> oidMap) {
+ /* check for and return cached name */
+ if (oidMap.isEmpty()) {
+ if (rfc2253Dn != null) {
+ return rfc2253Dn;
+ } else {
+ rfc2253Dn = generateRFC2253DN(oidMap);
+ return rfc2253Dn;
+ }
+ }
+ return generateRFC2253DN(oidMap);
+ }
+
+ private String generateRFC2253DN(Map<String, String> oidMap) {
+ /*
+ * Section 2.1 : if the RDNSequence is an empty sequence
+ * the result is the empty or zero length string.
+ */
+ if (names.length == 0) {
+ return "";
+ }
+
+ /*
+ * 2.1 (continued) : Otherwise, the output consists of the string
+ * encodings of each RelativeDistinguishedName in the RDNSequence
+ * (according to 2.2), starting with the last element of the sequence
+ * and moving backwards toward the first.
+ *
+ * The encodings of adjoining RelativeDistinguishedNames are separated
+ * by a comma character (',' ASCII 44).
+ */
+ StringJoiner sj = new StringJoiner(",");
+ for (int i = names.length - 1; i >= 0; i--) {
+ sj.add(names[i].toRFC2253String(oidMap));
+ }
+ return sj.toString();
+ }
+
+ public String getRFC2253CanonicalName() {
+ /* check for and return cached name */
+ if (canonicalDn != null) {
+ return canonicalDn;
+ }
+ /*
+ * Section 2.1 : if the RDNSequence is an empty sequence
+ * the result is the empty or zero length string.
+ */
+ if (names.length == 0) {
+ canonicalDn = "";
+ return canonicalDn;
+ }
+
+ /*
+ * 2.1 (continued) : Otherwise, the output consists of the string
+ * encodings of each RelativeDistinguishedName in the RDNSequence
+ * (according to 2.2), starting with the last element of the sequence
+ * and moving backwards toward the first.
+ *
+ * The encodings of adjoining RelativeDistinguishedNames are separated
+ * by a comma character (',' ASCII 44).
+ */
+ StringJoiner sj = new StringJoiner(",");
+ for (int i = names.length - 1; i >= 0; i--) {
+ sj.add(names[i].toRFC2253String(true));
+ }
+ canonicalDn = sj.toString();
+ return canonicalDn;
+ }
+
+ /**
+ * Returns the value of toString(). This call is needed to
+ * implement the java.security.Principal interface.
+ */
+ public String getName() { return toString(); }
+
+ /**
+ * Find the first instance of this attribute in a "top down"
+ * search of all the attributes in the name.
+ */
+ private DerValue findAttribute(ObjectIdentifier attribute) {
+ if (names != null) {
+ for (int i = 0; i < names.length; i++) {
+ DerValue value = names[i].findAttribute(attribute);
+ if (value != null) {
+ return value;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Find the most specific ("last") attribute of the given
+ * type.
+ */
+ public DerValue findMostSpecificAttribute(ObjectIdentifier attribute) {
+ if (names != null) {
+ for (int i = names.length - 1; i >= 0; i--) {
+ DerValue value = names[i].findAttribute(attribute);
+ if (value != null) {
+ return value;
+ }
+ }
+ }
+ return null;
+ }
+
+ /****************************************************************/
+
+ private void parseDER(DerInputStream in) throws IOException {
+ //
+ // X.500 names are a "SEQUENCE OF" RDNs, which means zero or
+ // more and order matters. We scan them in order, which
+ // conventionally is big-endian.
+ //
+ DerValue[] nameseq = null;
+ byte[] derBytes = in.toByteArray();
+
+ try {
+ nameseq = in.getSequence(5);
+ } catch (IOException ioe) {
+ if (derBytes == null) {
+ nameseq = null;
+ } else {
+ DerValue derVal = new DerValue(DerValue.tag_Sequence,
+ derBytes);
+ derBytes = derVal.toByteArray();
+ nameseq = new DerInputStream(derBytes).getSequence(5);
+ }
+ }
+
+ if (nameseq == null) {
+ names = new RDN[0];
+ } else {
+ names = new RDN[nameseq.length];
+ for (int i = 0; i < nameseq.length; i++) {
+ names[i] = new RDN(nameseq[i]);
+ }
+ }
+ }
+
+ /**
+ * Encodes the name in DER-encoded form.
+ *
+ * @deprecated Use encode() instead
+ * @param out where to put the DER-encoded X.500 name
+ */
+ @Deprecated
+ public void emit(DerOutputStream out) throws IOException {
+ encode(out);
+ }
+
+ /**
+ * Encodes the name in DER-encoded form.
+ *
+ * @param out where to put the DER-encoded X.500 name
+ */
+ public void encode(DerOutputStream out) throws IOException {
+ DerOutputStream tmp = new DerOutputStream();
+ for (int i = 0; i < names.length; i++) {
+ names[i].encode(tmp);
+ }
+ out.write(DerValue.tag_Sequence, tmp);
+ }
+
+ /**
+ * Returned the encoding as an uncloned byte array. Callers must
+ * guarantee that they neither modify it not expose it to untrusted
+ * code.
+ */
+ public byte[] getEncodedInternal() throws IOException {
+ if (encoded == null) {
+ DerOutputStream out = new DerOutputStream();
+ DerOutputStream tmp = new DerOutputStream();
+ for (int i = 0; i < names.length; i++) {
+ names[i].encode(tmp);
+ }
+ out.write(DerValue.tag_Sequence, tmp);
+ encoded = out.toByteArray();
+ }
+ return encoded;
+ }
+
+ /**
+ * Gets the name in DER-encoded form.
+ *
+ * @return the DER encoded byte array of this name.
+ */
+ public byte[] getEncoded() throws IOException {
+ return getEncodedInternal().clone();
+ }
+
+ /*
+ * Parses a Distinguished Name (DN) in printable representation.
+ *
+ * According to RFC 1779, RDNs in a DN are separated by comma.
+ * The following examples show both methods of quoting a comma, so that it
+ * is not considered a separator:
+ *
+ * O="Sue, Grabbit and Runn" or
+ * O=Sue\, Grabbit and Runn
+ *
+ * This method can parse RFC 1779, 2253 or 4514 DNs and non-standard 5280
+ * keywords. Additional keywords can be specified in the keyword/OID map.
+ */
+ private void parseDN(String input, Map<String, String> keywordMap)
+ throws IOException {
+ if (input == null || input.length() == 0) {
+ names = new RDN[0];
+ return;
+ }
+
+ List<RDN> dnVector = new ArrayList<>();
+ int dnOffset = 0;
+ int rdnEnd;
+ String rdnString;
+ int quoteCount = 0;
+
+ String dnString = input;
+
+ int searchOffset = 0;
+ int nextComma = dnString.indexOf(',');
+ int nextSemiColon = dnString.indexOf(';');
+ while (nextComma >=0 || nextSemiColon >=0) {
+
+ if (nextSemiColon < 0) {
+ rdnEnd = nextComma;
+ } else if (nextComma < 0) {
+ rdnEnd = nextSemiColon;
+ } else {
+ rdnEnd = Math.min(nextComma, nextSemiColon);
+ }
+ quoteCount += countQuotes(dnString, searchOffset, rdnEnd);
+
+ /*
+ * We have encountered an RDN delimiter (comma or a semicolon).
+ * If the comma or semicolon in the RDN under consideration is
+ * preceded by a backslash (escape), or by a double quote, it
+ * is part of the RDN. Otherwise, it is used as a separator, to
+ * delimit the RDN under consideration from any subsequent RDNs.
+ */
+ if (rdnEnd >= 0 && quoteCount != 1 &&
+ !escaped(rdnEnd, searchOffset, dnString)) {
+
+ /*
+ * Comma/semicolon is a separator
+ */
+ rdnString = dnString.substring(dnOffset, rdnEnd);
+
+ // Parse RDN, and store it in vector
+ RDN rdn = new RDN(rdnString, keywordMap);
+ dnVector.add(rdn);
+
+ // Increase the offset
+ dnOffset = rdnEnd + 1;
+
+ // Set quote counter back to zero
+ quoteCount = 0;
+ }
+
+ searchOffset = rdnEnd + 1;
+ nextComma = dnString.indexOf(',', searchOffset);
+ nextSemiColon = dnString.indexOf(';', searchOffset);
+ }
+
+ // Parse last or only RDN, and store it in vector
+ rdnString = dnString.substring(dnOffset);
+ RDN rdn = new RDN(rdnString, keywordMap);
+ dnVector.add(rdn);
+
+ /*
+ * Store the vector elements as an array of RDNs
+ * NOTE: It's only on output that little-endian ordering is used.
+ */
+ Collections.reverse(dnVector);
+ names = dnVector.toArray(new RDN[dnVector.size()]);
+ }
+
+ private void parseRFC2253DN(String dnString) throws IOException {
+ if (dnString.length() == 0) {
+ names = new RDN[0];
+ return;
+ }
+
+ List<RDN> dnVector = new ArrayList<>();
+ int dnOffset = 0;
+ String rdnString;
+ int searchOffset = 0;
+ int rdnEnd = dnString.indexOf(',');
+ while (rdnEnd >=0) {
+ /*
+ * We have encountered an RDN delimiter (comma).
+ * If the comma in the RDN under consideration is
+ * preceded by a backslash (escape), it
+ * is part of the RDN. Otherwise, it is used as a separator, to
+ * delimit the RDN under consideration from any subsequent RDNs.
+ */
+ if (rdnEnd > 0 && !escaped(rdnEnd, searchOffset, dnString)) {
+
+ /*
+ * Comma is a separator
+ */
+ rdnString = dnString.substring(dnOffset, rdnEnd);
+
+ // Parse RDN, and store it in vector
+ RDN rdn = new RDN(rdnString, "RFC2253");
+ dnVector.add(rdn);
+
+ // Increase the offset
+ dnOffset = rdnEnd + 1;
+ }
+
+ searchOffset = rdnEnd + 1;
+ rdnEnd = dnString.indexOf(',', searchOffset);
+ }
+
+ // Parse last or only RDN, and store it in vector
+ rdnString = dnString.substring(dnOffset);
+ RDN rdn = new RDN(rdnString, "RFC2253");
+ dnVector.add(rdn);
+
+ /*
+ * Store the vector elements as an array of RDNs
+ * NOTE: It's only on output that little-endian ordering is used.
+ */
+ Collections.reverse(dnVector);
+ names = dnVector.toArray(new RDN[dnVector.size()]);
+ }
+
+ /*
+ * Counts double quotes in string.
+ * Escaped quotes are ignored.
+ */
+ static int countQuotes(String string, int from, int to) {
+ int count = 0;
+
+ for (int i = from; i < to; i++) {
+ if ((string.charAt(i) == '"' && i == from) ||
+ (string.charAt(i) == '"' && string.charAt(i-1) != '\\')) {
+ count++;
+ }
+ }
+
+ return count;
+ }
+
+ private static boolean escaped
+ (int rdnEnd, int searchOffset, String dnString) {
+
+ if (rdnEnd == 1 && dnString.charAt(rdnEnd - 1) == '\\') {
+
+ // case 1:
+ // \,
+
+ return true;
+
+ } else if (rdnEnd > 1 && dnString.charAt(rdnEnd - 1) == '\\' &&
+ dnString.charAt(rdnEnd - 2) != '\\') {
+
+ // case 2:
+ // foo\,
+
+ return true;
+
+ } else if (rdnEnd > 1 && dnString.charAt(rdnEnd - 1) == '\\' &&
+ dnString.charAt(rdnEnd - 2) == '\\') {
+
+ // case 3:
+ // foo\\\\\,
+
+ int count = 0;
+ rdnEnd--; // back up to last backSlash
+ while (rdnEnd >= searchOffset) {
+ if (dnString.charAt(rdnEnd) == '\\') {
+ count++; // count consecutive backslashes
+ }
+ rdnEnd--;
+ }
+
+ // if count is odd, then rdnEnd is escaped
+ return (count % 2) != 0 ? true : false;
+
+ } else {
+ return false;
+ }
+ }
+
+ /*
+ * Dump the printable form of a distinguished name. Each relative
+ * name is separated from the next by a ",", and assertions in the
+ * relative names have "label=value" syntax.
+ *
+ * Uses RFC 1779 syntax (i.e. little-endian, comma separators)
+ */
+ private void generateDN() {
+ if (names.length == 1) {
+ dn = names[0].toString();
+ return;
+ }
+
+ if (names == null) {
+ dn = "";
+ return;
+ }
+
+ StringJoiner sj = new StringJoiner(", ");
+ for (int i = names.length - 1; i >= 0; i--) {
+ sj.add(names[i].toString());
+ }
+ dn = sj.toString();
+ }
+
+ /*
+ * Dump the printable form of a distinguished name. Each relative
+ * name is separated from the next by a ",", and assertions in the
+ * relative names have "label=value" syntax.
+ *
+ * Uses RFC 1779 syntax (i.e. little-endian, comma separators)
+ * Valid keywords from RFC 1779 are used. Additional keywords can be
+ * specified in the OID/keyword map.
+ */
+ private String generateRFC1779DN(Map<String, String> oidMap) {
+ if (names.length == 1) {
+ return names[0].toRFC1779String(oidMap);
+ }
+
+ if (names == null) {
+ return "";
+ }
+
+ StringJoiner sj = new StringJoiner(", ");
+ for (int i = names.length - 1; i >= 0; i--) {
+ sj.add(names[i].toRFC1779String(oidMap));
+ }
+ return sj.toString();
+ }
+
+ /****************************************************************/
+
+ /*
+ * Maybe return a preallocated OID, to reduce storage costs
+ * and speed recognition of common X.500 attributes.
+ */
+ static ObjectIdentifier intern(ObjectIdentifier oid) {
+ ObjectIdentifier interned = internedOIDs.putIfAbsent(oid, oid);
+ return (interned == null) ? oid : interned;
+ }
+
+ private static final Map<ObjectIdentifier,ObjectIdentifier> internedOIDs
+ = new HashMap<ObjectIdentifier,ObjectIdentifier>();
+
+ /*
+ * Selected OIDs from X.520
+ * Includes all those specified in RFC 5280 as MUST or SHOULD
+ * be recognized
+ */
+ private static final int[] commonName_data = { 2, 5, 4, 3 };
+ private static final int[] SURNAME_DATA = { 2, 5, 4, 4 };
+ private static final int[] SERIALNUMBER_DATA = { 2, 5, 4, 5 };
+ private static final int[] countryName_data = { 2, 5, 4, 6 };
+ private static final int[] localityName_data = { 2, 5, 4, 7 };
+ private static final int[] stateName_data = { 2, 5, 4, 8 };
+ private static final int[] streetAddress_data = { 2, 5, 4, 9 };
+ private static final int[] orgName_data = { 2, 5, 4, 10 };
+ private static final int[] orgUnitName_data = { 2, 5, 4, 11 };
+ private static final int[] title_data = { 2, 5, 4, 12 };
+ private static final int[] GIVENNAME_DATA = { 2, 5, 4, 42 };
+ private static final int[] INITIALS_DATA = { 2, 5, 4, 43 };
+ private static final int[] GENERATIONQUALIFIER_DATA = { 2, 5, 4, 44 };
+ private static final int[] DNQUALIFIER_DATA = { 2, 5, 4, 46 };
+
+ private static final int[] ipAddress_data = { 1, 3, 6, 1, 4, 1, 42, 2, 11, 2, 1 };
+ private static final int[] DOMAIN_COMPONENT_DATA =
+ { 0, 9, 2342, 19200300, 100, 1, 25 };
+ private static final int[] userid_data =
+ { 0, 9, 2342, 19200300, 100, 1, 1 };
+
+
+ public static final ObjectIdentifier commonName_oid;
+ public static final ObjectIdentifier countryName_oid;
+ public static final ObjectIdentifier localityName_oid;
+ public static final ObjectIdentifier orgName_oid;
+ public static final ObjectIdentifier orgUnitName_oid;
+ public static final ObjectIdentifier stateName_oid;
+ public static final ObjectIdentifier streetAddress_oid;
+ public static final ObjectIdentifier title_oid;
+ public static final ObjectIdentifier DNQUALIFIER_OID;
+ public static final ObjectIdentifier SURNAME_OID;
+ public static final ObjectIdentifier GIVENNAME_OID;
+ public static final ObjectIdentifier INITIALS_OID;
+ public static final ObjectIdentifier GENERATIONQUALIFIER_OID;
+ public static final ObjectIdentifier ipAddress_oid;
+ public static final ObjectIdentifier DOMAIN_COMPONENT_OID;
+ public static final ObjectIdentifier userid_oid;
+ public static final ObjectIdentifier SERIALNUMBER_OID;
+
+ static {
+ /** OID for the "CN=" attribute, denoting a person's common name. */
+ commonName_oid = intern(ObjectIdentifier.newInternal(commonName_data));
+
+ /** OID for the "SERIALNUMBER=" attribute, denoting a serial number for.
+ a name. Do not confuse with PKCS#9 issuerAndSerialNumber or the
+ certificate serial number. */
+ SERIALNUMBER_OID = intern(ObjectIdentifier.newInternal(SERIALNUMBER_DATA));
+
+ /** OID for the "C=" attribute, denoting a country. */
+ countryName_oid = intern(ObjectIdentifier.newInternal(countryName_data));
+
+ /** OID for the "L=" attribute, denoting a locality (such as a city) */
+ localityName_oid = intern(ObjectIdentifier.newInternal(localityName_data));
+
+ /** OID for the "O=" attribute, denoting an organization name */
+ orgName_oid = intern(ObjectIdentifier.newInternal(orgName_data));
+
+ /** OID for the "OU=" attribute, denoting an organizational unit name */
+ orgUnitName_oid = intern(ObjectIdentifier.newInternal(orgUnitName_data));
+
+ /** OID for the "S=" attribute, denoting a state (such as Delaware) */
+ stateName_oid = intern(ObjectIdentifier.newInternal(stateName_data));
+
+ /** OID for the "STREET=" attribute, denoting a street address. */
+ streetAddress_oid = intern(ObjectIdentifier.newInternal(streetAddress_data));
+
+ /** OID for the "T=" attribute, denoting a person's title. */
+ title_oid = intern(ObjectIdentifier.newInternal(title_data));
+
+ /** OID for the "DNQUALIFIER=" or "DNQ=" attribute, denoting DN
+ disambiguating information.*/
+ DNQUALIFIER_OID = intern(ObjectIdentifier.newInternal(DNQUALIFIER_DATA));
+
+ /** OID for the "SURNAME=" attribute, denoting a person's surname.*/
+ SURNAME_OID = intern(ObjectIdentifier.newInternal(SURNAME_DATA));
+
+ /** OID for the "GIVENNAME=" attribute, denoting a person's given name.*/
+ GIVENNAME_OID = intern(ObjectIdentifier.newInternal(GIVENNAME_DATA));
+
+ /** OID for the "INITIALS=" attribute, denoting a person's initials.*/
+ INITIALS_OID = intern(ObjectIdentifier.newInternal(INITIALS_DATA));
+
+ /** OID for the "GENERATION=" attribute, denoting Jr., II, etc.*/
+ GENERATIONQUALIFIER_OID =
+ intern(ObjectIdentifier.newInternal(GENERATIONQUALIFIER_DATA));
+
+ /*
+ * OIDs from other sources which show up in X.500 names we
+ * expect to deal with often
+ */
+ /** OID for "IP=" IP address attributes, used with SKIP. */
+ ipAddress_oid = intern(ObjectIdentifier.newInternal(ipAddress_data));
+
+ /*
+ * Domain component OID from RFC 1274, RFC 2247, RFC 5280
+ */
+
+ /*
+ * OID for "DC=" domain component attributes, used with DNS names in DN
+ * format
+ */
+ DOMAIN_COMPONENT_OID =
+ intern(ObjectIdentifier.newInternal(DOMAIN_COMPONENT_DATA));
+
+ /** OID for "UID=" denoting a user id, defined in RFCs 1274 & 2798. */
+ userid_oid = intern(ObjectIdentifier.newInternal(userid_data));
+ }
+
+ /**
+ * Return constraint type:<ul>
+ * <li>NAME_DIFF_TYPE = -1: input name is different type from this name
+ * (i.e. does not constrain)
+ * <li>NAME_MATCH = 0: input name matches this name
+ * <li>NAME_NARROWS = 1: input name narrows this name
+ * <li>NAME_WIDENS = 2: input name widens this name
+ * <li>NAME_SAME_TYPE = 3: input name does not match or narrow this name,
+ * but is same type.
+ * </ul>
+ * These results are used in checking NameConstraints during
+ * certification path verification.
+ *
+ * @param inputName to be checked for being constrained
+ * @return constraint type above
+ * @throws UnsupportedOperationException if name is not exact match, but
+ * narrowing and widening are not supported for this name type.
+ */
+ public int constrains(GeneralNameInterface inputName)
+ throws UnsupportedOperationException {
+ int constraintType;
+ if (inputName == null) {
+ constraintType = NAME_DIFF_TYPE;
+ } else if (inputName.getType() != NAME_DIRECTORY) {
+ constraintType = NAME_DIFF_TYPE;
+ } else { // type == NAME_DIRECTORY
+ X500Name inputX500 = (X500Name)inputName;
+ if (inputX500.equals(this)) {
+ constraintType = NAME_MATCH;
+ } else if (inputX500.names.length == 0) {
+ constraintType = NAME_WIDENS;
+ } else if (this.names.length == 0) {
+ constraintType = NAME_NARROWS;
+ } else if (inputX500.isWithinSubtree(this)) {
+ constraintType = NAME_NARROWS;
+ } else if (isWithinSubtree(inputX500)) {
+ constraintType = NAME_WIDENS;
+ } else {
+ constraintType = NAME_SAME_TYPE;
+ }
+ }
+ return constraintType;
+ }
+
+ /**
+ * Compares this name with another and determines if
+ * it is within the subtree of the other. Useful for
+ * checking against the name constraints extension.
+ *
+ * @return true iff this name is within the subtree of other.
+ */
+ private boolean isWithinSubtree(X500Name other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null) {
+ return false;
+ }
+ if (other.names.length == 0) {
+ return true;
+ }
+ if (this.names.length == 0) {
+ return false;
+ }
+ if (names.length < other.names.length) {
+ return false;
+ }
+ for (int i = 0; i < other.names.length; i++) {
+ if (!names[i].equals(other.names[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return subtree depth of this name for purposes of determining
+ * NameConstraints minimum and maximum bounds and for calculating
+ * path lengths in name subtrees.
+ *
+ * @return distance of name from root
+ * @throws UnsupportedOperationException if not supported for this name type
+ */
+ public int subtreeDepth() throws UnsupportedOperationException {
+ return names.length;
+ }
+
+ /**
+ * Return lowest common ancestor of this name and other name
+ *
+ * @param other another X500Name
+ * @return X500Name of lowest common ancestor; null if none
+ */
+ public X500Name commonAncestor(X500Name other) {
+
+ if (other == null) {
+ return null;
+ }
+ int otherLen = other.names.length;
+ int thisLen = this.names.length;
+ if (thisLen == 0 || otherLen == 0) {
+ return null;
+ }
+ int minLen = (thisLen < otherLen) ? thisLen: otherLen;
+
+ //Compare names from highest RDN down the naming tree
+ //Note that these are stored in RDN[0]...
+ int i=0;
+ for (; i < minLen; i++) {
+ if (!names[i].equals(other.names[i])) {
+ if (i == 0) {
+ return null;
+ } else {
+ break;
+ }
+ }
+ }
+
+ //Copy matching RDNs into new RDN array
+ RDN[] ancestor = new RDN[i];
+ for (int j=0; j < i; j++) {
+ ancestor[j] = names[j];
+ }
+
+ X500Name commonAncestor = null;
+ try {
+ commonAncestor = new X500Name(ancestor);
+ } catch (IOException ioe) {
+ return null;
+ }
+ return commonAncestor;
+ }
+
+ /**
+ * Constructor object for use by asX500Principal().
+ */
+ private static final Constructor<X500Principal> principalConstructor;
+
+ /**
+ * Field object for use by asX500Name().
+ */
+ private static final Field principalField;
+
+ /**
+ * Retrieve the Constructor and Field we need for reflective access
+ * and make them accessible.
+ */
+ static {
+ PrivilegedExceptionAction<Object[]> pa =
+ new PrivilegedExceptionAction<>() {
+ public Object[] run() throws Exception {
+ Class<X500Principal> pClass = X500Principal.class;
+ Class<?>[] args = new Class<?>[] { X500Name.class };
+ Constructor<X500Principal> cons = pClass.getDeclaredConstructor(args);
+ cons.setAccessible(true);
+ Field field = pClass.getDeclaredField("thisX500Name");
+ field.setAccessible(true);
+ return new Object[] {cons, field};
+ }
+ };
+ try {
+ Object[] result = AccessController.doPrivileged(pa);
+ @SuppressWarnings("unchecked")
+ Constructor<X500Principal> constr =
+ (Constructor<X500Principal>)result[0];
+ principalConstructor = constr;
+ principalField = (Field)result[1];
+ } catch (Exception e) {
+ throw new InternalError("Could not obtain X500Principal access", e);
+ }
+ }
+
+ /**
+ * Get an X500Principal backed by this X500Name.
+ *
+ * Note that we are using privileged reflection to access the hidden
+ * package private constructor in X500Principal.
+ */
+ public X500Principal asX500Principal() {
+ if (x500Principal == null) {
+ try {
+ Object[] args = new Object[] {this};
+ x500Principal = principalConstructor.newInstance(args);
+ } catch (Exception e) {
+ throw new RuntimeException("Unexpected exception", e);
+ }
+ }
+ return x500Principal;
+ }
+
+ /**
+ * Get the X500Name contained in the given X500Principal.
+ *
+ * Note that the X500Name is retrieved using reflection.
+ */
+ public static X500Name asX500Name(X500Principal p) {
+ try {
+ X500Name name = (X500Name)principalField.get(p);
+ name.x500Principal = p;
+ return name;
+ } catch (Exception e) {
+ throw new RuntimeException("Unexpected exception", e);
+ }
+ }
+
+}