/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
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.misc.
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://www.ietf.org/rfc/rfc3280.txt">RFC 3280:
* 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 final static
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<
String>(
keyPurposeSet));
keyPurposeOIDSet = new
HashSet<
ObjectIdentifier>();
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 3280). 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 3280, section 4.2.1.7)
* @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 3280 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 3280, section 4.2.1.7)
* @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<
List<?>>();
}
if (
subjectAlternativeGeneralNames == null) {
subjectAlternativeGeneralNames = new
HashSet<
GeneralNameInterface>();
}
List<
Object>
list = new
ArrayList<
Object>(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<
GeneralNameInterface>();
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 3280
* 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<
String>(
certPolicySet));
/* Convert to Vector of ObjectIdentifiers */
Iterator<
String>
i =
tempSet.
iterator();
Vector<
CertificatePolicyId>
polIdVector = new
Vector<
CertificatePolicyId>();
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 3280). 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 3280, section 4.2.1.7)
* @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 3280 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 3280, section 4.2.1.7)
* @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 3280, section 4.2.1.7)
* @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<
List<?>>();
pathToGeneralNames = new
HashSet<
GeneralNameInterface>();
}
List<
Object>
list = new
ArrayList<
Object>(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<
List<?>>();
for (
List<?>
o :
names)
{
namesCopy.
add(new
ArrayList<
Object>(
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 3280
* 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() {
StringBuffer sb = new
StringBuffer();
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((
Object)
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<
CertificatePolicyId>(
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);
}
}
}