/** *********************************************************************** * * * CESeCore: CE Security Core * * * * This software is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or any later version. * * * * See terms of license at gnu.org. * * * ************************************************************************ */ package org.ejbca.util; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.MalformedURLException; import java.net.URL; import java.security.NoSuchProviderException; import java.security.Security; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.regex.Pattern; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.x500.AttributeTypeAndValue; import org.bouncycastle.asn1.x500.RDN; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x500.X500NameBuilder; import org.bouncycastle.asn1.x500.X500NameStyle; import org.bouncycastle.asn1.x500.style.IETFUtils; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.X509NameTokenizer; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.encoders.Base64; /** * Tools to handle common certificate operations. * * @version $Id$ */ public abstract class CertTools { //private static final Logger log = Logger.getLogger(CertTools.class); public static final String EMAIL = "rfc822name"; public static final String EMAIL1 = "email"; public static final String EMAIL2 = "EmailAddress"; public static final String EMAIL3 = "E"; public static final String DNS = "dNSName"; public static final String URI = "uniformResourceIdentifier"; public static final String URI1 = "uri"; public static final String URI2 = "uniformResourceId"; public static final String IPADDR = "iPAddress"; public static final String DIRECTORYNAME = "directoryName"; public static final String REGISTEREDID = "registeredID"; public static final String XMPPADDR = "xmppAddr"; public static final String SRVNAME = "srvName"; public static final String FASCN = "fascN"; /** * Kerberos altName for smart card logon */ public static final String KRB5PRINCIPAL = "krb5principal"; /** * OID for Kerberos altName for smart card logon */ public static final String KRB5PRINCIPAL_OBJECTID = "1.3.6.1.5.2.2"; /** * Microsoft altName for windows smart card logon */ public static final String UPN = "upn"; /** * ObjectID for upn altName for windows smart card logon */ public static final String UPN_OBJECTID = "1.3.6.1.4.1.311.20.2.3"; /** * ObjectID for XmppAddr, rfc6120#section-13.7.1.4 */ public static final String XMPPADDR_OBJECTID = "1.3.6.1.5.5.7.8.5"; /** * ObjectID for srvName, rfc4985 */ public static final String SRVNAME_OBJECTID = "1.3.6.1.5.5.7.8.7"; public static final String PERMANENTIDENTIFIER = "permanentIdentifier"; public static final String PERMANENTIDENTIFIER_OBJECTID = "1.3.6.1.5.5.7.8.3"; public static final String PERMANENTIDENTIFIER_SEP = "/"; public static final String FASCN_OBJECTID = "2.16.840.1.101.3.6.6"; /** * Microsoft altName for windows domain controller guid */ public static final String GUID = "guid"; /** * ObjectID for upn altName for windows domain controller guid */ public static final String GUID_OBJECTID = "1.3.6.1.4.1.311.25.1"; /** * ObjectID for Microsoft Encrypted File System Certificates extended key * usage */ public static final String EFS_OBJECTID = "1.3.6.1.4.1.311.10.3.4"; /** * ObjectID for Microsoft Encrypted File System Recovery Certificates * extended key usage */ public static final String EFSR_OBJECTID = "1.3.6.1.4.1.311.10.3.4.1"; /** * ObjectID for Microsoft Signer of documents extended key usage */ public static final String MS_DOCUMENT_SIGNING_OBJECTID = "1.3.6.1.4.1.311.10.3.12"; public static final String PRECERT_POISON_EXTENSION_OID = "1.3.6.1.4.1.11129.2.4.3"; /** * Object id id-pkix */ public static final String id_pkix = "1.3.6.1.5.5.7"; /** * Object id id-kp */ public static final String id_kp = id_pkix + ".3"; /** * Object id id-pda */ public static final String id_pda = id_pkix + ".9"; /** * Object id id-pda-dateOfBirth DateOfBirth ::= GeneralizedTime */ public static final String id_pda_dateOfBirth = id_pda + ".1"; /** * Object id id-pda-placeOfBirth PlaceOfBirth ::= DirectoryString */ public static final String id_pda_placeOfBirth = id_pda + ".2"; /** * Object id id-pda-gender Gender ::= PrintableString (SIZE(1)) -- "M", "F", * "m" or "f" */ public static final String id_pda_gender = id_pda + ".3"; /** * Object id id-pda-countryOfCitizenship CountryOfCitizenship ::= * PrintableString (SIZE (2)) -- ISO 3166 Country Code */ public static final String id_pda_countryOfCitizenship = id_pda + ".4"; /** * Object id id-pda-countryOfResidence CountryOfResidence ::= * PrintableString (SIZE (2)) -- ISO 3166 Country Code */ public static final String id_pda_countryOfResidence = id_pda + ".5"; /** * OID used for creating MS Templates certificate extension */ public static final String OID_MSTEMPLATE = "1.3.6.1.4.1.311.20.2"; /** * extended key usage OID Intel AMT (out of band) network management */ public static final String Intel_amt = "2.16.840.1.113741.1.2.3"; /** * Object ID for CT (Certificate Transparency) specific extensions */ public static final String id_ct_redacted_domains = "1.3.6.1.4.1.11129.2.4.6"; private static final String[] EMAILIDS = {EMAIL, EMAIL1, EMAIL2, EMAIL3}; private static final Pattern UNESCAPE_FIELD_REGEX = Pattern.compile("\\\\([,+\"\\\\<>; ])"); public static final String BEGIN_CERTIFICATE_REQUEST = "-----BEGIN CERTIFICATE REQUEST-----"; public static final String END_CERTIFICATE_REQUEST = "-----END CERTIFICATE REQUEST-----"; public static final String BEGIN_KEYTOOL_CERTIFICATE_REQUEST = "-----BEGIN NEW CERTIFICATE REQUEST-----"; public static final String END_KEYTOOL_CERTIFICATE_REQUEST = "-----END NEW CERTIFICATE REQUEST-----"; public static final String BEGIN_CERTIFICATE = "-----BEGIN CERTIFICATE-----"; public static final String END_CERTIFICATE = "-----END CERTIFICATE-----"; public static final String BEGIN_CERTIFICATE_WITH_NL = "-----BEGIN CERTIFICATE-----\n"; public static final String END_CERTIFICATE_WITH_NL = "\n-----END CERTIFICATE-----\n"; public static final String BEGIN_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----"; public static final String END_PUBLIC_KEY = "-----END PUBLIC KEY-----"; public static final String BEGIN_PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----"; public static final String END_PRIVATE_KEY = "-----END PRIVATE KEY-----"; public static final String BEGIN_X509_CRL_KEY = "-----BEGIN X509 CRL-----"; public static final String END_X509_CRL_KEY = "-----END X509 CRL-----"; public static final String BEGIN_PKCS7 = "-----BEGIN PKCS7-----"; public static final String END_PKCS7 = "-----END PKCS7-----"; public static List getCertsFromPEM(InputStream certstream, Class returnType) throws CertificateParsingException { final ArrayList ret = new ArrayList<>(); String beginKeyTrust = "-----BEGIN TRUSTED CERTIFICATE-----"; String endKeyTrust = "-----END TRUSTED CERTIFICATE-----"; try (final BufferedReader bufRdr = new BufferedReader(new InputStreamReader(new SecurityFilterInputStream(certstream)))) { while (bufRdr.ready()) { final ByteArrayOutputStream ostr = new ByteArrayOutputStream(); final PrintStream opstr = new PrintStream(ostr); String temp; while ((temp = bufRdr.readLine()) != null && !(temp.equals(CertTools.BEGIN_CERTIFICATE) || temp.equals(beginKeyTrust))) { continue; } if (temp == null) { if (ret.isEmpty()) { // There was no certificate in the file throw new CertificateParsingException("Error in " + certstream.toString() + ", missing " + CertTools.BEGIN_CERTIFICATE + " boundary"); } else { // There were certificates, but some blank lines or something in the end // anyhow, the file has ended so we can break here. break; } } while ((temp = bufRdr.readLine()) != null && !(temp.equals(CertTools.END_CERTIFICATE) || temp.equals(endKeyTrust))) { opstr.print(temp); } if (temp == null) { throw new IllegalArgumentException("Error in " + certstream.toString() + ", missing " + CertTools.END_CERTIFICATE + " boundary"); } opstr.close(); byte[] certbuf = Base64.decode(ostr.toByteArray()); ostr.close(); // Phweeew, were done, now decode the cert from file back to Certificate object T cert = getCertfromByteArray(certbuf, returnType); ret.add(cert); } } catch (IOException e) { throw new IllegalStateException("Exception caught when attempting to read stream, see underlying IOException", e); } return ret; } /** * Returns a certificate in PEM-format. * * @param cacert a Certificate to convert to PEM * @return byte array containing PEM certificate * @throws CertificateEncodingException if an encoding error occurred */ public static String getPemFromCertificate(Certificate cacert) throws CertificateEncodingException { byte[] enccert = cacert.getEncoded(); byte[] b64cert = Base64.encode(enccert); String out = BEGIN_CERTIFICATE_WITH_NL; out += new String(b64cert); out += END_CERTIFICATE_WITH_NL; return out; } /** * @return a CRL in PEM-format as a byte array. */ public static byte[] getPEMFromCrl(byte[] crlBytes) { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (final PrintStream printStream = new PrintStream(baos)) { writeAsPemEncoded(printStream, crlBytes, BEGIN_X509_CRL_KEY, END_X509_CRL_KEY); } return baos.toByteArray(); } /** * @return a PublicKey in PEM-format as a byte array. */ public static byte[] getPEMFromPublicKey(final byte[] publicKeyBytes) { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (final PrintStream printStream = new PrintStream(baos)) { writeAsPemEncoded(printStream, publicKeyBytes, BEGIN_PUBLIC_KEY, END_PUBLIC_KEY); } return baos.toByteArray(); } public static byte[] getPEMFromPrivateKey(final byte[] privateKeyBytes) { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (final PrintStream printStream = new PrintStream(baos)) { writeAsPemEncoded(printStream, privateKeyBytes, BEGIN_PRIVATE_KEY, END_PRIVATE_KEY); } return baos.toByteArray(); } /** * @return a PublicKey in PEM-format as a byte array. */ public static byte[] getPEMFromCertificateRequest(final byte[] certificateRequestBytes) { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (final PrintStream printStream = new PrintStream(baos)) { writeAsPemEncoded(printStream, certificateRequestBytes, BEGIN_CERTIFICATE_REQUEST, END_CERTIFICATE_REQUEST); } return baos.toByteArray(); } /** * Generates PEM from binary pkcs#7 data. * * @param pkcs7Binary pkcs#7 binary data * @return a pkcs#7 PEM encoded */ public static byte[] getPemFromPkcs7(final byte[] pkcs7Binary) { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (final PrintStream printStream = new PrintStream(baos)) { writeAsPemEncoded(printStream, pkcs7Binary, BEGIN_PKCS7, END_PKCS7); } return baos.toByteArray(); } /** * Write the supplied bytes to the printstream as Base64 using beginKey and * endKey around it. */ private static void writeAsPemEncoded(PrintStream printStream, byte[] unencodedData, String beginKey, String endKey) { printStream.println(beginKey); printStream.println(new String(Base64.encode(unencodedData))); printStream.println(endKey); } @Deprecated public static Certificate getCertfromByteArray(byte[] cert, String provider) throws CertificateParsingException { return getCertfromByteArray(cert, provider, Certificate.class); } public static T getCertfromByteArray(byte[] cert, String provider, Class returnType) throws CertificateParsingException { String prov = provider; if (provider == null) { prov = BouncyCastleProvider.PROVIDER_NAME; } if (returnType.equals(X509Certificate.class)) { return returnType.cast(parseX509Certificate(prov, cert)); } else { //Let's guess... try { return returnType.cast(parseX509Certificate(prov, cert)); } catch (CertificateParsingException e) { e.printStackTrace(); throw new CertificateParsingException("No certificate could be parsed from byte array. See debug logs for details."); } } } @Deprecated public static Certificate getCertfromByteArray(byte[] cert) throws CertificateParsingException { return getCertfromByteArray(cert, Certificate.class); } public static T getCertfromByteArray(byte[] cert, Class returnType) throws CertificateParsingException { return getCertfromByteArray(cert, BouncyCastleProvider.PROVIDER_NAME, returnType); } private static X509Certificate parseX509Certificate(String provider, byte[] cert) throws CertificateParsingException { final CertificateFactory cf = getCertificateFactory(provider); X509Certificate result; try { result = (X509Certificate) cf.generateCertificate(new SecurityFilterInputStream(new ByteArrayInputStream(cert))); } catch (CertificateException e) { throw new CertificateParsingException("Could not parse byte array as X509Certificate." + e.getCause().getMessage(), e); } if (result != null) { return result; } else { throw new CertificateParsingException("Could not parse byte array as X509Certificate."); } } public static CertificateFactory getCertificateFactory(final String provider) { final String prov; if (provider == null) { prov = BouncyCastleProvider.PROVIDER_NAME; } else { prov = provider; } if (BouncyCastleProvider.PROVIDER_NAME.equals(prov)) { Security.addProvider(new BouncyCastleProvider()); } try { return CertificateFactory.getInstance("X.509", prov); } catch (NoSuchProviderException nspe) { nspe.printStackTrace(); } catch (CertificateException ce) { ce.printStackTrace(); } return null; } public static CertificateFactory getCertificateFactory() { return getCertificateFactory(BouncyCastleProvider.PROVIDER_NAME); } public static List getCrlDistributionPoints(final X509Certificate x509cert) { return getCrlDistributionPoints(x509cert, false); } private static List getCrlDistributionPoints(final ASN1Primitive extensionValue, final boolean onlyfirst) { final ArrayList cdps = new ArrayList<>(); final ASN1Sequence crlDistributionPoints = ASN1Sequence.getInstance(extensionValue); for (int i = 0; i < crlDistributionPoints.size(); i++) { final ASN1Sequence distributionPoint = ASN1Sequence.getInstance(crlDistributionPoints.getObjectAt(i)); for (int j = 0; j < distributionPoint.size(); j++) { final ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(distributionPoint.getObjectAt(j)); if (tagged.getTagNo() == 0) { String url = getStringFromGeneralNames(tagged.getObject()); if (url != null) { try { new URL(url); // Syntax check cdps.add(url); } catch (MalformedURLException e) { e.printStackTrace(); } } if (onlyfirst) { return cdps; // returning only the first URL } } } } return cdps; } private static List getCrlDistributionPoints(final X509Certificate x509cert, final boolean onlyfirst) { final ASN1Primitive extensionValue = getExtensionValue(x509cert, Extension.cRLDistributionPoints.getId()); if (extensionValue == null) { return Collections.emptyList(); } return getCrlDistributionPoints(extensionValue, onlyfirst); } public static URL getCrlDistributionPoint(final Certificate certificate) { try { if (certificate instanceof X509Certificate) { final X509Certificate x509cert = (X509Certificate) certificate; final Collection cdps = getCrlDistributionPoints(x509cert, true); if (!cdps.isEmpty()) { return new URL(cdps.iterator().next()); } } } catch (Exception e) { e.printStackTrace(); } return null; } protected static ASN1Primitive getExtensionValue(X509Certificate cert, String oid) { if (cert == null) { return null; } return getDerObjectFromByteArray(cert.getExtensionValue(oid)); } private static ASN1Primitive getDerObjectFromByteArray(byte[] bytes) { if (bytes == null) { return null; } try { return ASN1Primitive.fromByteArray(ASN1OctetString.getInstance(bytes).getOctets()); } catch (IOException e) { throw new RuntimeException("Caught an unexected IOException", e); } } private static String getStringFromGeneralNames(ASN1Primitive names) { final ASN1Sequence namesSequence = ASN1Sequence.getInstance(ASN1TaggedObject.getInstance(names), false); if (namesSequence.size() == 0) { return null; } final ASN1TaggedObject taggedObject = ASN1TaggedObject.getInstance(namesSequence.getObjectAt(0)); if (taggedObject.getTagNo() != GeneralName.uniformResourceIdentifier) { // uniformResourceIdentifier [6] IA5String, return null; } return new String(ASN1OctetString.getInstance(taggedObject, false).getOctets()); } public static String stringToBCDNString(String dn) { // BC now seem to handle multi-valued RDNs, but we keep escaping this for now to keep the behavior until support is required //dn = handleUnescapedPlus(dn); // Log warning if dn contains unescaped '+' if (isDNReversed(dn)) { dn = reverseDN(dn); } String ret = null; final X500Name name = stringToBcX500Name(dn); if (name != null) { ret = name.toString(); } return ret; } public static X500Name stringToBcX500Name(final String dn) { final X500NameStyle nameStyle = CeSecoreNameStyle.INSTANCE; return stringToBcX500Name(dn, nameStyle, true); } public static X500Name stringToBcX500Name(final String dn, boolean ldapOrder) { final X500NameStyle nameStyle = CeSecoreNameStyle.INSTANCE; return stringToBcX500Name(dn, nameStyle, ldapOrder); } public static X500Name stringToBcX500Name(String dn, final X500NameStyle nameStyle, final boolean ldaporder) { return stringToBcX500Name(dn, nameStyle, ldaporder, null); } public static X500Name stringToBcX500Name(String dn, final X500NameStyle nameStyle, final boolean ldaporder, final String[] order) { return stringToBcX500Name(dn, nameStyle, ldaporder, order, true); } public static X500Name stringToBcX500Name(String dn, final X500NameStyle nameStyle, final boolean ldaporder, final String[] order, final boolean applyLdapToCustomOrder) { final X500Name x500Name = stringToUnorderedX500Name(dn, nameStyle); if (x500Name == null) { return null; } // -- Reorder fields final X500Name orderedX500Name = getOrderedX500Name(x500Name, ldaporder, order, applyLdapToCustomOrder, nameStyle); return orderedX500Name; } private static List getX509FieldOrder(String[] order) { List fieldOrder = new ArrayList<>(); for (final String dNObject : order) { fieldOrder.add(DnComponents.getOid(dNObject)); } return fieldOrder; } public static List getX509FieldOrder(boolean ldaporder) { return getX509FieldOrder(DnComponents.getDnObjects(ldaporder)); } private static X500Name getOrderedX500Name(final X500Name x500Name, boolean ldaporder, String[] order, final boolean applyLdapToCustomOrder, final X500NameStyle nameStyle) { // Guess order of the input name final boolean isLdapOrder = !isDNReversed(x500Name.toString()); // If we think the DN is in LDAP order, first order it as a LDAP DN, if we don't think it's LDAP order // order it as a X.500 DN. If we haven't specified our own ordering final List ordering; final boolean useCustomOrder = (order != null) && (order.length > 0); if (useCustomOrder) { ordering = getX509FieldOrder(order); } else { ordering = getX509FieldOrder(isLdapOrder); } // -- New order for the X509 Fields final List newOrdering = new ArrayList<>(); final List newValues = new ArrayList<>(); // -- Add ordered fields final RDN[] allRdns = x500Name.getRDNs(); final HashSet hs = new HashSet<>(allRdns.length + ordering.size()); for (final ASN1ObjectIdentifier oid : ordering) { if (!hs.contains(oid)) { hs.add(oid); // We can't use x500Name.getRDNs(oid) because it will also hunt inside multi valued RNDs //final RDN[] valueList = x500Name.getRDNs(oid); // -- Only add the OID if has not null value for (final RDN value : allRdns) { if (oid.equals(value.getFirst().getType())) { newOrdering.add(oid); newValues.add(value); } } } } // -- Add unexpected fields to the end for (final RDN rdn : allRdns) { final ASN1ObjectIdentifier oid = rdn.getFirst().getType(); if (!hs.contains(oid)) { hs.add(oid); final RDN[] valueList = x500Name.getRDNs(oid); // -- Only add the OID if has not null value for (final RDN value : valueList) { newOrdering.add(oid); newValues.add(value); } } } // If the requested ordering was the reverse of the ordering the input string was in (by our guess in the beginning) // we have to reverse the vectors. // Unless we have specified a custom order, and choose to not apply LDAP Order to this custom order, in which case we will not change the order from the custom if ((useCustomOrder && applyLdapToCustomOrder) || !useCustomOrder) { if (ldaporder != isLdapOrder) { Collections.reverse(newOrdering); Collections.reverse(newValues); } } X500NameBuilder nameBuilder = new X500NameBuilder(nameStyle); for (int i = 0; i < newOrdering.size(); i++) { RDN rdn = newValues.get(i); if (rdn.isMultiValued()) { AttributeTypeAndValue avas[] = rdn.getTypesAndValues(); nameBuilder.addMultiValuedRDN(avas); } else { nameBuilder.addRDN(newOrdering.get(i), rdn.getFirst().getValue()); } } // -- Return X500Name with the ordered fields return nameBuilder.build(); } public static X500Name stringToUnorderedX500Name(String dn, final X500NameStyle nameStyle) { if (dn == null) { return null; } // If the entire DN is quoted (which is strange but legacy), we just remove these quotes and carry on if (dn.length() > 2 && dn.charAt(0) == '"' && dn.charAt(dn.length() - 1) == '"') { dn = dn.substring(1, dn.length() - 1); } final X500NameBuilder nameBuilder = new X500NameBuilder(nameStyle); // split DN string into RDNs, but if it is empty, don't try to split it // this means an empty X500Name will be returned, as according to the javadoc if (dn.length() > 0) { RDN[] rdns; // Will throw an IllegalArgumentException if the DN is badly formatted rdns = IETFUtils.rDNsFromString(dn, nameStyle); for (RDN rdn : rdns) { if (rdn.isMultiValued()) { AttributeTypeAndValue avas[] = rdn.getTypesAndValues(); nameBuilder.addMultiValuedRDN(avas); } else { AttributeTypeAndValue ava = rdn.getFirst(); nameBuilder.addRDN(ava); } } // This was the old legacy way we did the IETFUtils.rDNsFromString manually before // Keep it for reference at until EJBCA 7.2 or something, just to aid in potential support cases // boolean quoted = false; // boolean escapeNext = false; // int currentStartPosition = -1; // String currentPartName = null; // for (int i = 0; i < dn.length(); i++) { // final char current = dn.charAt(i); // // Toggle quoting for every non-escaped "-char // if (!escapeNext && current == '"') { // quoted = !quoted; // } // // If there is an unescaped and unquoted =-char the proceeding chars is a part name // if (currentStartPosition == -1 && !quoted && !escapeNext && current == '=' && 1 <= i) { // // Trim spaces (e.g. "O =value") // int endIndexOfPartName = i; // while (endIndexOfPartName > 0 && dn.charAt(endIndexOfPartName - 1) == ' ') { // endIndexOfPartName--; // } // int startIndexOfPartName = endIndexOfPartName - 1; // final String endOfPartNameSearchChars = ", +"; // while (startIndexOfPartName > 0 && (endOfPartNameSearchChars.indexOf(dn.charAt(startIndexOfPartName - 1)) == -1)) { // startIndexOfPartName--; // } // currentPartName = dn.substring(startIndexOfPartName, endIndexOfPartName); // currentStartPosition = i + 1; // } // // When we have found a start marker, we need to be on the lookout for the ending marker // if (currentStartPosition != -1 && ((!quoted && !escapeNext && (current == ',' || current == '+')) || i == dn.length() - 1)) { // int endPosition = (i == dn.length() - 1) ? dn.length() - 1 : i - 1; // // Remove white spaces from the end of the value // while (endPosition > currentStartPosition && dn.charAt(endPosition) == ' ') { // endPosition--; // } // // Remove white spaces from the beginning of the value // while (endPosition > currentStartPosition && dn.charAt(currentStartPosition) == ' ') { // currentStartPosition++; // } // // Only return the inner value if the part is quoted // if (currentStartPosition < dn.length() && dn.charAt(currentStartPosition) == '"' && dn.charAt(endPosition) == '"') { // currentStartPosition++; // endPosition--; // } // String currentValue = dn.substring(currentStartPosition, endPosition + 1); // // Unescape value (except escaped #) since the nameBuilder will double each escape // currentValue = unescapeValue(new StringBuilder(currentValue)).toString(); // try { // // -- First search the OID by name in declared OID's // ASN1ObjectIdentifier oid = DnComponents.getOid(currentPartName); // // -- If isn't declared, we try to create it // if (oid == null) { // oid = new ASN1ObjectIdentifier(currentPartName); // } // nameBuilder.addRDN(oid, currentValue); // } catch (IllegalArgumentException e) { // // If it is not an OID we will ignore it // log.warn("Unknown DN component ignored and silently dropped: " + currentPartName); // } // // Reset markers // currentStartPosition = -1; // currentPartName = null; // } // if (escapeNext) { // // This character was escaped, so don't escape the next one // escapeNext = false; // } else { // if (!quoted && current == '\\') { // // This escape character is not escaped itself, so the next one should be // escapeNext = true; // } // } // } } // finally builds X500 name final X500Name x500Name = nameBuilder.build(); return x500Name; } public static String reverseDN(String dn) { String ret = null; if (dn != null) { String o; final BasicX509NameTokenizer xt = new BasicX509NameTokenizer(dn); StringBuilder buf = new StringBuilder(); boolean first = true; while (xt.hasMoreTokens()) { o = xt.nextToken(); // log.debug("token: "+o); if (!first) { buf.insert(0, ","); } else { first = false; } buf.insert(0, o); } if (buf.length() > 0) { ret = buf.toString(); } } return ret; } public static boolean isDNReversed(String dn) { /* * if (log.isTraceEnabled()) { log.trace(">isDNReversed: dn: " + dn); } */ boolean ret = false; if (dn != null) { String first = null; String last = null; X509NameTokenizer xt = new X509NameTokenizer(dn); if (xt.hasMoreTokens()) { first = xt.nextToken().trim(); } while (xt.hasMoreTokens()) { last = xt.nextToken().trim(); } String[] dNObjects = DnComponents.getDnObjects(true); if ((first != null) && (last != null)) { // Be careful for bad input, that may not have any = sign in it final int fi = first.indexOf('='); first = first.substring(0, (fi != -1 ? fi : (first.length() - 1))); final int li = last.indexOf('='); last = last.substring(0, (li != -1 ? li : (last.length() - 1))); int firsti = 0, lasti = 0; for (int i = 0; i < dNObjects.length; i++) { if (first.equalsIgnoreCase(dNObjects[i])) { firsti = i; } if (last.equalsIgnoreCase(dNObjects[i])) { lasti = i; } } if (lasti < firsti) { ret = true; } } } /* * if (log.isTraceEnabled()) { log.trace("