| /* |
| * Copyright (c) 2000, 2015, 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 java.security.cert; |
| |
| import java.io.IOException; |
| import java.math.BigInteger; |
| import java.security.PublicKey; |
| import java.util.*; |
| import javax.security.auth.x500.X500Principal; |
| |
| import sun.security.util.HexDumpEncoder; |
| import sun.security.util.Debug; |
| import sun.security.util.DerInputStream; |
| import sun.security.util.DerValue; |
| import sun.security.util.ObjectIdentifier; |
| import sun.security.x509.*; |
| |
| /** |
| * A {@code CertSelector} that selects {@code X509Certificates} that |
| * match all specified criteria. This class is particularly useful when |
| * selecting certificates from a {@code CertStore} to build a |
| * PKIX-compliant certification path. |
| * <p> |
| * When first constructed, an {@code X509CertSelector} has no criteria |
| * enabled and each of the {@code get} methods return a default value |
| * ({@code null}, or {@code -1} for the {@link #getBasicConstraints |
| * getBasicConstraints} method). Therefore, the {@link #match match} |
| * method would return {@code true} for any {@code X509Certificate}. |
| * Typically, several criteria are enabled (by calling |
| * {@link #setIssuer setIssuer} or |
| * {@link #setKeyUsage setKeyUsage}, for instance) and then the |
| * {@code X509CertSelector} is passed to |
| * {@link CertStore#getCertificates CertStore.getCertificates} or some similar |
| * method. |
| * <p> |
| * Several criteria can be enabled (by calling {@link #setIssuer setIssuer} |
| * and {@link #setSerialNumber setSerialNumber}, |
| * for example) such that the {@code match} method |
| * usually uniquely matches a single {@code X509Certificate}. We say |
| * usually, since it is possible for two issuing CAs to have the same |
| * distinguished name and each issue a certificate with the same serial |
| * number. Other unique combinations include the issuer, subject, |
| * subjectKeyIdentifier and/or the subjectPublicKey criteria. |
| * <p> |
| * Please refer to <a href="http://tools.ietf.org/html/rfc5280">RFC 5280: |
| * Internet X.509 Public Key Infrastructure Certificate and CRL Profile</a> for |
| * definitions of the X.509 certificate extensions mentioned below. |
| * <p> |
| * <b>Concurrent Access</b> |
| * <p> |
| * Unless otherwise specified, the methods defined in this class are not |
| * thread-safe. Multiple threads that need to access a single |
| * object concurrently should synchronize amongst themselves and |
| * provide the necessary locking. Multiple threads each manipulating |
| * separate objects need not synchronize. |
| * |
| * @see CertSelector |
| * @see X509Certificate |
| * |
| * @since 1.4 |
| * @author Steve Hanna |
| */ |
| public class X509CertSelector implements CertSelector { |
| |
| private static final Debug debug = Debug.getInstance("certpath"); |
| |
| private static final ObjectIdentifier ANY_EXTENDED_KEY_USAGE = |
| ObjectIdentifier.newInternal(new int[] {2, 5, 29, 37, 0}); |
| |
| static { |
| CertPathHelperImpl.initialize(); |
| } |
| |
| private BigInteger serialNumber; |
| private X500Principal issuer; |
| private X500Principal subject; |
| private byte[] subjectKeyID; |
| private byte[] authorityKeyID; |
| private Date certificateValid; |
| private Date privateKeyValid; |
| private ObjectIdentifier subjectPublicKeyAlgID; |
| private PublicKey subjectPublicKey; |
| private byte[] subjectPublicKeyBytes; |
| private boolean[] keyUsage; |
| private Set<String> keyPurposeSet; |
| private Set<ObjectIdentifier> keyPurposeOIDSet; |
| private Set<List<?>> subjectAlternativeNames; |
| private Set<GeneralNameInterface> subjectAlternativeGeneralNames; |
| private CertificatePolicySet policy; |
| private Set<String> policySet; |
| private Set<List<?>> pathToNames; |
| private Set<GeneralNameInterface> pathToGeneralNames; |
| private NameConstraintsExtension nc; |
| private byte[] ncBytes; |
| private int basicConstraints = -1; |
| private X509Certificate x509Cert; |
| private boolean matchAllSubjectAltNames = true; |
| |
| private static final Boolean FALSE = Boolean.FALSE; |
| |
| private static final int PRIVATE_KEY_USAGE_ID = 0; |
| private static final int SUBJECT_ALT_NAME_ID = 1; |
| private static final int NAME_CONSTRAINTS_ID = 2; |
| private static final int CERT_POLICIES_ID = 3; |
| private static final int EXTENDED_KEY_USAGE_ID = 4; |
| private static final int NUM_OF_EXTENSIONS = 5; |
| private static final String[] EXTENSION_OIDS = new String[NUM_OF_EXTENSIONS]; |
| |
| static { |
| EXTENSION_OIDS[PRIVATE_KEY_USAGE_ID] = "2.5.29.16"; |
| EXTENSION_OIDS[SUBJECT_ALT_NAME_ID] = "2.5.29.17"; |
| EXTENSION_OIDS[NAME_CONSTRAINTS_ID] = "2.5.29.30"; |
| EXTENSION_OIDS[CERT_POLICIES_ID] = "2.5.29.32"; |
| EXTENSION_OIDS[EXTENDED_KEY_USAGE_ID] = "2.5.29.37"; |
| }; |
| |
| /* Constants representing the GeneralName types */ |
| static final int NAME_ANY = 0; |
| static final int NAME_RFC822 = 1; |
| static final int NAME_DNS = 2; |
| static final int NAME_X400 = 3; |
| static final int NAME_DIRECTORY = 4; |
| static final int NAME_EDI = 5; |
| static final int NAME_URI = 6; |
| static final int NAME_IP = 7; |
| static final int NAME_OID = 8; |
| |
| /** |
| * Creates an {@code X509CertSelector}. Initially, no criteria are set |
| * so any {@code X509Certificate} will match. |
| */ |
| public X509CertSelector() { |
| // empty |
| } |
| |
| /** |
| * Sets the certificateEquals criterion. The specified |
| * {@code X509Certificate} must be equal to the |
| * {@code X509Certificate} passed to the {@code match} method. |
| * If {@code null}, then this check is not applied. |
| * |
| * <p>This method is particularly useful when it is necessary to |
| * match a single certificate. Although other criteria can be specified |
| * in conjunction with the certificateEquals criterion, it is usually not |
| * practical or necessary. |
| * |
| * @param cert the {@code X509Certificate} to match (or |
| * {@code null}) |
| * @see #getCertificate |
| */ |
| public void setCertificate(X509Certificate cert) { |
| x509Cert = cert; |
| } |
| |
| /** |
| * Sets the serialNumber criterion. The specified serial number |
| * must match the certificate serial number in the |
| * {@code X509Certificate}. If {@code null}, any certificate |
| * serial number will do. |
| * |
| * @param serial the certificate serial number to match |
| * (or {@code null}) |
| * @see #getSerialNumber |
| */ |
| public void setSerialNumber(BigInteger serial) { |
| serialNumber = serial; |
| } |
| |
| /** |
| * Sets the issuer criterion. The specified distinguished name |
| * must match the issuer distinguished name in the |
| * {@code X509Certificate}. If {@code null}, any issuer |
| * distinguished name will do. |
| * |
| * @param issuer a distinguished name as X500Principal |
| * (or {@code null}) |
| * @since 1.5 |
| */ |
| public void setIssuer(X500Principal issuer) { |
| this.issuer = issuer; |
| } |
| |
| /** |
| * <strong>Denigrated</strong>, use {@linkplain #setIssuer(X500Principal)} |
| * or {@linkplain #setIssuer(byte[])} instead. This method should not be |
| * relied on as it can fail to match some certificates because of a loss of |
| * encoding information in the |
| * <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a> String form |
| * of some distinguished names. |
| * <p> |
| * Sets the issuer criterion. The specified distinguished name |
| * must match the issuer distinguished name in the |
| * {@code X509Certificate}. If {@code null}, any issuer |
| * distinguished name will do. |
| * <p> |
| * If {@code issuerDN} is not {@code null}, it should contain a |
| * distinguished name, in RFC 2253 format. |
| * |
| * @param issuerDN a distinguished name in RFC 2253 format |
| * (or {@code null}) |
| * @throws IOException if a parsing error occurs (incorrect form for DN) |
| */ |
| public void setIssuer(String issuerDN) throws IOException { |
| if (issuerDN == null) { |
| issuer = null; |
| } else { |
| issuer = new X500Name(issuerDN).asX500Principal(); |
| } |
| } |
| |
| /** |
| * Sets the issuer criterion. The specified distinguished name |
| * must match the issuer distinguished name in the |
| * {@code X509Certificate}. If {@code null} is specified, |
| * the issuer criterion is disabled and any issuer distinguished name will |
| * do. |
| * <p> |
| * If {@code issuerDN} is not {@code null}, it should contain a |
| * single DER encoded distinguished name, as defined in X.501. The ASN.1 |
| * notation for this structure is as follows. |
| * <pre>{@code |
| * Name ::= CHOICE { |
| * RDNSequence } |
| * |
| * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName |
| * |
| * RelativeDistinguishedName ::= |
| * SET SIZE (1 .. MAX) 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> |
| * Note that the byte array specified here is cloned to protect against |
| * subsequent modifications. |
| * |
| * @param issuerDN a byte array containing the distinguished name |
| * in ASN.1 DER encoded form (or {@code null}) |
| * @throws IOException if an encoding error occurs (incorrect form for DN) |
| */ |
| public void setIssuer(byte[] issuerDN) throws IOException { |
| try { |
| issuer = (issuerDN == null ? null : new X500Principal(issuerDN)); |
| } catch (IllegalArgumentException e) { |
| throw new IOException("Invalid name", e); |
| } |
| } |
| |
| /** |
| * Sets the subject criterion. The specified distinguished name |
| * must match the subject distinguished name in the |
| * {@code X509Certificate}. If {@code null}, any subject |
| * distinguished name will do. |
| * |
| * @param subject a distinguished name as X500Principal |
| * (or {@code null}) |
| * @since 1.5 |
| */ |
| public void setSubject(X500Principal subject) { |
| this.subject = subject; |
| } |
| |
| /** |
| * <strong>Denigrated</strong>, use {@linkplain #setSubject(X500Principal)} |
| * or {@linkplain #setSubject(byte[])} instead. This method should not be |
| * relied on as it can fail to match some certificates because of a loss of |
| * encoding information in the RFC 2253 String form of some distinguished |
| * names. |
| * <p> |
| * Sets the subject criterion. The specified distinguished name |
| * must match the subject distinguished name in the |
| * {@code X509Certificate}. If {@code null}, any subject |
| * distinguished name will do. |
| * <p> |
| * If {@code subjectDN} is not {@code null}, it should contain a |
| * distinguished name, in RFC 2253 format. |
| * |
| * @param subjectDN a distinguished name in RFC 2253 format |
| * (or {@code null}) |
| * @throws IOException if a parsing error occurs (incorrect form for DN) |
| */ |
| public void setSubject(String subjectDN) throws IOException { |
| if (subjectDN == null) { |
| subject = null; |
| } else { |
| subject = new X500Name(subjectDN).asX500Principal(); |
| } |
| } |
| |
| /** |
| * Sets the subject criterion. The specified distinguished name |
| * must match the subject distinguished name in the |
| * {@code X509Certificate}. If {@code null}, any subject |
| * distinguished name will do. |
| * <p> |
| * If {@code subjectDN} is not {@code null}, it should contain a |
| * single DER encoded distinguished name, as defined in X.501. For the ASN.1 |
| * notation for this structure, see |
| * {@link #setIssuer(byte [] issuerDN) setIssuer(byte [] issuerDN)}. |
| * |
| * @param subjectDN a byte array containing the distinguished name in |
| * ASN.1 DER format (or {@code null}) |
| * @throws IOException if an encoding error occurs (incorrect form for DN) |
| */ |
| public void setSubject(byte[] subjectDN) throws IOException { |
| try { |
| subject = (subjectDN == null ? null : new X500Principal(subjectDN)); |
| } catch (IllegalArgumentException e) { |
| throw new IOException("Invalid name", e); |
| } |
| } |
| |
| /** |
| * Sets the subjectKeyIdentifier criterion. The |
| * {@code X509Certificate} must contain a SubjectKeyIdentifier |
| * extension for which the contents of the extension |
| * matches the specified criterion value. |
| * If the criterion value is {@code null}, no |
| * subjectKeyIdentifier check will be done. |
| * <p> |
| * If {@code subjectKeyID} is not {@code null}, it |
| * should contain a single DER encoded value corresponding to the contents |
| * of the extension value (not including the object identifier, |
| * criticality setting, and encapsulating OCTET STRING) |
| * for a SubjectKeyIdentifier extension. |
| * The ASN.1 notation for this structure follows. |
| * |
| * <pre>{@code |
| * SubjectKeyIdentifier ::= KeyIdentifier |
| * |
| * KeyIdentifier ::= OCTET STRING |
| * }</pre> |
| * <p> |
| * Since the format of subject key identifiers is not mandated by |
| * any standard, subject key identifiers are not parsed by the |
| * {@code X509CertSelector}. Instead, the values are compared using |
| * a byte-by-byte comparison. |
| * <p> |
| * Note that the byte array supplied here is cloned to protect against |
| * subsequent modifications. |
| * |
| * @param subjectKeyID the subject key identifier (or {@code null}) |
| * @see #getSubjectKeyIdentifier |
| */ |
| public void setSubjectKeyIdentifier(byte[] subjectKeyID) { |
| if (subjectKeyID == null) { |
| this.subjectKeyID = null; |
| } else { |
| this.subjectKeyID = subjectKeyID.clone(); |
| } |
| } |
| |
| /** |
| * Sets the authorityKeyIdentifier criterion. The |
| * {@code X509Certificate} must contain an |
| * AuthorityKeyIdentifier extension for which the contents of the |
| * extension value matches the specified criterion value. |
| * If the criterion value is {@code null}, no |
| * authorityKeyIdentifier check will be done. |
| * <p> |
| * If {@code authorityKeyID} is not {@code null}, it |
| * should contain a single DER encoded value corresponding to the contents |
| * of the extension value (not including the object identifier, |
| * criticality setting, and encapsulating OCTET STRING) |
| * for an AuthorityKeyIdentifier extension. |
| * The ASN.1 notation for this structure follows. |
| * |
| * <pre>{@code |
| * AuthorityKeyIdentifier ::= SEQUENCE { |
| * keyIdentifier [0] KeyIdentifier OPTIONAL, |
| * authorityCertIssuer [1] GeneralNames OPTIONAL, |
| * authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } |
| * |
| * KeyIdentifier ::= OCTET STRING |
| * }</pre> |
| * <p> |
| * Authority key identifiers are not parsed by the |
| * {@code X509CertSelector}. Instead, the values are |
| * compared using a byte-by-byte comparison. |
| * <p> |
| * When the {@code keyIdentifier} field of |
| * {@code AuthorityKeyIdentifier} is populated, the value is |
| * usually taken from the {@code SubjectKeyIdentifier} extension |
| * in the issuer's certificate. Note, however, that the result of |
| * {@code X509Certificate.getExtensionValue(<SubjectKeyIdentifier Object |
| * Identifier>)} on the issuer's certificate may NOT be used |
| * directly as the input to {@code setAuthorityKeyIdentifier}. |
| * This is because the SubjectKeyIdentifier contains |
| * only a KeyIdentifier OCTET STRING, and not a SEQUENCE of |
| * KeyIdentifier, GeneralNames, and CertificateSerialNumber. |
| * In order to use the extension value of the issuer certificate's |
| * {@code SubjectKeyIdentifier} |
| * extension, it will be necessary to extract the value of the embedded |
| * {@code KeyIdentifier} OCTET STRING, then DER encode this OCTET |
| * STRING inside a SEQUENCE. |
| * For more details on SubjectKeyIdentifier, see |
| * {@link #setSubjectKeyIdentifier(byte[] subjectKeyID)}. |
| * <p> |
| * Note also that the byte array supplied here is cloned to protect against |
| * subsequent modifications. |
| * |
| * @param authorityKeyID the authority key identifier |
| * (or {@code null}) |
| * @see #getAuthorityKeyIdentifier |
| */ |
| public void setAuthorityKeyIdentifier(byte[] authorityKeyID) { |
| if (authorityKeyID == null) { |
| this.authorityKeyID = null; |
| } else { |
| this.authorityKeyID = authorityKeyID.clone(); |
| } |
| } |
| |
| /** |
| * Sets the certificateValid criterion. The specified date must fall |
| * within the certificate validity period for the |
| * {@code X509Certificate}. If {@code null}, no certificateValid |
| * check will be done. |
| * <p> |
| * Note that the {@code Date} supplied here is cloned to protect |
| * against subsequent modifications. |
| * |
| * @param certValid the {@code Date} to check (or {@code null}) |
| * @see #getCertificateValid |
| */ |
| public void setCertificateValid(Date certValid) { |
| if (certValid == null) { |
| certificateValid = null; |
| } else { |
| certificateValid = (Date)certValid.clone(); |
| } |
| } |
| |
| /** |
| * Sets the privateKeyValid criterion. The specified date must fall |
| * within the private key validity period for the |
| * {@code X509Certificate}. If {@code null}, no privateKeyValid |
| * check will be done. |
| * <p> |
| * Note that the {@code Date} supplied here is cloned to protect |
| * against subsequent modifications. |
| * |
| * @param privateKeyValid the {@code Date} to check (or |
| * {@code null}) |
| * @see #getPrivateKeyValid |
| */ |
| public void setPrivateKeyValid(Date privateKeyValid) { |
| if (privateKeyValid == null) { |
| this.privateKeyValid = null; |
| } else { |
| this.privateKeyValid = (Date)privateKeyValid.clone(); |
| } |
| } |
| |
| /** |
| * Sets the subjectPublicKeyAlgID criterion. The |
| * {@code X509Certificate} must contain a subject public key |
| * with the specified algorithm. If {@code null}, no |
| * subjectPublicKeyAlgID check will be done. |
| * |
| * @param oid The object identifier (OID) of the algorithm to check |
| * for (or {@code null}). An OID is represented by a |
| * set of nonnegative integers separated by periods. |
| * @throws IOException if the OID is invalid, such as |
| * the first component being not 0, 1 or 2 or the second component |
| * being greater than 39. |
| * |
| * @see #getSubjectPublicKeyAlgID |
| */ |
| public void setSubjectPublicKeyAlgID(String oid) throws IOException { |
| if (oid == null) { |
| subjectPublicKeyAlgID = null; |
| } else { |
| subjectPublicKeyAlgID = new ObjectIdentifier(oid); |
| } |
| } |
| |
| /** |
| * Sets the subjectPublicKey criterion. The |
| * {@code X509Certificate} must contain the specified subject public |
| * key. If {@code null}, no subjectPublicKey check will be done. |
| * |
| * @param key the subject public key to check for (or {@code null}) |
| * @see #getSubjectPublicKey |
| */ |
| public void setSubjectPublicKey(PublicKey key) { |
| if (key == null) { |
| subjectPublicKey = null; |
| subjectPublicKeyBytes = null; |
| } else { |
| subjectPublicKey = key; |
| subjectPublicKeyBytes = key.getEncoded(); |
| } |
| } |
| |
| /** |
| * Sets the subjectPublicKey criterion. The {@code X509Certificate} |
| * must contain the specified subject public key. If {@code null}, |
| * no subjectPublicKey check will be done. |
| * <p> |
| * Because this method allows the public key to be specified as a byte |
| * array, it may be used for unknown key types. |
| * <p> |
| * If {@code key} is not {@code null}, it should contain a |
| * single DER encoded SubjectPublicKeyInfo structure, as defined in X.509. |
| * The ASN.1 notation for this structure is as follows. |
| * <pre>{@code |
| * SubjectPublicKeyInfo ::= SEQUENCE { |
| * algorithm AlgorithmIdentifier, |
| * subjectPublicKey BIT STRING } |
| * |
| * AlgorithmIdentifier ::= SEQUENCE { |
| * algorithm OBJECT IDENTIFIER, |
| * parameters ANY DEFINED BY algorithm OPTIONAL } |
| * -- contains a value of the type |
| * -- registered for use with the |
| * -- algorithm object identifier value |
| * }</pre> |
| * <p> |
| * Note that the byte array supplied here is cloned to protect against |
| * subsequent modifications. |
| * |
| * @param key a byte array containing the subject public key in ASN.1 DER |
| * form (or {@code null}) |
| * @throws IOException if an encoding error occurs (incorrect form for |
| * subject public key) |
| * @see #getSubjectPublicKey |
| */ |
| public void setSubjectPublicKey(byte[] key) throws IOException { |
| if (key == null) { |
| subjectPublicKey = null; |
| subjectPublicKeyBytes = null; |
| } else { |
| subjectPublicKeyBytes = key.clone(); |
| subjectPublicKey = X509Key.parse(new DerValue(subjectPublicKeyBytes)); |
| } |
| } |
| |
| /** |
| * Sets the keyUsage criterion. The {@code X509Certificate} |
| * must allow the specified keyUsage values. If {@code null}, no |
| * keyUsage check will be done. Note that an {@code X509Certificate} |
| * that has no keyUsage extension implicitly allows all keyUsage values. |
| * <p> |
| * Note that the boolean array supplied here is cloned to protect against |
| * subsequent modifications. |
| * |
| * @param keyUsage a boolean array in the same format as the boolean |
| * array returned by |
| * {@link X509Certificate#getKeyUsage() X509Certificate.getKeyUsage()}. |
| * Or {@code null}. |
| * @see #getKeyUsage |
| */ |
| public void setKeyUsage(boolean[] keyUsage) { |
| if (keyUsage == null) { |
| this.keyUsage = null; |
| } else { |
| this.keyUsage = keyUsage.clone(); |
| } |
| } |
| |
| /** |
| * Sets the extendedKeyUsage criterion. The {@code X509Certificate} |
| * must allow the specified key purposes in its extended key usage |
| * extension. If {@code keyPurposeSet} is empty or {@code null}, |
| * no extendedKeyUsage check will be done. Note that an |
| * {@code X509Certificate} that has no extendedKeyUsage extension |
| * implicitly allows all key purposes. |
| * <p> |
| * Note that the {@code Set} is cloned to protect against |
| * subsequent modifications. |
| * |
| * @param keyPurposeSet a {@code Set} of key purpose OIDs in string |
| * format (or {@code null}). Each OID is represented by a set of |
| * nonnegative integers separated by periods. |
| * @throws IOException if the OID is invalid, such as |
| * the first component being not 0, 1 or 2 or the second component |
| * being greater than 39. |
| * @see #getExtendedKeyUsage |
| */ |
| public void setExtendedKeyUsage(Set<String> keyPurposeSet) throws IOException { |
| if ((keyPurposeSet == null) || keyPurposeSet.isEmpty()) { |
| this.keyPurposeSet = null; |
| keyPurposeOIDSet = null; |
| } else { |
| this.keyPurposeSet = |
| Collections.unmodifiableSet(new HashSet<>(keyPurposeSet)); |
| keyPurposeOIDSet = new HashSet<>(); |
| for (String s : this.keyPurposeSet) { |
| keyPurposeOIDSet.add(new ObjectIdentifier(s)); |
| } |
| } |
| } |
| |
| /** |
| * Enables/disables matching all of the subjectAlternativeNames |
| * specified in the {@link #setSubjectAlternativeNames |
| * setSubjectAlternativeNames} or {@link #addSubjectAlternativeName |
| * addSubjectAlternativeName} methods. If enabled, |
| * the {@code X509Certificate} must contain all of the |
| * specified subject alternative names. If disabled, the |
| * {@code X509Certificate} must contain at least one of the |
| * specified subject alternative names. |
| * |
| * <p>The matchAllNames flag is {@code true} by default. |
| * |
| * @param matchAllNames if {@code true}, the flag is enabled; |
| * if {@code false}, the flag is disabled. |
| * @see #getMatchAllSubjectAltNames |
| */ |
| public void setMatchAllSubjectAltNames(boolean matchAllNames) { |
| this.matchAllSubjectAltNames = matchAllNames; |
| } |
| |
| /** |
| * Sets the subjectAlternativeNames criterion. The |
| * {@code X509Certificate} must contain all or at least one of the |
| * specified subjectAlternativeNames, depending on the value of |
| * the matchAllNames flag (see {@link #setMatchAllSubjectAltNames |
| * setMatchAllSubjectAltNames}). |
| * <p> |
| * This method allows the caller to specify, with a single method call, |
| * the complete set of subject alternative names for the |
| * subjectAlternativeNames criterion. The specified value replaces |
| * the previous value for the subjectAlternativeNames criterion. |
| * <p> |
| * The {@code names} parameter (if not {@code null}) is a |
| * {@code Collection} with one |
| * entry for each name to be included in the subject alternative name |
| * criterion. Each entry is a {@code List} whose first entry is an |
| * {@code Integer} (the name type, 0-8) and whose second |
| * entry is a {@code String} or a byte array (the name, in |
| * string or ASN.1 DER encoded form, respectively). |
| * There can be multiple names of the same type. If {@code null} |
| * is supplied as the value for this argument, no |
| * subjectAlternativeNames check will be performed. |
| * <p> |
| * Each subject alternative name in the {@code Collection} |
| * may be specified either as a {@code String} or as an ASN.1 encoded |
| * byte array. For more details about the formats used, see |
| * {@link #addSubjectAlternativeName(int type, String name) |
| * addSubjectAlternativeName(int type, String name)} and |
| * {@link #addSubjectAlternativeName(int type, byte [] name) |
| * addSubjectAlternativeName(int type, byte [] name)}. |
| * <p> |
| * <strong>Note:</strong> for distinguished names, specify the byte |
| * array form instead of the String form. See the note in |
| * {@link #addSubjectAlternativeName(int, String)} for more information. |
| * <p> |
| * Note that the {@code names} parameter can contain duplicate |
| * names (same name and name type), but they may be removed from the |
| * {@code Collection} of names returned by the |
| * {@link #getSubjectAlternativeNames getSubjectAlternativeNames} method. |
| * <p> |
| * Note that a deep copy is performed on the {@code Collection} to |
| * protect against subsequent modifications. |
| * |
| * @param names a {@code Collection} of names (or {@code null}) |
| * @throws IOException if a parsing error occurs |
| * @see #getSubjectAlternativeNames |
| */ |
| public void setSubjectAlternativeNames(Collection<List<?>> names) |
| throws IOException { |
| if (names == null) { |
| subjectAlternativeNames = null; |
| subjectAlternativeGeneralNames = null; |
| } else { |
| if (names.isEmpty()) { |
| subjectAlternativeNames = null; |
| subjectAlternativeGeneralNames = null; |
| return; |
| } |
| Set<List<?>> tempNames = cloneAndCheckNames(names); |
| // Ensure that we either set both of these or neither |
| subjectAlternativeGeneralNames = parseNames(tempNames); |
| subjectAlternativeNames = tempNames; |
| } |
| } |
| |
| /** |
| * Adds a name to the subjectAlternativeNames criterion. The |
| * {@code X509Certificate} must contain all or at least one |
| * of the specified subjectAlternativeNames, depending on the value of |
| * the matchAllNames flag (see {@link #setMatchAllSubjectAltNames |
| * setMatchAllSubjectAltNames}). |
| * <p> |
| * This method allows the caller to add a name to the set of subject |
| * alternative names. |
| * The specified name is added to any previous value for the |
| * subjectAlternativeNames criterion. If the specified name is a |
| * duplicate, it may be ignored. |
| * <p> |
| * The name is provided in string format. |
| * <a href="http://www.ietf.org/rfc/rfc822.txt">RFC 822</a>, DNS, and URI |
| * names use the well-established string formats for those types (subject to |
| * the restrictions included in RFC 5280). IPv4 address names are |
| * supplied using dotted quad notation. OID address names are represented |
| * as a series of nonnegative integers separated by periods. And |
| * directory names (distinguished names) are supplied in RFC 2253 format. |
| * No standard string format is defined for otherNames, X.400 names, |
| * EDI party names, IPv6 address names, or any other type of names. They |
| * should be specified using the |
| * {@link #addSubjectAlternativeName(int type, byte [] name) |
| * addSubjectAlternativeName(int type, byte [] name)} |
| * method. |
| * <p> |
| * <strong>Note:</strong> for distinguished names, use |
| * {@linkplain #addSubjectAlternativeName(int, byte[])} instead. |
| * This method should not be relied on as it can fail to match some |
| * certificates because of a loss of encoding information in the RFC 2253 |
| * String form of some distinguished names. |
| * |
| * @param type the name type (0-8, as specified in |
| * RFC 5280, section 4.2.1.6) |
| * @param name the name in string form (not {@code null}) |
| * @throws IOException if a parsing error occurs |
| */ |
| public void addSubjectAlternativeName(int type, String name) |
| throws IOException { |
| addSubjectAlternativeNameInternal(type, name); |
| } |
| |
| /** |
| * Adds a name to the subjectAlternativeNames criterion. The |
| * {@code X509Certificate} must contain all or at least one |
| * of the specified subjectAlternativeNames, depending on the value of |
| * the matchAllNames flag (see {@link #setMatchAllSubjectAltNames |
| * setMatchAllSubjectAltNames}). |
| * <p> |
| * This method allows the caller to add a name to the set of subject |
| * alternative names. |
| * The specified name is added to any previous value for the |
| * subjectAlternativeNames criterion. If the specified name is a |
| * duplicate, it may be ignored. |
| * <p> |
| * The name is provided as a byte array. This byte array should contain |
| * the DER encoded name, as it would appear in the GeneralName structure |
| * defined in RFC 5280 and X.509. The encoded byte array should only contain |
| * the encoded value of the name, and should not include the tag associated |
| * with the name in the GeneralName structure. The ASN.1 definition of this |
| * structure appears below. |
| * <pre>{@code |
| * GeneralName ::= CHOICE { |
| * otherName [0] OtherName, |
| * rfc822Name [1] IA5String, |
| * dNSName [2] IA5String, |
| * x400Address [3] ORAddress, |
| * directoryName [4] Name, |
| * ediPartyName [5] EDIPartyName, |
| * uniformResourceIdentifier [6] IA5String, |
| * iPAddress [7] OCTET STRING, |
| * registeredID [8] OBJECT IDENTIFIER} |
| * }</pre> |
| * <p> |
| * Note that the byte array supplied here is cloned to protect against |
| * subsequent modifications. |
| * |
| * @param type the name type (0-8, as listed above) |
| * @param name a byte array containing the name in ASN.1 DER encoded form |
| * @throws IOException if a parsing error occurs |
| */ |
| public void addSubjectAlternativeName(int type, byte[] name) |
| throws IOException { |
| // clone because byte arrays are modifiable |
| addSubjectAlternativeNameInternal(type, name.clone()); |
| } |
| |
| /** |
| * A private method that adds a name (String or byte array) to the |
| * subjectAlternativeNames criterion. The {@code X509Certificate} |
| * must contain the specified subjectAlternativeName. |
| * |
| * @param type the name type (0-8, as specified in |
| * RFC 5280, section 4.2.1.6) |
| * @param name the name in string or byte array form |
| * @throws IOException if a parsing error occurs |
| */ |
| private void addSubjectAlternativeNameInternal(int type, Object name) |
| throws IOException { |
| // First, ensure that the name parses |
| GeneralNameInterface tempName = makeGeneralNameInterface(type, name); |
| if (subjectAlternativeNames == null) { |
| subjectAlternativeNames = new HashSet<>(); |
| } |
| if (subjectAlternativeGeneralNames == null) { |
| subjectAlternativeGeneralNames = new HashSet<>(); |
| } |
| List<Object> list = new ArrayList<>(2); |
| list.add(Integer.valueOf(type)); |
| list.add(name); |
| subjectAlternativeNames.add(list); |
| subjectAlternativeGeneralNames.add(tempName); |
| } |
| |
| /** |
| * Parse an argument of the form passed to setSubjectAlternativeNames, |
| * returning a {@code Collection} of |
| * {@code GeneralNameInterface}s. |
| * Throw an IllegalArgumentException or a ClassCastException |
| * if the argument is malformed. |
| * |
| * @param names a Collection with one entry per name. |
| * Each entry is a {@code List} whose first entry |
| * is an Integer (the name type, 0-8) and whose second |
| * entry is a String or a byte array (the name, in |
| * string or ASN.1 DER encoded form, respectively). |
| * There can be multiple names of the same type. Null is |
| * not an acceptable value. |
| * @return a Set of {@code GeneralNameInterface}s |
| * @throws IOException if a parsing error occurs |
| */ |
| private static Set<GeneralNameInterface> parseNames(Collection<List<?>> names) throws IOException { |
| Set<GeneralNameInterface> genNames = new HashSet<>(); |
| for (List<?> nameList : names) { |
| if (nameList.size() != 2) { |
| throw new IOException("name list size not 2"); |
| } |
| Object o = nameList.get(0); |
| if (!(o instanceof Integer)) { |
| throw new IOException("expected an Integer"); |
| } |
| int nameType = ((Integer)o).intValue(); |
| o = nameList.get(1); |
| genNames.add(makeGeneralNameInterface(nameType, o)); |
| } |
| |
| return genNames; |
| } |
| |
| /** |
| * Compare for equality two objects of the form passed to |
| * setSubjectAlternativeNames (or X509CRLSelector.setIssuerNames). |
| * Throw an {@code IllegalArgumentException} or a |
| * {@code ClassCastException} if one of the objects is malformed. |
| * |
| * @param object1 a Collection containing the first object to compare |
| * @param object2 a Collection containing the second object to compare |
| * @return true if the objects are equal, false otherwise |
| */ |
| static boolean equalNames(Collection<?> object1, Collection<?> object2) { |
| if ((object1 == null) || (object2 == null)) { |
| return object1 == object2; |
| } |
| return object1.equals(object2); |
| } |
| |
| /** |
| * Make a {@code GeneralNameInterface} out of a name type (0-8) and an |
| * Object that may be a byte array holding the ASN.1 DER encoded |
| * name or a String form of the name. Except for X.509 |
| * Distinguished Names, the String form of the name must not be the |
| * result from calling toString on an existing GeneralNameInterface |
| * implementing class. The output of toString is not compatible |
| * with the String constructors for names other than Distinguished |
| * Names. |
| * |
| * @param type name type (0-8) |
| * @param name name as ASN.1 Der-encoded byte array or String |
| * @return a GeneralNameInterface name |
| * @throws IOException if a parsing error occurs |
| */ |
| static GeneralNameInterface makeGeneralNameInterface(int type, Object name) |
| throws IOException { |
| GeneralNameInterface result; |
| if (debug != null) { |
| debug.println("X509CertSelector.makeGeneralNameInterface(" |
| + type + ")..."); |
| } |
| |
| if (name instanceof String) { |
| if (debug != null) { |
| debug.println("X509CertSelector.makeGeneralNameInterface() " |
| + "name is String: " + name); |
| } |
| switch (type) { |
| case NAME_RFC822: |
| result = new RFC822Name((String)name); |
| break; |
| case NAME_DNS: |
| result = new DNSName((String)name); |
| break; |
| case NAME_DIRECTORY: |
| result = new X500Name((String)name); |
| break; |
| case NAME_URI: |
| result = new URIName((String)name); |
| break; |
| case NAME_IP: |
| result = new IPAddressName((String)name); |
| break; |
| case NAME_OID: |
| result = new OIDName((String)name); |
| break; |
| default: |
| throw new IOException("unable to parse String names of type " |
| + type); |
| } |
| if (debug != null) { |
| debug.println("X509CertSelector.makeGeneralNameInterface() " |
| + "result: " + result.toString()); |
| } |
| } else if (name instanceof byte[]) { |
| DerValue val = new DerValue((byte[]) name); |
| if (debug != null) { |
| debug.println |
| ("X509CertSelector.makeGeneralNameInterface() is byte[]"); |
| } |
| |
| switch (type) { |
| case NAME_ANY: |
| result = new OtherName(val); |
| break; |
| case NAME_RFC822: |
| result = new RFC822Name(val); |
| break; |
| case NAME_DNS: |
| result = new DNSName(val); |
| break; |
| case NAME_X400: |
| result = new X400Address(val); |
| break; |
| case NAME_DIRECTORY: |
| result = new X500Name(val); |
| break; |
| case NAME_EDI: |
| result = new EDIPartyName(val); |
| break; |
| case NAME_URI: |
| result = new URIName(val); |
| break; |
| case NAME_IP: |
| result = new IPAddressName(val); |
| break; |
| case NAME_OID: |
| result = new OIDName(val); |
| break; |
| default: |
| throw new IOException("unable to parse byte array names of " |
| + "type " + type); |
| } |
| if (debug != null) { |
| debug.println("X509CertSelector.makeGeneralNameInterface() result: " |
| + result.toString()); |
| } |
| } else { |
| if (debug != null) { |
| debug.println("X509CertSelector.makeGeneralName() input name " |
| + "not String or byte array"); |
| } |
| throw new IOException("name not String or byte array"); |
| } |
| return result; |
| } |
| |
| |
| /** |
| * Sets the name constraints criterion. The {@code X509Certificate} |
| * must have subject and subject alternative names that |
| * meet the specified name constraints. |
| * <p> |
| * The name constraints are specified as a byte array. This byte array |
| * should contain the DER encoded form of the name constraints, as they |
| * would appear in the NameConstraints structure defined in RFC 5280 |
| * and X.509. The ASN.1 definition of this structure appears below. |
| * |
| * <pre>{@code |
| * NameConstraints ::= SEQUENCE { |
| * permittedSubtrees [0] GeneralSubtrees OPTIONAL, |
| * excludedSubtrees [1] GeneralSubtrees OPTIONAL } |
| * |
| * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree |
| * |
| * GeneralSubtree ::= SEQUENCE { |
| * base GeneralName, |
| * minimum [0] BaseDistance DEFAULT 0, |
| * maximum [1] BaseDistance OPTIONAL } |
| * |
| * BaseDistance ::= INTEGER (0..MAX) |
| * |
| * GeneralName ::= CHOICE { |
| * otherName [0] OtherName, |
| * rfc822Name [1] IA5String, |
| * dNSName [2] IA5String, |
| * x400Address [3] ORAddress, |
| * directoryName [4] Name, |
| * ediPartyName [5] EDIPartyName, |
| * uniformResourceIdentifier [6] IA5String, |
| * iPAddress [7] OCTET STRING, |
| * registeredID [8] OBJECT IDENTIFIER} |
| * }</pre> |
| * <p> |
| * Note that the byte array supplied here is cloned to protect against |
| * subsequent modifications. |
| * |
| * @param bytes a byte array containing the ASN.1 DER encoding of |
| * a NameConstraints extension to be used for checking |
| * name constraints. Only the value of the extension is |
| * included, not the OID or criticality flag. Can be |
| * {@code null}, |
| * in which case no name constraints check will be performed. |
| * @throws IOException if a parsing error occurs |
| * @see #getNameConstraints |
| */ |
| public void setNameConstraints(byte[] bytes) throws IOException { |
| if (bytes == null) { |
| ncBytes = null; |
| nc = null; |
| } else { |
| ncBytes = bytes.clone(); |
| nc = new NameConstraintsExtension(FALSE, bytes); |
| } |
| } |
| |
| /** |
| * Sets the basic constraints constraint. If the value is greater than or |
| * equal to zero, {@code X509Certificates} must include a |
| * basicConstraints extension with |
| * a pathLen of at least this value. If the value is -2, only end-entity |
| * certificates are accepted. If the value is -1, no check is done. |
| * <p> |
| * This constraint is useful when building a certification path forward |
| * (from the target toward the trust anchor. If a partial path has been |
| * built, any candidate certificate must have a maxPathLen value greater |
| * than or equal to the number of certificates in the partial path. |
| * |
| * @param minMaxPathLen the value for the basic constraints constraint |
| * @throws IllegalArgumentException if the value is less than -2 |
| * @see #getBasicConstraints |
| */ |
| public void setBasicConstraints(int minMaxPathLen) { |
| if (minMaxPathLen < -2) { |
| throw new IllegalArgumentException("basic constraints less than -2"); |
| } |
| basicConstraints = minMaxPathLen; |
| } |
| |
| /** |
| * Sets the policy constraint. The {@code X509Certificate} must |
| * include at least one of the specified policies in its certificate |
| * policies extension. If {@code certPolicySet} is empty, then the |
| * {@code X509Certificate} must include at least some specified policy |
| * in its certificate policies extension. If {@code certPolicySet} is |
| * {@code null}, no policy check will be performed. |
| * <p> |
| * Note that the {@code Set} is cloned to protect against |
| * subsequent modifications. |
| * |
| * @param certPolicySet a {@code Set} of certificate policy OIDs in |
| * string format (or {@code null}). Each OID is |
| * represented by a set of nonnegative integers |
| * separated by periods. |
| * @throws IOException if a parsing error occurs on the OID such as |
| * the first component is not 0, 1 or 2 or the second component is |
| * greater than 39. |
| * @see #getPolicy |
| */ |
| public void setPolicy(Set<String> certPolicySet) throws IOException { |
| if (certPolicySet == null) { |
| policySet = null; |
| policy = null; |
| } else { |
| // Snapshot set and parse it |
| Set<String> tempSet = Collections.unmodifiableSet |
| (new HashSet<>(certPolicySet)); |
| /* Convert to Vector of ObjectIdentifiers */ |
| Iterator<String> i = tempSet.iterator(); |
| Vector<CertificatePolicyId> polIdVector = new Vector<>(); |
| while (i.hasNext()) { |
| Object o = i.next(); |
| if (!(o instanceof String)) { |
| throw new IOException("non String in certPolicySet"); |
| } |
| polIdVector.add(new CertificatePolicyId(new ObjectIdentifier( |
| (String)o))); |
| } |
| // If everything went OK, make the changes |
| policySet = tempSet; |
| policy = new CertificatePolicySet(polIdVector); |
| } |
| } |
| |
| /** |
| * Sets the pathToNames criterion. The {@code X509Certificate} must |
| * not include name constraints that would prohibit building a |
| * path to the specified names. |
| * <p> |
| * This method allows the caller to specify, with a single method call, |
| * the complete set of names which the {@code X509Certificates}'s |
| * name constraints must permit. The specified value replaces |
| * the previous value for the pathToNames criterion. |
| * <p> |
| * This constraint is useful when building a certification path forward |
| * (from the target toward the trust anchor. If a partial path has been |
| * built, any candidate certificate must not include name constraints that |
| * would prohibit building a path to any of the names in the partial path. |
| * <p> |
| * The {@code names} parameter (if not {@code null}) is a |
| * {@code Collection} with one |
| * entry for each name to be included in the pathToNames |
| * criterion. Each entry is a {@code List} whose first entry is an |
| * {@code Integer} (the name type, 0-8) and whose second |
| * entry is a {@code String} or a byte array (the name, in |
| * string or ASN.1 DER encoded form, respectively). |
| * There can be multiple names of the same type. If {@code null} |
| * is supplied as the value for this argument, no |
| * pathToNames check will be performed. |
| * <p> |
| * Each name in the {@code Collection} |
| * may be specified either as a {@code String} or as an ASN.1 encoded |
| * byte array. For more details about the formats used, see |
| * {@link #addPathToName(int type, String name) |
| * addPathToName(int type, String name)} and |
| * {@link #addPathToName(int type, byte [] name) |
| * addPathToName(int type, byte [] name)}. |
| * <p> |
| * <strong>Note:</strong> for distinguished names, specify the byte |
| * array form instead of the String form. See the note in |
| * {@link #addPathToName(int, String)} for more information. |
| * <p> |
| * Note that the {@code names} parameter can contain duplicate |
| * names (same name and name type), but they may be removed from the |
| * {@code Collection} of names returned by the |
| * {@link #getPathToNames getPathToNames} method. |
| * <p> |
| * Note that a deep copy is performed on the {@code Collection} to |
| * protect against subsequent modifications. |
| * |
| * @param names a {@code Collection} with one entry per name |
| * (or {@code null}) |
| * @throws IOException if a parsing error occurs |
| * @see #getPathToNames |
| */ |
| public void setPathToNames(Collection<List<?>> names) throws IOException { |
| if ((names == null) || names.isEmpty()) { |
| pathToNames = null; |
| pathToGeneralNames = null; |
| } else { |
| Set<List<?>> tempNames = cloneAndCheckNames(names); |
| pathToGeneralNames = parseNames(tempNames); |
| // Ensure that we either set both of these or neither |
| pathToNames = tempNames; |
| } |
| } |
| |
| // called from CertPathHelper |
| void setPathToNamesInternal(Set<GeneralNameInterface> names) { |
| // set names to non-null dummy value |
| // this breaks getPathToNames() |
| pathToNames = Collections.<List<?>>emptySet(); |
| pathToGeneralNames = names; |
| } |
| |
| /** |
| * Adds a name to the pathToNames criterion. The {@code X509Certificate} |
| * must not include name constraints that would prohibit building a |
| * path to the specified name. |
| * <p> |
| * This method allows the caller to add a name to the set of names which |
| * the {@code X509Certificates}'s name constraints must permit. |
| * The specified name is added to any previous value for the |
| * pathToNames criterion. If the name is a duplicate, it may be ignored. |
| * <p> |
| * The name is provided in string format. RFC 822, DNS, and URI names |
| * use the well-established string formats for those types (subject to |
| * the restrictions included in RFC 5280). IPv4 address names are |
| * supplied using dotted quad notation. OID address names are represented |
| * as a series of nonnegative integers separated by periods. And |
| * directory names (distinguished names) are supplied in RFC 2253 format. |
| * No standard string format is defined for otherNames, X.400 names, |
| * EDI party names, IPv6 address names, or any other type of names. They |
| * should be specified using the |
| * {@link #addPathToName(int type, byte [] name) |
| * addPathToName(int type, byte [] name)} method. |
| * <p> |
| * <strong>Note:</strong> for distinguished names, use |
| * {@linkplain #addPathToName(int, byte[])} instead. |
| * This method should not be relied on as it can fail to match some |
| * certificates because of a loss of encoding information in the RFC 2253 |
| * String form of some distinguished names. |
| * |
| * @param type the name type (0-8, as specified in |
| * RFC 5280, section 4.2.1.6) |
| * @param name the name in string form |
| * @throws IOException if a parsing error occurs |
| */ |
| public void addPathToName(int type, String name) throws IOException { |
| addPathToNameInternal(type, name); |
| } |
| |
| /** |
| * Adds a name to the pathToNames criterion. The {@code X509Certificate} |
| * must not include name constraints that would prohibit building a |
| * path to the specified name. |
| * <p> |
| * This method allows the caller to add a name to the set of names which |
| * the {@code X509Certificates}'s name constraints must permit. |
| * The specified name is added to any previous value for the |
| * pathToNames criterion. If the name is a duplicate, it may be ignored. |
| * <p> |
| * The name is provided as a byte array. This byte array should contain |
| * the DER encoded name, as it would appear in the GeneralName structure |
| * defined in RFC 5280 and X.509. The ASN.1 definition of this structure |
| * appears in the documentation for |
| * {@link #addSubjectAlternativeName(int type, byte [] name) |
| * addSubjectAlternativeName(int type, byte [] name)}. |
| * <p> |
| * Note that the byte array supplied here is cloned to protect against |
| * subsequent modifications. |
| * |
| * @param type the name type (0-8, as specified in |
| * RFC 5280, section 4.2.1.6) |
| * @param name a byte array containing the name in ASN.1 DER encoded form |
| * @throws IOException if a parsing error occurs |
| */ |
| public void addPathToName(int type, byte [] name) throws IOException { |
| // clone because byte arrays are modifiable |
| addPathToNameInternal(type, name.clone()); |
| } |
| |
| /** |
| * A private method that adds a name (String or byte array) to the |
| * pathToNames criterion. The {@code X509Certificate} must contain |
| * the specified pathToName. |
| * |
| * @param type the name type (0-8, as specified in |
| * RFC 5280, section 4.2.1.6) |
| * @param name the name in string or byte array form |
| * @throws IOException if an encoding error occurs (incorrect form for DN) |
| */ |
| private void addPathToNameInternal(int type, Object name) |
| throws IOException { |
| // First, ensure that the name parses |
| GeneralNameInterface tempName = makeGeneralNameInterface(type, name); |
| if (pathToGeneralNames == null) { |
| pathToNames = new HashSet<>(); |
| pathToGeneralNames = new HashSet<>(); |
| } |
| List<Object> list = new ArrayList<>(2); |
| list.add(Integer.valueOf(type)); |
| list.add(name); |
| pathToNames.add(list); |
| pathToGeneralNames.add(tempName); |
| } |
| |
| /** |
| * Returns the certificateEquals criterion. The specified |
| * {@code X509Certificate} must be equal to the |
| * {@code X509Certificate} passed to the {@code match} method. |
| * If {@code null}, this check is not applied. |
| * |
| * @return the {@code X509Certificate} to match (or {@code null}) |
| * @see #setCertificate |
| */ |
| public X509Certificate getCertificate() { |
| return x509Cert; |
| } |
| |
| /** |
| * Returns the serialNumber criterion. The specified serial number |
| * must match the certificate serial number in the |
| * {@code X509Certificate}. If {@code null}, any certificate |
| * serial number will do. |
| * |
| * @return the certificate serial number to match |
| * (or {@code null}) |
| * @see #setSerialNumber |
| */ |
| public BigInteger getSerialNumber() { |
| return serialNumber; |
| } |
| |
| /** |
| * Returns the issuer criterion as an {@code X500Principal}. This |
| * distinguished name must match the issuer distinguished name in the |
| * {@code X509Certificate}. If {@code null}, the issuer criterion |
| * is disabled and any issuer distinguished name will do. |
| * |
| * @return the required issuer distinguished name as X500Principal |
| * (or {@code null}) |
| * @since 1.5 |
| */ |
| public X500Principal getIssuer() { |
| return issuer; |
| } |
| |
| /** |
| * <strong>Denigrated</strong>, use {@linkplain #getIssuer()} or |
| * {@linkplain #getIssuerAsBytes()} instead. This method should not be |
| * relied on as it can fail to match some certificates because of a loss of |
| * encoding information in the RFC 2253 String form of some distinguished |
| * names. |
| * <p> |
| * Returns the issuer criterion as a {@code String}. This |
| * distinguished name must match the issuer distinguished name in the |
| * {@code X509Certificate}. If {@code null}, the issuer criterion |
| * is disabled and any issuer distinguished name will do. |
| * <p> |
| * If the value returned is not {@code null}, it is a |
| * distinguished name, in RFC 2253 format. |
| * |
| * @return the required issuer distinguished name in RFC 2253 format |
| * (or {@code null}) |
| */ |
| public String getIssuerAsString() { |
| return (issuer == null ? null : issuer.getName()); |
| } |
| |
| /** |
| * Returns the issuer criterion as a byte array. This distinguished name |
| * must match the issuer distinguished name in the |
| * {@code X509Certificate}. If {@code null}, the issuer criterion |
| * is disabled and any issuer distinguished name will do. |
| * <p> |
| * If the value returned is not {@code null}, it is a byte |
| * array containing a single DER encoded distinguished name, as defined in |
| * X.501. The ASN.1 notation for this structure is supplied in the |
| * documentation for |
| * {@link #setIssuer(byte [] issuerDN) setIssuer(byte [] issuerDN)}. |
| * <p> |
| * Note that the byte array returned is cloned to protect against |
| * subsequent modifications. |
| * |
| * @return a byte array containing the required issuer distinguished name |
| * in ASN.1 DER format (or {@code null}) |
| * @throws IOException if an encoding error occurs |
| */ |
| public byte[] getIssuerAsBytes() throws IOException { |
| return (issuer == null ? null: issuer.getEncoded()); |
| } |
| |
| /** |
| * Returns the subject criterion as an {@code X500Principal}. This |
| * distinguished name must match the subject distinguished name in the |
| * {@code X509Certificate}. If {@code null}, the subject criterion |
| * is disabled and any subject distinguished name will do. |
| * |
| * @return the required subject distinguished name as X500Principal |
| * (or {@code null}) |
| * @since 1.5 |
| */ |
| public X500Principal getSubject() { |
| return subject; |
| } |
| |
| /** |
| * <strong>Denigrated</strong>, use {@linkplain #getSubject()} or |
| * {@linkplain #getSubjectAsBytes()} instead. This method should not be |
| * relied on as it can fail to match some certificates because of a loss of |
| * encoding information in the RFC 2253 String form of some distinguished |
| * names. |
| * <p> |
| * Returns the subject criterion as a {@code String}. This |
| * distinguished name must match the subject distinguished name in the |
| * {@code X509Certificate}. If {@code null}, the subject criterion |
| * is disabled and any subject distinguished name will do. |
| * <p> |
| * If the value returned is not {@code null}, it is a |
| * distinguished name, in RFC 2253 format. |
| * |
| * @return the required subject distinguished name in RFC 2253 format |
| * (or {@code null}) |
| */ |
| public String getSubjectAsString() { |
| return (subject == null ? null : subject.getName()); |
| } |
| |
| /** |
| * Returns the subject criterion as a byte array. This distinguished name |
| * must match the subject distinguished name in the |
| * {@code X509Certificate}. If {@code null}, the subject criterion |
| * is disabled and any subject distinguished name will do. |
| * <p> |
| * If the value returned is not {@code null}, it is a byte |
| * array containing a single DER encoded distinguished name, as defined in |
| * X.501. The ASN.1 notation for this structure is supplied in the |
| * documentation for |
| * {@link #setSubject(byte [] subjectDN) setSubject(byte [] subjectDN)}. |
| * <p> |
| * Note that the byte array returned is cloned to protect against |
| * subsequent modifications. |
| * |
| * @return a byte array containing the required subject distinguished name |
| * in ASN.1 DER format (or {@code null}) |
| * @throws IOException if an encoding error occurs |
| */ |
| public byte[] getSubjectAsBytes() throws IOException { |
| return (subject == null ? null : subject.getEncoded()); |
| } |
| |
| /** |
| * Returns the subjectKeyIdentifier criterion. The |
| * {@code X509Certificate} must contain a SubjectKeyIdentifier |
| * extension with the specified value. If {@code null}, no |
| * subjectKeyIdentifier check will be done. |
| * <p> |
| * Note that the byte array returned is cloned to protect against |
| * subsequent modifications. |
| * |
| * @return the key identifier (or {@code null}) |
| * @see #setSubjectKeyIdentifier |
| */ |
| public byte[] getSubjectKeyIdentifier() { |
| if (subjectKeyID == null) { |
| return null; |
| } |
| return subjectKeyID.clone(); |
| } |
| |
| /** |
| * Returns the authorityKeyIdentifier criterion. The |
| * {@code X509Certificate} must contain a AuthorityKeyIdentifier |
| * extension with the specified value. If {@code null}, no |
| * authorityKeyIdentifier check will be done. |
| * <p> |
| * Note that the byte array returned is cloned to protect against |
| * subsequent modifications. |
| * |
| * @return the key identifier (or {@code null}) |
| * @see #setAuthorityKeyIdentifier |
| */ |
| public byte[] getAuthorityKeyIdentifier() { |
| if (authorityKeyID == null) { |
| return null; |
| } |
| return authorityKeyID.clone(); |
| } |
| |
| /** |
| * Returns the certificateValid criterion. The specified date must fall |
| * within the certificate validity period for the |
| * {@code X509Certificate}. If {@code null}, no certificateValid |
| * check will be done. |
| * <p> |
| * Note that the {@code Date} returned is cloned to protect against |
| * subsequent modifications. |
| * |
| * @return the {@code Date} to check (or {@code null}) |
| * @see #setCertificateValid |
| */ |
| public Date getCertificateValid() { |
| if (certificateValid == null) { |
| return null; |
| } |
| return (Date)certificateValid.clone(); |
| } |
| |
| /** |
| * Returns the privateKeyValid criterion. The specified date must fall |
| * within the private key validity period for the |
| * {@code X509Certificate}. If {@code null}, no privateKeyValid |
| * check will be done. |
| * <p> |
| * Note that the {@code Date} returned is cloned to protect against |
| * subsequent modifications. |
| * |
| * @return the {@code Date} to check (or {@code null}) |
| * @see #setPrivateKeyValid |
| */ |
| public Date getPrivateKeyValid() { |
| if (privateKeyValid == null) { |
| return null; |
| } |
| return (Date)privateKeyValid.clone(); |
| } |
| |
| /** |
| * Returns the subjectPublicKeyAlgID criterion. The |
| * {@code X509Certificate} must contain a subject public key |
| * with the specified algorithm. If {@code null}, no |
| * subjectPublicKeyAlgID check will be done. |
| * |
| * @return the object identifier (OID) of the signature algorithm to check |
| * for (or {@code null}). An OID is represented by a set of |
| * nonnegative integers separated by periods. |
| * @see #setSubjectPublicKeyAlgID |
| */ |
| public String getSubjectPublicKeyAlgID() { |
| if (subjectPublicKeyAlgID == null) { |
| return null; |
| } |
| return subjectPublicKeyAlgID.toString(); |
| } |
| |
| /** |
| * Returns the subjectPublicKey criterion. The |
| * {@code X509Certificate} must contain the specified subject |
| * public key. If {@code null}, no subjectPublicKey check will be done. |
| * |
| * @return the subject public key to check for (or {@code null}) |
| * @see #setSubjectPublicKey |
| */ |
| public PublicKey getSubjectPublicKey() { |
| return subjectPublicKey; |
| } |
| |
| /** |
| * Returns the keyUsage criterion. The {@code X509Certificate} |
| * must allow the specified keyUsage values. If null, no keyUsage |
| * check will be done. |
| * <p> |
| * Note that the boolean array returned is cloned to protect against |
| * subsequent modifications. |
| * |
| * @return a boolean array in the same format as the boolean |
| * array returned by |
| * {@link X509Certificate#getKeyUsage() X509Certificate.getKeyUsage()}. |
| * Or {@code null}. |
| * @see #setKeyUsage |
| */ |
| public boolean[] getKeyUsage() { |
| if (keyUsage == null) { |
| return null; |
| } |
| return keyUsage.clone(); |
| } |
| |
| /** |
| * Returns the extendedKeyUsage criterion. The {@code X509Certificate} |
| * must allow the specified key purposes in its extended key usage |
| * extension. If the {@code keyPurposeSet} returned is empty or |
| * {@code null}, no extendedKeyUsage check will be done. Note that an |
| * {@code X509Certificate} that has no extendedKeyUsage extension |
| * implicitly allows all key purposes. |
| * |
| * @return an immutable {@code Set} of key purpose OIDs in string |
| * format (or {@code null}) |
| * @see #setExtendedKeyUsage |
| */ |
| public Set<String> getExtendedKeyUsage() { |
| return keyPurposeSet; |
| } |
| |
| /** |
| * Indicates if the {@code X509Certificate} must contain all |
| * or at least one of the subjectAlternativeNames |
| * specified in the {@link #setSubjectAlternativeNames |
| * setSubjectAlternativeNames} or {@link #addSubjectAlternativeName |
| * addSubjectAlternativeName} methods. If {@code true}, |
| * the {@code X509Certificate} must contain all of the |
| * specified subject alternative names. If {@code false}, the |
| * {@code X509Certificate} must contain at least one of the |
| * specified subject alternative names. |
| * |
| * @return {@code true} if the flag is enabled; |
| * {@code false} if the flag is disabled. The flag is |
| * {@code true} by default. |
| * @see #setMatchAllSubjectAltNames |
| */ |
| public boolean getMatchAllSubjectAltNames() { |
| return matchAllSubjectAltNames; |
| } |
| |
| /** |
| * Returns a copy of the subjectAlternativeNames criterion. |
| * The {@code X509Certificate} must contain all or at least one |
| * of the specified subjectAlternativeNames, depending on the value |
| * of the matchAllNames flag (see {@link #getMatchAllSubjectAltNames |
| * getMatchAllSubjectAltNames}). If the value returned is |
| * {@code null}, no subjectAlternativeNames check will be performed. |
| * <p> |
| * If the value returned is not {@code null}, it is a |
| * {@code Collection} with |
| * one entry for each name to be included in the subject alternative name |
| * criterion. Each entry is a {@code List} whose first entry is an |
| * {@code Integer} (the name type, 0-8) and whose second |
| * entry is a {@code String} or a byte array (the name, in |
| * string or ASN.1 DER encoded form, respectively). |
| * There can be multiple names of the same type. Note that the |
| * {@code Collection} returned may contain duplicate names (same name |
| * and name type). |
| * <p> |
| * Each subject alternative name in the {@code Collection} |
| * may be specified either as a {@code String} or as an ASN.1 encoded |
| * byte array. For more details about the formats used, see |
| * {@link #addSubjectAlternativeName(int type, String name) |
| * addSubjectAlternativeName(int type, String name)} and |
| * {@link #addSubjectAlternativeName(int type, byte [] name) |
| * addSubjectAlternativeName(int type, byte [] name)}. |
| * <p> |
| * Note that a deep copy is performed on the {@code Collection} to |
| * protect against subsequent modifications. |
| * |
| * @return a {@code Collection} of names (or {@code null}) |
| * @see #setSubjectAlternativeNames |
| */ |
| public Collection<List<?>> getSubjectAlternativeNames() { |
| if (subjectAlternativeNames == null) { |
| return null; |
| } |
| return cloneNames(subjectAlternativeNames); |
| } |
| |
| /** |
| * Clone an object of the form passed to |
| * setSubjectAlternativeNames and setPathToNames. |
| * Throw a {@code RuntimeException} if the argument is malformed. |
| * <p> |
| * This method wraps cloneAndCheckNames, changing any |
| * {@code IOException} into a {@code RuntimeException}. This |
| * method should be used when the object being |
| * cloned has already been checked, so there should never be any exceptions. |
| * |
| * @param names a {@code Collection} with one entry per name. |
| * Each entry is a {@code List} whose first entry |
| * is an Integer (the name type, 0-8) and whose second |
| * entry is a String or a byte array (the name, in |
| * string or ASN.1 DER encoded form, respectively). |
| * There can be multiple names of the same type. Null |
| * is not an acceptable value. |
| * @return a deep copy of the specified {@code Collection} |
| * @throws RuntimeException if a parsing error occurs |
| */ |
| private static Set<List<?>> cloneNames(Collection<List<?>> names) { |
| try { |
| return cloneAndCheckNames(names); |
| } catch (IOException e) { |
| throw new RuntimeException("cloneNames encountered IOException: " + |
| e.getMessage()); |
| } |
| } |
| |
| /** |
| * Clone and check an argument of the form passed to |
| * setSubjectAlternativeNames and setPathToNames. |
| * Throw an {@code IOException} if the argument is malformed. |
| * |
| * @param names a {@code Collection} with one entry per name. |
| * Each entry is a {@code List} whose first entry |
| * is an Integer (the name type, 0-8) and whose second |
| * entry is a String or a byte array (the name, in |
| * string or ASN.1 DER encoded form, respectively). |
| * There can be multiple names of the same type. |
| * {@code null} is not an acceptable value. |
| * @return a deep copy of the specified {@code Collection} |
| * @throws IOException if a parsing error occurs |
| */ |
| private static Set<List<?>> cloneAndCheckNames(Collection<List<?>> names) throws IOException { |
| // Copy the Lists and Collection |
| Set<List<?>> namesCopy = new HashSet<>(); |
| for (List<?> o : names) |
| { |
| namesCopy.add(new ArrayList<>(o)); |
| } |
| |
| // Check the contents of the Lists and clone any byte arrays |
| for (List<?> list : namesCopy) { |
| @SuppressWarnings("unchecked") // See javadoc for parameter "names". |
| List<Object> nameList = (List<Object>)list; |
| if (nameList.size() != 2) { |
| throw new IOException("name list size not 2"); |
| } |
| Object o = nameList.get(0); |
| if (!(o instanceof Integer)) { |
| throw new IOException("expected an Integer"); |
| } |
| int nameType = ((Integer)o).intValue(); |
| if ((nameType < 0) || (nameType > 8)) { |
| throw new IOException("name type not 0-8"); |
| } |
| Object nameObject = nameList.get(1); |
| if (!(nameObject instanceof byte[]) && |
| !(nameObject instanceof String)) { |
| if (debug != null) { |
| debug.println("X509CertSelector.cloneAndCheckNames() " |
| + "name not byte array"); |
| } |
| throw new IOException("name not byte array or String"); |
| } |
| if (nameObject instanceof byte[]) { |
| nameList.set(1, ((byte[]) nameObject).clone()); |
| } |
| } |
| return namesCopy; |
| } |
| |
| /** |
| * Returns the name constraints criterion. The {@code X509Certificate} |
| * must have subject and subject alternative names that |
| * meet the specified name constraints. |
| * <p> |
| * The name constraints are returned as a byte array. This byte array |
| * contains the DER encoded form of the name constraints, as they |
| * would appear in the NameConstraints structure defined in RFC 5280 |
| * and X.509. The ASN.1 notation for this structure is supplied in the |
| * documentation for |
| * {@link #setNameConstraints(byte [] bytes) setNameConstraints(byte [] bytes)}. |
| * <p> |
| * Note that the byte array returned is cloned to protect against |
| * subsequent modifications. |
| * |
| * @return a byte array containing the ASN.1 DER encoding of |
| * a NameConstraints extension used for checking name constraints. |
| * {@code null} if no name constraints check will be performed. |
| * @see #setNameConstraints |
| */ |
| public byte[] getNameConstraints() { |
| if (ncBytes == null) { |
| return null; |
| } else { |
| return ncBytes.clone(); |
| } |
| } |
| |
| /** |
| * Returns the basic constraints constraint. If the value is greater than |
| * or equal to zero, the {@code X509Certificates} must include a |
| * basicConstraints extension with a pathLen of at least this value. |
| * If the value is -2, only end-entity certificates are accepted. If |
| * the value is -1, no basicConstraints check is done. |
| * |
| * @return the value for the basic constraints constraint |
| * @see #setBasicConstraints |
| */ |
| public int getBasicConstraints() { |
| return basicConstraints; |
| } |
| |
| /** |
| * Returns the policy criterion. The {@code X509Certificate} must |
| * include at least one of the specified policies in its certificate policies |
| * extension. If the {@code Set} returned is empty, then the |
| * {@code X509Certificate} must include at least some specified policy |
| * in its certificate policies extension. If the {@code Set} returned is |
| * {@code null}, no policy check will be performed. |
| * |
| * @return an immutable {@code Set} of certificate policy OIDs in |
| * string format (or {@code null}) |
| * @see #setPolicy |
| */ |
| public Set<String> getPolicy() { |
| return policySet; |
| } |
| |
| /** |
| * Returns a copy of the pathToNames criterion. The |
| * {@code X509Certificate} must not include name constraints that would |
| * prohibit building a path to the specified names. If the value |
| * returned is {@code null}, no pathToNames check will be performed. |
| * <p> |
| * If the value returned is not {@code null}, it is a |
| * {@code Collection} with one |
| * entry for each name to be included in the pathToNames |
| * criterion. Each entry is a {@code List} whose first entry is an |
| * {@code Integer} (the name type, 0-8) and whose second |
| * entry is a {@code String} or a byte array (the name, in |
| * string or ASN.1 DER encoded form, respectively). |
| * There can be multiple names of the same type. Note that the |
| * {@code Collection} returned may contain duplicate names (same |
| * name and name type). |
| * <p> |
| * Each name in the {@code Collection} |
| * may be specified either as a {@code String} or as an ASN.1 encoded |
| * byte array. For more details about the formats used, see |
| * {@link #addPathToName(int type, String name) |
| * addPathToName(int type, String name)} and |
| * {@link #addPathToName(int type, byte [] name) |
| * addPathToName(int type, byte [] name)}. |
| * <p> |
| * Note that a deep copy is performed on the {@code Collection} to |
| * protect against subsequent modifications. |
| * |
| * @return a {@code Collection} of names (or {@code null}) |
| * @see #setPathToNames |
| */ |
| public Collection<List<?>> getPathToNames() { |
| if (pathToNames == null) { |
| return null; |
| } |
| return cloneNames(pathToNames); |
| } |
| |
| /** |
| * Return a printable representation of the {@code CertSelector}. |
| * |
| * @return a {@code String} describing the contents of the |
| * {@code CertSelector} |
| */ |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("X509CertSelector: [\n"); |
| if (x509Cert != null) { |
| sb.append(" Certificate: " + x509Cert.toString() + "\n"); |
| } |
| if (serialNumber != null) { |
| sb.append(" Serial Number: " + serialNumber.toString() + "\n"); |
| } |
| if (issuer != null) { |
| sb.append(" Issuer: " + getIssuerAsString() + "\n"); |
| } |
| if (subject != null) { |
| sb.append(" Subject: " + getSubjectAsString() + "\n"); |
| } |
| sb.append(" matchAllSubjectAltNames flag: " |
| + String.valueOf(matchAllSubjectAltNames) + "\n"); |
| if (subjectAlternativeNames != null) { |
| sb.append(" SubjectAlternativeNames:\n"); |
| Iterator<List<?>> i = subjectAlternativeNames.iterator(); |
| while (i.hasNext()) { |
| List<?> list = i.next(); |
| sb.append(" type " + list.get(0) + |
| ", name " + list.get(1) + "\n"); |
| } |
| } |
| if (subjectKeyID != null) { |
| HexDumpEncoder enc = new HexDumpEncoder(); |
| sb.append(" Subject Key Identifier: " + |
| enc.encodeBuffer(subjectKeyID) + "\n"); |
| } |
| if (authorityKeyID != null) { |
| HexDumpEncoder enc = new HexDumpEncoder(); |
| sb.append(" Authority Key Identifier: " + |
| enc.encodeBuffer(authorityKeyID) + "\n"); |
| } |
| if (certificateValid != null) { |
| sb.append(" Certificate Valid: " + |
| certificateValid.toString() + "\n"); |
| } |
| if (privateKeyValid != null) { |
| sb.append(" Private Key Valid: " + |
| privateKeyValid.toString() + "\n"); |
| } |
| if (subjectPublicKeyAlgID != null) { |
| sb.append(" Subject Public Key AlgID: " + |
| subjectPublicKeyAlgID.toString() + "\n"); |
| } |
| if (subjectPublicKey != null) { |
| sb.append(" Subject Public Key: " + |
| subjectPublicKey.toString() + "\n"); |
| } |
| if (keyUsage != null) { |
| sb.append(" Key Usage: " + keyUsageToString(keyUsage) + "\n"); |
| } |
| if (keyPurposeSet != null) { |
| sb.append(" Extended Key Usage: " + |
| keyPurposeSet.toString() + "\n"); |
| } |
| if (policy != null) { |
| sb.append(" Policy: " + policy.toString() + "\n"); |
| } |
| if (pathToGeneralNames != null) { |
| sb.append(" Path to names:\n"); |
| Iterator<GeneralNameInterface> i = pathToGeneralNames.iterator(); |
| while (i.hasNext()) { |
| sb.append(" " + i.next() + "\n"); |
| } |
| } |
| sb.append("]"); |
| return sb.toString(); |
| } |
| |
| // Copied from sun.security.x509.KeyUsageExtension |
| // (without calling the superclass) |
| /** |
| * Returns a printable representation of the KeyUsage. |
| */ |
| private static String keyUsageToString(boolean[] k) { |
| String s = "KeyUsage [\n"; |
| try { |
| if (k[0]) { |
| s += " DigitalSignature\n"; |
| } |
| if (k[1]) { |
| s += " Non_repudiation\n"; |
| } |
| if (k[2]) { |
| s += " Key_Encipherment\n"; |
| } |
| if (k[3]) { |
| s += " Data_Encipherment\n"; |
| } |
| if (k[4]) { |
| s += " Key_Agreement\n"; |
| } |
| if (k[5]) { |
| s += " Key_CertSign\n"; |
| } |
| if (k[6]) { |
| s += " Crl_Sign\n"; |
| } |
| if (k[7]) { |
| s += " Encipher_Only\n"; |
| } |
| if (k[8]) { |
| s += " Decipher_Only\n"; |
| } |
| } catch (ArrayIndexOutOfBoundsException ex) {} |
| |
| s += "]\n"; |
| |
| return (s); |
| } |
| |
| /** |
| * Returns an Extension object given any X509Certificate and extension oid. |
| * Throw an {@code IOException} if the extension byte value is |
| * malformed. |
| * |
| * @param cert a {@code X509Certificate} |
| * @param extId an {@code integer} which specifies the extension index. |
| * Currently, the supported extensions are as follows: |
| * index 0 - PrivateKeyUsageExtension |
| * index 1 - SubjectAlternativeNameExtension |
| * index 2 - NameConstraintsExtension |
| * index 3 - CertificatePoliciesExtension |
| * index 4 - ExtendedKeyUsageExtension |
| * @return an {@code Extension} object whose real type is as specified |
| * by the extension oid. |
| * @throws IOException if cannot construct the {@code Extension} |
| * object with the extension encoding retrieved from the passed in |
| * {@code X509Certificate}. |
| */ |
| private static Extension getExtensionObject(X509Certificate cert, int extId) |
| throws IOException { |
| if (cert instanceof X509CertImpl) { |
| X509CertImpl impl = (X509CertImpl)cert; |
| switch (extId) { |
| case PRIVATE_KEY_USAGE_ID: |
| return impl.getPrivateKeyUsageExtension(); |
| case SUBJECT_ALT_NAME_ID: |
| return impl.getSubjectAlternativeNameExtension(); |
| case NAME_CONSTRAINTS_ID: |
| return impl.getNameConstraintsExtension(); |
| case CERT_POLICIES_ID: |
| return impl.getCertificatePoliciesExtension(); |
| case EXTENDED_KEY_USAGE_ID: |
| return impl.getExtendedKeyUsageExtension(); |
| default: |
| return null; |
| } |
| } |
| byte[] rawExtVal = cert.getExtensionValue(EXTENSION_OIDS[extId]); |
| if (rawExtVal == null) { |
| return null; |
| } |
| DerInputStream in = new DerInputStream(rawExtVal); |
| byte[] encoded = in.getOctetString(); |
| switch (extId) { |
| case PRIVATE_KEY_USAGE_ID: |
| try { |
| return new PrivateKeyUsageExtension(FALSE, encoded); |
| } catch (CertificateException ex) { |
| throw new IOException(ex.getMessage()); |
| } |
| case SUBJECT_ALT_NAME_ID: |
| return new SubjectAlternativeNameExtension(FALSE, encoded); |
| case NAME_CONSTRAINTS_ID: |
| return new NameConstraintsExtension(FALSE, encoded); |
| case CERT_POLICIES_ID: |
| return new CertificatePoliciesExtension(FALSE, encoded); |
| case EXTENDED_KEY_USAGE_ID: |
| return new ExtendedKeyUsageExtension(FALSE, encoded); |
| default: |
| return null; |
| } |
| } |
| |
| /** |
| * Decides whether a {@code Certificate} should be selected. |
| * |
| * @param cert the {@code Certificate} to be checked |
| * @return {@code true} if the {@code Certificate} should be |
| * selected, {@code false} otherwise |
| */ |
| public boolean match(Certificate cert) { |
| if (!(cert instanceof X509Certificate)) { |
| return false; |
| } |
| X509Certificate xcert = (X509Certificate)cert; |
| |
| if (debug != null) { |
| debug.println("X509CertSelector.match(SN: " |
| + (xcert.getSerialNumber()).toString(16) + "\n Issuer: " |
| + xcert.getIssuerDN() + "\n Subject: " + xcert.getSubjectDN() |
| + ")"); |
| } |
| |
| /* match on X509Certificate */ |
| if (x509Cert != null) { |
| if (!x509Cert.equals(xcert)) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "certs don't match"); |
| } |
| return false; |
| } |
| } |
| |
| /* match on serial number */ |
| if (serialNumber != null) { |
| if (!serialNumber.equals(xcert.getSerialNumber())) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "serial numbers don't match"); |
| } |
| return false; |
| } |
| } |
| |
| /* match on issuer name */ |
| if (issuer != null) { |
| if (!issuer.equals(xcert.getIssuerX500Principal())) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "issuer DNs don't match"); |
| } |
| return false; |
| } |
| } |
| |
| /* match on subject name */ |
| if (subject != null) { |
| if (!subject.equals(xcert.getSubjectX500Principal())) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "subject DNs don't match"); |
| } |
| return false; |
| } |
| } |
| |
| /* match on certificate validity range */ |
| if (certificateValid != null) { |
| try { |
| xcert.checkValidity(certificateValid); |
| } catch (CertificateException e) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "certificate not within validity period"); |
| } |
| return false; |
| } |
| } |
| |
| /* match on subject public key */ |
| if (subjectPublicKeyBytes != null) { |
| byte[] certKey = xcert.getPublicKey().getEncoded(); |
| if (!Arrays.equals(subjectPublicKeyBytes, certKey)) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "subject public keys don't match"); |
| } |
| return false; |
| } |
| } |
| |
| boolean result = matchBasicConstraints(xcert) |
| && matchKeyUsage(xcert) |
| && matchExtendedKeyUsage(xcert) |
| && matchSubjectKeyID(xcert) |
| && matchAuthorityKeyID(xcert) |
| && matchPrivateKeyValid(xcert) |
| && matchSubjectPublicKeyAlgID(xcert) |
| && matchPolicy(xcert) |
| && matchSubjectAlternativeNames(xcert) |
| && matchPathToNames(xcert) |
| && matchNameConstraints(xcert); |
| |
| if (result && (debug != null)) { |
| debug.println("X509CertSelector.match returning: true"); |
| } |
| return result; |
| } |
| |
| /* match on subject key identifier extension value */ |
| private boolean matchSubjectKeyID(X509Certificate xcert) { |
| if (subjectKeyID == null) { |
| return true; |
| } |
| try { |
| byte[] extVal = xcert.getExtensionValue("2.5.29.14"); |
| if (extVal == null) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "no subject key ID extension"); |
| } |
| return false; |
| } |
| DerInputStream in = new DerInputStream(extVal); |
| byte[] certSubjectKeyID = in.getOctetString(); |
| if (certSubjectKeyID == null || |
| !Arrays.equals(subjectKeyID, certSubjectKeyID)) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "subject key IDs don't match"); |
| } |
| return false; |
| } |
| } catch (IOException ex) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "exception in subject key ID check"); |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| /* match on authority key identifier extension value */ |
| private boolean matchAuthorityKeyID(X509Certificate xcert) { |
| if (authorityKeyID == null) { |
| return true; |
| } |
| try { |
| byte[] extVal = xcert.getExtensionValue("2.5.29.35"); |
| if (extVal == null) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "no authority key ID extension"); |
| } |
| return false; |
| } |
| DerInputStream in = new DerInputStream(extVal); |
| byte[] certAuthKeyID = in.getOctetString(); |
| if (certAuthKeyID == null || |
| !Arrays.equals(authorityKeyID, certAuthKeyID)) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "authority key IDs don't match"); |
| } |
| return false; |
| } |
| } catch (IOException ex) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "exception in authority key ID check"); |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| /* match on private key usage range */ |
| private boolean matchPrivateKeyValid(X509Certificate xcert) { |
| if (privateKeyValid == null) { |
| return true; |
| } |
| PrivateKeyUsageExtension ext = null; |
| try { |
| ext = (PrivateKeyUsageExtension) |
| getExtensionObject(xcert, PRIVATE_KEY_USAGE_ID); |
| if (ext != null) { |
| ext.valid(privateKeyValid); |
| } |
| } catch (CertificateExpiredException e1) { |
| if (debug != null) { |
| String time = "n/a"; |
| try { |
| Date notAfter = ext.get(PrivateKeyUsageExtension.NOT_AFTER); |
| time = notAfter.toString(); |
| } catch (CertificateException ex) { |
| // not able to retrieve notAfter value |
| } |
| debug.println("X509CertSelector.match: private key usage not " |
| + "within validity date; ext.NOT_After: " |
| + time + "; X509CertSelector: " |
| + this.toString()); |
| e1.printStackTrace(); |
| } |
| return false; |
| } catch (CertificateNotYetValidException e2) { |
| if (debug != null) { |
| String time = "n/a"; |
| try { |
| Date notBefore = ext.get(PrivateKeyUsageExtension.NOT_BEFORE); |
| time = notBefore.toString(); |
| } catch (CertificateException ex) { |
| // not able to retrieve notBefore value |
| } |
| debug.println("X509CertSelector.match: private key usage not " |
| + "within validity date; ext.NOT_BEFORE: " |
| + time + "; X509CertSelector: " |
| + this.toString()); |
| e2.printStackTrace(); |
| } |
| return false; |
| } catch (IOException e4) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: IOException in " |
| + "private key usage check; X509CertSelector: " |
| + this.toString()); |
| e4.printStackTrace(); |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| /* match on subject public key algorithm OID */ |
| private boolean matchSubjectPublicKeyAlgID(X509Certificate xcert) { |
| if (subjectPublicKeyAlgID == null) { |
| return true; |
| } |
| try { |
| byte[] encodedKey = xcert.getPublicKey().getEncoded(); |
| DerValue val = new DerValue(encodedKey); |
| if (val.tag != DerValue.tag_Sequence) { |
| throw new IOException("invalid key format"); |
| } |
| |
| AlgorithmId algID = AlgorithmId.parse(val.data.getDerValue()); |
| if (debug != null) { |
| debug.println("X509CertSelector.match: subjectPublicKeyAlgID = " |
| + subjectPublicKeyAlgID + ", xcert subjectPublicKeyAlgID = " |
| + algID.getOID()); |
| } |
| if (!subjectPublicKeyAlgID.equals(algID.getOID())) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "subject public key alg IDs don't match"); |
| } |
| return false; |
| } |
| } catch (IOException e5) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: IOException in subject " |
| + "public key algorithm OID check"); |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| /* match on key usage extension value */ |
| private boolean matchKeyUsage(X509Certificate xcert) { |
| if (keyUsage == null) { |
| return true; |
| } |
| boolean[] certKeyUsage = xcert.getKeyUsage(); |
| if (certKeyUsage != null) { |
| for (int keyBit = 0; keyBit < keyUsage.length; keyBit++) { |
| if (keyUsage[keyBit] && |
| ((keyBit >= certKeyUsage.length) || !certKeyUsage[keyBit])) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "key usage bits don't match"); |
| } |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| /* match on extended key usage purpose OIDs */ |
| private boolean matchExtendedKeyUsage(X509Certificate xcert) { |
| if ((keyPurposeSet == null) || keyPurposeSet.isEmpty()) { |
| return true; |
| } |
| try { |
| ExtendedKeyUsageExtension ext = |
| (ExtendedKeyUsageExtension)getExtensionObject(xcert, |
| EXTENDED_KEY_USAGE_ID); |
| if (ext != null) { |
| Vector<ObjectIdentifier> certKeyPurposeVector = |
| ext.get(ExtendedKeyUsageExtension.USAGES); |
| if (!certKeyPurposeVector.contains(ANY_EXTENDED_KEY_USAGE) |
| && !certKeyPurposeVector.containsAll(keyPurposeOIDSet)) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: cert failed " |
| + "extendedKeyUsage criterion"); |
| } |
| return false; |
| } |
| } |
| } catch (IOException ex) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "IOException in extended key usage check"); |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| /* match on subject alternative name extension names */ |
| private boolean matchSubjectAlternativeNames(X509Certificate xcert) { |
| if ((subjectAlternativeNames == null) || subjectAlternativeNames.isEmpty()) { |
| return true; |
| } |
| try { |
| SubjectAlternativeNameExtension sanExt = |
| (SubjectAlternativeNameExtension) getExtensionObject(xcert, |
| SUBJECT_ALT_NAME_ID); |
| if (sanExt == null) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "no subject alternative name extension"); |
| } |
| return false; |
| } |
| GeneralNames certNames = |
| sanExt.get(SubjectAlternativeNameExtension.SUBJECT_NAME); |
| Iterator<GeneralNameInterface> i = |
| subjectAlternativeGeneralNames.iterator(); |
| while (i.hasNext()) { |
| GeneralNameInterface matchName = i.next(); |
| boolean found = false; |
| for (Iterator<GeneralName> t = certNames.iterator(); |
| t.hasNext() && !found; ) { |
| GeneralNameInterface certName = (t.next()).getName(); |
| found = certName.equals(matchName); |
| } |
| if (!found && (matchAllSubjectAltNames || !i.hasNext())) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: subject alternative " |
| + "name " + matchName + " not found"); |
| } |
| return false; |
| } else if (found && !matchAllSubjectAltNames) { |
| break; |
| } |
| } |
| } catch (IOException ex) { |
| if (debug != null) |
| debug.println("X509CertSelector.match: IOException in subject " |
| + "alternative name check"); |
| return false; |
| } |
| return true; |
| } |
| |
| /* match on name constraints */ |
| private boolean matchNameConstraints(X509Certificate xcert) { |
| if (nc == null) { |
| return true; |
| } |
| try { |
| if (!nc.verify(xcert)) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "name constraints not satisfied"); |
| } |
| return false; |
| } |
| } catch (IOException e) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "IOException in name constraints check"); |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| /* match on policy OIDs */ |
| private boolean matchPolicy(X509Certificate xcert) { |
| if (policy == null) { |
| return true; |
| } |
| try { |
| CertificatePoliciesExtension ext = (CertificatePoliciesExtension) |
| getExtensionObject(xcert, CERT_POLICIES_ID); |
| if (ext == null) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "no certificate policy extension"); |
| } |
| return false; |
| } |
| List<PolicyInformation> policies = ext.get(CertificatePoliciesExtension.POLICIES); |
| /* |
| * Convert the Vector of PolicyInformation to a Vector |
| * of CertificatePolicyIds for easier comparison. |
| */ |
| List<CertificatePolicyId> policyIDs = new ArrayList<>(policies.size()); |
| for (PolicyInformation info : policies) { |
| policyIDs.add(info.getPolicyIdentifier()); |
| } |
| if (policy != null) { |
| boolean foundOne = false; |
| /* |
| * if the user passes in an empty policy Set, then |
| * we just want to make sure that the candidate certificate |
| * has some policy OID in its CertPoliciesExtension |
| */ |
| if (policy.getCertPolicyIds().isEmpty()) { |
| if (policyIDs.isEmpty()) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "cert failed policyAny criterion"); |
| } |
| return false; |
| } |
| } else { |
| for (CertificatePolicyId id : policy.getCertPolicyIds()) { |
| if (policyIDs.contains(id)) { |
| foundOne = true; |
| break; |
| } |
| } |
| if (!foundOne) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "cert failed policyAny criterion"); |
| } |
| return false; |
| } |
| } |
| } |
| } catch (IOException ex) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "IOException in certificate policy ID check"); |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| /* match on pathToNames */ |
| private boolean matchPathToNames(X509Certificate xcert) { |
| if (pathToGeneralNames == null) { |
| return true; |
| } |
| try { |
| NameConstraintsExtension ext = (NameConstraintsExtension) |
| getExtensionObject(xcert, NAME_CONSTRAINTS_ID); |
| if (ext == null) { |
| return true; |
| } |
| if ((debug != null) && Debug.isOn("certpath")) { |
| debug.println("X509CertSelector.match pathToNames:\n"); |
| Iterator<GeneralNameInterface> i = |
| pathToGeneralNames.iterator(); |
| while (i.hasNext()) { |
| debug.println(" " + i.next() + "\n"); |
| } |
| } |
| |
| GeneralSubtrees permitted = |
| ext.get(NameConstraintsExtension.PERMITTED_SUBTREES); |
| GeneralSubtrees excluded = |
| ext.get(NameConstraintsExtension.EXCLUDED_SUBTREES); |
| if (excluded != null) { |
| if (matchExcluded(excluded) == false) { |
| return false; |
| } |
| } |
| if (permitted != null) { |
| if (matchPermitted(permitted) == false) { |
| return false; |
| } |
| } |
| } catch (IOException ex) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: " |
| + "IOException in name constraints check"); |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| private boolean matchExcluded(GeneralSubtrees excluded) { |
| /* |
| * Enumerate through excluded and compare each entry |
| * to all pathToNames. If any pathToName is within any of the |
| * subtrees listed in excluded, return false. |
| */ |
| for (Iterator<GeneralSubtree> t = excluded.iterator(); t.hasNext(); ) { |
| GeneralSubtree tree = t.next(); |
| GeneralNameInterface excludedName = tree.getName().getName(); |
| Iterator<GeneralNameInterface> i = pathToGeneralNames.iterator(); |
| while (i.hasNext()) { |
| GeneralNameInterface pathToName = i.next(); |
| if (excludedName.getType() == pathToName.getType()) { |
| switch (pathToName.constrains(excludedName)) { |
| case GeneralNameInterface.NAME_WIDENS: |
| case GeneralNameInterface.NAME_MATCH: |
| if (debug != null) { |
| debug.println("X509CertSelector.match: name constraints " |
| + "inhibit path to specified name"); |
| debug.println("X509CertSelector.match: excluded name: " + |
| pathToName); |
| } |
| return false; |
| default: |
| } |
| } |
| } |
| } |
| return true; |
| } |
| |
| private boolean matchPermitted(GeneralSubtrees permitted) { |
| /* |
| * Enumerate through pathToNames, checking that each pathToName |
| * is in at least one of the subtrees listed in permitted. |
| * If not, return false. However, if no subtrees of a given type |
| * are listed, all names of that type are permitted. |
| */ |
| Iterator<GeneralNameInterface> i = pathToGeneralNames.iterator(); |
| while (i.hasNext()) { |
| GeneralNameInterface pathToName = i.next(); |
| Iterator<GeneralSubtree> t = permitted.iterator(); |
| boolean permittedNameFound = false; |
| boolean nameTypeFound = false; |
| String names = ""; |
| while (t.hasNext() && !permittedNameFound) { |
| GeneralSubtree tree = t.next(); |
| GeneralNameInterface permittedName = tree.getName().getName(); |
| if (permittedName.getType() == pathToName.getType()) { |
| nameTypeFound = true; |
| names = names + " " + permittedName; |
| switch (pathToName.constrains(permittedName)) { |
| case GeneralNameInterface.NAME_WIDENS: |
| case GeneralNameInterface.NAME_MATCH: |
| permittedNameFound = true; |
| break; |
| default: |
| } |
| } |
| } |
| if (!permittedNameFound && nameTypeFound) { |
| if (debug != null) |
| debug.println("X509CertSelector.match: " + |
| "name constraints inhibit path to specified name; " + |
| "permitted names of type " + pathToName.getType() + |
| ": " + names); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /* match on basic constraints */ |
| private boolean matchBasicConstraints(X509Certificate xcert) { |
| if (basicConstraints == -1) { |
| return true; |
| } |
| int maxPathLen = xcert.getBasicConstraints(); |
| if (basicConstraints == -2) { |
| if (maxPathLen != -1) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: not an EE cert"); |
| } |
| return false; |
| } |
| } else { |
| if (maxPathLen < basicConstraints) { |
| if (debug != null) { |
| debug.println("X509CertSelector.match: cert's maxPathLen " + |
| "is less than the min maxPathLen set by " + |
| "basicConstraints. " + |
| "(" + maxPathLen + " < " + basicConstraints + ")"); |
| } |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| @SuppressWarnings("unchecked") // Safe casts assuming clone() works correctly |
| private static <T> Set<T> cloneSet(Set<T> set) { |
| if (set instanceof HashSet) { |
| Object clone = ((HashSet<T>)set).clone(); |
| return (Set<T>)clone; |
| } else { |
| return new HashSet<T>(set); |
| } |
| } |
| |
| /** |
| * Returns a copy of this object. |
| * |
| * @return the copy |
| */ |
| public Object clone() { |
| try { |
| X509CertSelector copy = (X509CertSelector)super.clone(); |
| // Must clone these because addPathToName et al. modify them |
| if (subjectAlternativeNames != null) { |
| copy.subjectAlternativeNames = |
| cloneSet(subjectAlternativeNames); |
| copy.subjectAlternativeGeneralNames = |
| cloneSet(subjectAlternativeGeneralNames); |
| } |
| if (pathToGeneralNames != null) { |
| copy.pathToNames = cloneSet(pathToNames); |
| copy.pathToGeneralNames = cloneSet(pathToGeneralNames); |
| } |
| return copy; |
| } catch (CloneNotSupportedException e) { |
| /* Cannot happen */ |
| throw new InternalError(e.toString(), e); |
| } |
| } |
| } |