|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
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; |
|
private String rfc1779Dn; |
|
private String rfc2253Dn; |
|
private String canonicalDn; |
|
private RDN[] names; |
|
private X500Principal x500Principal; |
|
private byte[] encoded; |
|
|
|
|
|
private volatile List<RDN> rdnList; |
|
private volatile List<AVA> allAvaList; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public X500Name(String dname) throws IOException { |
|
this(dname, Collections.<String, String>emptyMap()); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public X500Name(String dname, Map<String, String> keywordMap) |
|
throws IOException { |
|
parseDN(dname, keywordMap); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public X500Name(String commonName, String organizationUnit, |
|
String organizationName, String country) |
|
throws IOException { |
|
names = new RDN[4]; |
|
|
|
|
|
|
|
*/ |
|
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)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public X500Name(String commonName, String organizationUnit, |
|
String organizationName, String localityName, |
|
String stateName, String country) |
|
throws IOException { |
|
names = new RDN[6]; |
|
|
|
|
|
|
|
*/ |
|
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)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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"); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public X500Name(DerValue value) throws IOException { |
|
//Note that toDerInputStream uses only the buffer (data) and not |
|
|
|
this(value.toDerInputStream()); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public X500Name(DerInputStream in) throws IOException { |
|
parseDER(in); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public X500Name(byte[] name) throws IOException { |
|
DerInputStream in = new DerInputStream(name); |
|
parseDER(in); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public List<RDN> rdns() { |
|
List<RDN> list = rdnList; |
|
if (list == null) { |
|
list = Collections.unmodifiableList(Arrays.asList(names)); |
|
rdnList = list; |
|
} |
|
return list; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public int size() { |
|
return names.length; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public int avaSize() { |
|
return allAvas().size(); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean isEmpty() { |
|
int n = names.length; |
|
for (int i = 0; i < n; i++) { |
|
if (names[i].assertion.length != 0) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public int hashCode() { |
|
return getRFC2253CanonicalName().hashCode(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean equals(Object obj) { |
|
if (this == obj) { |
|
return true; |
|
} |
|
if (obj instanceof X500Name == false) { |
|
return false; |
|
} |
|
X500Name other = (X500Name)obj; |
|
|
|
if ((this.canonicalDn != null) && (other.canonicalDn != null)) { |
|
return this.canonicalDn.equals(other.canonicalDn); |
|
} |
|
|
|
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; |
|
} |
|
} |
|
|
|
String thisCanonical = this.getRFC2253CanonicalName(); |
|
String otherCanonical = other.getRFC2253CanonicalName(); |
|
return thisCanonical.equals(otherCanonical); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public int getType() { |
|
return (GeneralNameInterface.NAME_DIRECTORY); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getCountry() throws IOException { |
|
DerValue attr = findAttribute(countryName_oid); |
|
|
|
return getString(attr); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getOrganization() throws IOException { |
|
DerValue attr = findAttribute(orgName_oid); |
|
|
|
return getString(attr); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getOrganizationalUnit() throws IOException { |
|
DerValue attr = findAttribute(orgUnitName_oid); |
|
|
|
return getString(attr); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getCommonName() throws IOException { |
|
DerValue attr = findAttribute(commonName_oid); |
|
|
|
return getString(attr); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getLocality() throws IOException { |
|
DerValue attr = findAttribute(localityName_oid); |
|
|
|
return getString(attr); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getState() throws IOException { |
|
DerValue attr = findAttribute(stateName_oid); |
|
|
|
return getString(attr); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getDomain() throws IOException { |
|
DerValue attr = findAttribute(DOMAIN_COMPONENT_OID); |
|
|
|
return getString(attr); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getDNQualifier() throws IOException { |
|
DerValue attr = findAttribute(DNQUALIFIER_OID); |
|
|
|
return getString(attr); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getSurname() throws IOException { |
|
DerValue attr = findAttribute(SURNAME_OID); |
|
|
|
return getString(attr); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getGivenName() throws IOException { |
|
DerValue attr = findAttribute(GIVENNAME_OID); |
|
|
|
return getString(attr); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getInitials() throws IOException { |
|
DerValue attr = findAttribute(INITIALS_OID); |
|
|
|
return getString(attr); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getGeneration() throws IOException { |
|
DerValue attr = findAttribute(GENERATIONQUALIFIER_OID); |
|
|
|
return getString(attr); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getIP() throws IOException { |
|
DerValue attr = findAttribute(ipAddress_oid); |
|
|
|
return getString(attr); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String toString() { |
|
if (dn == null) { |
|
generateDN(); |
|
} |
|
return dn; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getRFC1779Name() { |
|
return getRFC1779Name(Collections.<String, String>emptyMap()); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getRFC1779Name(Map<String, String> oidMap) |
|
throws IllegalArgumentException { |
|
if (oidMap.isEmpty()) { |
|
|
|
if (rfc1779Dn != null) { |
|
return rfc1779Dn; |
|
} else { |
|
rfc1779Dn = generateRFC1779DN(oidMap); |
|
return rfc1779Dn; |
|
} |
|
} |
|
return generateRFC1779DN(oidMap); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getRFC2253Name() { |
|
return getRFC2253Name(Collections.<String, String>emptyMap()); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getRFC2253Name(Map<String, String> oidMap) { |
|
|
|
if (oidMap.isEmpty()) { |
|
if (rfc2253Dn != null) { |
|
return rfc2253Dn; |
|
} else { |
|
rfc2253Dn = generateRFC2253DN(oidMap); |
|
return rfc2253Dn; |
|
} |
|
} |
|
return generateRFC2253DN(oidMap); |
|
} |
|
|
|
private String generateRFC2253DN(Map<String, String> oidMap) { |
|
|
|
|
|
|
|
*/ |
|
if (names.length == 0) { |
|
return ""; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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() { |
|
|
|
if (canonicalDn != null) { |
|
return canonicalDn; |
|
} |
|
|
|
|
|
|
|
*/ |
|
if (names.length == 0) { |
|
canonicalDn = ""; |
|
return canonicalDn; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
StringJoiner sj = new StringJoiner(","); |
|
for (int i = names.length - 1; i >= 0; i--) { |
|
sj.add(names[i].toRFC2253String(true)); |
|
} |
|
canonicalDn = sj.toString(); |
|
return canonicalDn; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public String getName() { return toString(); } |
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
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]); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Deprecated |
|
public void emit(DerOutputStream out) throws IOException { |
|
encode(out); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public byte[] getEncoded() throws IOException { |
|
return getEncodedInternal().clone(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
if (rdnEnd >= 0 && quoteCount != 1 && |
|
!escaped(rdnEnd, searchOffset, dnString)) { |
|
|
|
|
|
|
|
*/ |
|
rdnString = dnString.substring(dnOffset, rdnEnd); |
|
|
|
|
|
RDN rdn = new RDN(rdnString, keywordMap); |
|
dnVector.add(rdn); |
|
|
|
|
|
dnOffset = rdnEnd + 1; |
|
|
|
|
|
quoteCount = 0; |
|
} |
|
|
|
searchOffset = rdnEnd + 1; |
|
nextComma = dnString.indexOf(',', searchOffset); |
|
nextSemiColon = dnString.indexOf(';', searchOffset); |
|
} |
|
|
|
|
|
rdnString = dnString.substring(dnOffset); |
|
RDN rdn = new RDN(rdnString, keywordMap); |
|
dnVector.add(rdn); |
|
|
|
|
|
|
|
|
|
*/ |
|
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) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
if (rdnEnd > 0 && !escaped(rdnEnd, searchOffset, dnString)) { |
|
|
|
|
|
|
|
*/ |
|
rdnString = dnString.substring(dnOffset, rdnEnd); |
|
|
|
|
|
RDN rdn = new RDN(rdnString, "RFC2253"); |
|
dnVector.add(rdn); |
|
|
|
|
|
dnOffset = rdnEnd + 1; |
|
} |
|
|
|
searchOffset = rdnEnd + 1; |
|
rdnEnd = dnString.indexOf(',', searchOffset); |
|
} |
|
|
|
|
|
rdnString = dnString.substring(dnOffset); |
|
RDN rdn = new RDN(rdnString, "RFC2253"); |
|
dnVector.add(rdn); |
|
|
|
|
|
|
|
|
|
*/ |
|
Collections.reverse(dnVector); |
|
names = dnVector.toArray(new RDN[dnVector.size()]); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
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--; |
|
while (rdnEnd >= searchOffset) { |
|
if (dnString.charAt(rdnEnd) == '\\') { |
|
count++; |
|
} |
|
rdnEnd--; |
|
} |
|
|
|
|
|
return (count % 2) != 0 ? true : false; |
|
|
|
} else { |
|
return false; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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(); |
|
} |
|
|
|
/****************************************************************/ |
|
|
|
|
|
|
|
|
|
*/ |
|
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>(); |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 { |
|
|
|
commonName_oid = intern(ObjectIdentifier.newInternal(commonName_data)); |
|
|
|
|
|
|
|
certificate serial number. */ |
|
SERIALNUMBER_OID = intern(ObjectIdentifier.newInternal(SERIALNUMBER_DATA)); |
|
|
|
|
|
countryName_oid = intern(ObjectIdentifier.newInternal(countryName_data)); |
|
|
|
|
|
localityName_oid = intern(ObjectIdentifier.newInternal(localityName_data)); |
|
|
|
|
|
orgName_oid = intern(ObjectIdentifier.newInternal(orgName_data)); |
|
|
|
|
|
orgUnitName_oid = intern(ObjectIdentifier.newInternal(orgUnitName_data)); |
|
|
|
|
|
stateName_oid = intern(ObjectIdentifier.newInternal(stateName_data)); |
|
|
|
|
|
streetAddress_oid = intern(ObjectIdentifier.newInternal(streetAddress_data)); |
|
|
|
|
|
title_oid = intern(ObjectIdentifier.newInternal(title_data)); |
|
|
|
|
|
disambiguating information.*/ |
|
DNQUALIFIER_OID = intern(ObjectIdentifier.newInternal(DNQUALIFIER_DATA)); |
|
|
|
|
|
SURNAME_OID = intern(ObjectIdentifier.newInternal(SURNAME_DATA)); |
|
|
|
|
|
GIVENNAME_OID = intern(ObjectIdentifier.newInternal(GIVENNAME_DATA)); |
|
|
|
|
|
INITIALS_OID = intern(ObjectIdentifier.newInternal(INITIALS_DATA)); |
|
|
|
|
|
GENERATIONQUALIFIER_OID = |
|
intern(ObjectIdentifier.newInternal(GENERATIONQUALIFIER_DATA)); |
|
|
|
/* |
|
* OIDs from other sources which show up in X.500 names we |
|
* expect to deal with often |
|
*/ |
|
|
|
ipAddress_oid = intern(ObjectIdentifier.newInternal(ipAddress_data)); |
|
|
|
/* |
|
* Domain component OID from RFC 1274, RFC 2247, RFC 5280 |
|
*/ |
|
|
|
|
|
|
|
|
|
*/ |
|
DOMAIN_COMPONENT_OID = |
|
intern(ObjectIdentifier.newInternal(DOMAIN_COMPONENT_DATA)); |
|
|
|
|
|
userid_oid = intern(ObjectIdentifier.newInternal(userid_data)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 { |
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public int subtreeDepth() throws UnsupportedOperationException { |
|
return names.length; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
int i=0; |
|
for (; i < minLen; i++) { |
|
if (!names[i].equals(other.names[i])) { |
|
if (i == 0) { |
|
return null; |
|
} else { |
|
break; |
|
} |
|
} |
|
} |
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static final Constructor<X500Principal> principalConstructor; |
|
|
|
|
|
|
|
*/ |
|
private static final Field principalField; |
|
|
|
|
|
|
|
|
|
*/ |
|
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); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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); |
|
} |
|
} |
|
|
|
} |