X509Certificates that match all
* specified criteria. This class is particularly useful when
* selecting certificates from a CertStore to build a PKIX-compliant
* certification path.
*
* When first constructed, an X509CertSelector
has no criteria enabled
* and each of the get methods return a default value (null
, or -1 for
* the {@link #getBasicConstraints} method). Therefore, the {@link #match} method would
* return true for any X509Certificate
. Typically, several criteria
* are enabled (by calling {@link #setIssuer} or {@link #setKeyUsage}, for instance) and
* then the X509CertSelector
is passed to {@link CertStore#getCertificates} or
* some similar method.
*
* Several criteria can be enabled (by calling {@link #setIssuer} and
* {@link #setSerialNumber}, for example) such that the match method usually
* uniquely matches a single 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.
*
* Please refer to RFC 2459 for definitions of the X.509 certificate
* extensions mentioned below.
*
* Concurrent Access
*
* 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.
*
* TODO: implement name constraints
* TODO: implement match check for path to names
*
* Uses {@link org.bouncycastle.asn1.ASN1InputStream ASN1InputStream},
* {@link org.bouncycastle.asn1.ASN1Sequence ASN1Sequence},
* {@link org.bouncycastle.asn1.ASN1ObjectIdentifier ASN1ObjectIdentifier},
* {@link org.bouncycastle.asn1.DEROutputStream DEROutputStream},
* {@link org.bouncycastle.asn1.ASN1Object ASN1Object},
* {@link org.bouncycastle.asn1.OIDTokenizer OIDTokenizer},
* {@link org.bouncycastle.asn1.x509.X509Name X509Name},
* {@link org.bouncycastle.asn1.x509.X509Extensions X509Extensions},
* {@link org.bouncycastle.asn1.x509.ExtendedKeyUsage ExtendedKeyUsage},
* {@link org.bouncycastle.asn1.x509.KeyPurposeId KeyPurposeId},
* {@link org.bouncycastle.asn1.x509.SubjectPublicKeyInfo SubjectPublicKeyInfo},
* {@link org.bouncycastle.asn1.x509.AlgorithmIdentifier AlgorithmIdentifier}
*/
public class X509CertSelector implements CertSelector
{
private static final Hashtable keyPurposeIdMap = new Hashtable();
static
{
keyPurposeIdMap.put(KeyPurposeId.id_kp_serverAuth.getId(),
KeyPurposeId.id_kp_serverAuth);
keyPurposeIdMap.put(KeyPurposeId.id_kp_clientAuth.getId(),
KeyPurposeId.id_kp_clientAuth);
keyPurposeIdMap.put(KeyPurposeId.id_kp_codeSigning.getId(),
KeyPurposeId.id_kp_codeSigning);
keyPurposeIdMap.put(KeyPurposeId.id_kp_emailProtection.getId(),
KeyPurposeId.id_kp_emailProtection);
keyPurposeIdMap.put(KeyPurposeId.id_kp_ipsecEndSystem.getId(),
KeyPurposeId.id_kp_ipsecEndSystem);
keyPurposeIdMap.put(KeyPurposeId.id_kp_ipsecTunnel.getId(),
KeyPurposeId.id_kp_ipsecTunnel);
keyPurposeIdMap.put(KeyPurposeId.id_kp_ipsecUser.getId(),
KeyPurposeId.id_kp_ipsecUser);
keyPurposeIdMap.put(KeyPurposeId.id_kp_timeStamping.getId(),
KeyPurposeId.id_kp_timeStamping);
}
private X509Certificate x509Cert = null;
private BigInteger serialNumber = null;
private Object issuerDN = null;
private X509Name issuerDNX509 = null;
private Object subjectDN = null;
private X509Name subjectDNX509 = null;
private byte[] subjectKeyID = null;
private byte[] authorityKeyID = null;
private Date certValid = null;
private Date privateKeyValid = null;
private ASN1ObjectIdentifier subjectKeyAlgID = null;
private PublicKey subjectPublicKey = null;
private byte[] subjectPublicKeyByte = null;
private boolean[] keyUsage = null;
private Set keyPurposeSet = null;
private boolean matchAllSubjectAltNames = true;
private Set subjectAltNames = null;
private Set subjectAltNamesByte = null;
private int minMaxPathLen = -1;
private Set policy = null;
private Set policyOID = null;
private Set pathToNames = null;
private Set pathToNamesByte = null;
/**
* Creates an X509CertSelector
. Initially, no criteria are
* set so any X509Certificate
will match.
*/
public X509CertSelector()
{
}
/**
* Sets the certificateEquals criterion. The specified
* X509Certificate
must be equal to the
* X509Certificate
passed to the match method. If
* null
, then this check is not applied.
*
* 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 X509Certificate to match (or 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 X509Certificate
. If
* null
, any certificate serial number will do.
*
* @param serial
* the certificate serial number to match (or 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 X509Certificate
. If
* null
, any issuer distinguished name will do.
*
* If issuerDN
is not null
, it should contain
* a distinguished name, in RFC 2253 format.
*
* Uses {@link org.bouncycastle.asn1.x509.X509Name X509Name} for parsing the
* issuerDN.
*
* @param issuerDN
* a distinguished name in RFC 2253 format (or null
)
*
* @exception IOException
* if a parsing error occurs (incorrect form for DN)
*/
public void setIssuer(String issuerDN) throws IOException
{
if (issuerDN == null)
{
this.issuerDN = null;
this.issuerDNX509 = null;
}
else
{
X509Name nameX509;
try
{
nameX509 = new X509Name(issuerDN);
}
catch (IllegalArgumentException ex)
{
throw new IOException(ex.getMessage());
}
this.issuerDNX509 = nameX509;
this.issuerDN = issuerDN;
}
}
/**
* Sets the issuer criterion. The specified distinguished name must match
* the issuer distinguished name in the X509Certificate
. If
* null is specified, the issuer criterion is disabled and any issuer
* distinguished name will do.
*
* If issuerDN
is not 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.
*
*
*
* Name ::= CHOICE {
* RDNSequence }
*
* RDNSequence ::= SEQUENCE OF RDN
*
* RDN ::=
* 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)) }
*
*
*
*
* Note that the byte array specified here is cloned to protect against
* subsequent modifications.
*
* Uses {@link org.bouncycastle.asn1.ASN1InputStream ASN1InputStream},
* {@link org.bouncycastle.asn1.ASN1Object ASN1Object},
* {@link org.bouncycastle.asn1.ASN1Sequence ASN1Sequence},
* {@link org.bouncycastle.asn1.x509.X509Name X509Name}
*
* @param issuerDN -
* a byte array containing the distinguished name in ASN.1 DER
* encoded form (or null
)
*
* @exception IOException
* if an encoding error occurs (incorrect form for DN)
*/
public void setIssuer(byte[] issuerDN) throws IOException
{
if (issuerDN == null)
{
this.issuerDN = null;
this.issuerDNX509 = null;
}
else
{
ByteArrayInputStream inStream = new ByteArrayInputStream(issuerDN);
ASN1InputStream derInStream = new ASN1InputStream(inStream);
ASN1Object obj = derInStream.readObject();
if (obj instanceof ASN1Sequence)
{
this.issuerDNX509 = new X509Name((ASN1Sequence)obj);
}
else
{
throw new IOException("parsing error");
}
this.issuerDN = (byte[])issuerDN.clone();
}
}
/**
* Sets the subject criterion. The specified distinguished name must match
* the subject distinguished name in the X509Certificate
. If
* null, any subject distinguished name will do.
*
* If subjectDN
is not null
, it should
* contain a distinguished name, in RFC 2253 format.
*
* Uses {@link org.bouncycastle.asn1.x509.X509Name X509Name} for parsing the
* subjectDN.
*
* @param subjectDN
* a distinguished name in RFC 2253 format (or null
)
*
* @exception IOException
* if a parsing error occurs (incorrect form for DN)
*/
public void setSubject(String subjectDN) throws IOException
{
if (subjectDN == null)
{
this.subjectDN = null;
this.subjectDNX509 = null;
}
else
{
X509Name nameX509;
try
{
nameX509 = new X509Name(subjectDN);
}
catch (IllegalArgumentException ex)
{
throw new IOException(ex.getMessage());
}
this.subjectDNX509 = nameX509;
this.subjectDN = subjectDN;
}
}
/**
* Sets the subject criterion. The specified distinguished name must match
* the subject distinguished name in the X509Certificate
. If
* null, any subject distinguished name will do.
*
* If subjectDN
is not 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 []) setIssuer(byte [] issuerDN)}.
*
* Uses {@link org.bouncycastle.asn1.ASN1InputStream ASN1InputStream},
* {@link org.bouncycastle.asn1.ASN1Object ASN1Object},
* {@link org.bouncycastle.asn1.ASN1Sequence ASN1Sequence},
* {@link org.bouncycastle.asn1.x509.X509Name X509Name}
*
* @param subjectDN
* a byte array containing the distinguished name in ASN.1 DER
* format (or null
)
*
* @exception IOException
* if an encoding error occurs (incorrect form for DN)
*/
public void setSubject(byte[] subjectDN) throws IOException
{
if (subjectDN == null)
{
this.subjectDN = null;
this.subjectDNX509 = null;
}
else
{
ByteArrayInputStream inStream = new ByteArrayInputStream(subjectDN);
ASN1InputStream derInStream = new ASN1InputStream(inStream);
ASN1Object obj = derInStream.readObject();
if (obj instanceof ASN1Sequence)
{
this.subjectDNX509 = new X509Name((ASN1Sequence)obj);
}
else
{
throw new IOException("parsing error");
}
this.subjectDN = (byte[])subjectDN.clone();
}
}
/**
* Sets the subjectKeyIdentifier criterion. The X509Certificate
* must contain a SubjectKeyIdentifier extension for which the contents of
* the extension matches the specified criterion value. If the criterion
* value is null, no subjectKeyIdentifier check will be done.
*
* If subjectKeyID
is not 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.
*
*
*
* SubjectKeyIdentifier ::= KeyIdentifier
*
* KeyIdentifier ::= OCTET STRING
*
*
*
*
* Since the format of subject key identifiers is not mandated by any
* standard, subject key identifiers are not parsed by the
* X509CertSelector
. Instead, the values are compared using
* a byte-by-byte comparison.
*
* Note that the byte array supplied here is cloned to protect against
* subsequent modifications.
*
* @param subjectKeyID -
* the subject key identifier (or null
)
*
* @see #getSubjectKeyIdentifier()
*/
public void setSubjectKeyIdentifier(byte[] subjectKeyID)
{
if (subjectKeyID == null)
{
this.subjectKeyID = null;
}
else
{
this.subjectKeyID = (byte[])subjectKeyID.clone();
}
}
/**
* Sets the authorityKeyIdentifier criterion. The
* X509Certificate
must contain an AuthorityKeyIdentifier
* extension for which the contents of the extension value matches the
* specified criterion value. If the criterion value is null
,
* no authorityKeyIdentifier check will be done.
*
* If authorityKeyID
is not 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.
*
*
*
* AuthorityKeyIdentifier ::= SEQUENCE {
* keyIdentifier [0] KeyIdentifier OPTIONAL,
* authorityCertIssuer [1] GeneralNames OPTIONAL,
* authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
*
* KeyIdentifier ::= OCTET STRING
*
*
*
*
* Authority key identifiers are not parsed by the
* X509CertSelector
. Instead, the values are compared using
* a byte-by-byte comparison.
*
* When the keyIdentifier
field of
* AuthorityKeyIdentifier
is populated, the value is usually
* taken from the SubjectKeyIdentifier extension in the issuer's
* certificate. Note, however, that the result of
* X509Certificate.getExtensionValue() on the issuer's certificate may NOT be used directly as the
* input to 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
* SubjectKeyIdentifier extension, it will be necessary to extract the value
* of the embedded KeyIdentifier OCTET STRING, then DER encode this OCTET
* STRING inside a SEQUENCE. For more details on SubjectKeyIdentifier, see
* {@link #setSubjectKeyIdentifier(byte[]) setSubjectKeyIdentifier(byte[] subjectKeyID }).
*
* Note also that the byte array supplied here is cloned to protect against
* subsequent modifications.
*
* @param authorityKeyID
* the authority key identifier (or null
)
*
* @see #getAuthorityKeyIdentifier()
*/
public void setAuthorityKeyIdentifier(byte[] authorityKeyID)
{
if (authorityKeyID == null)
{
this.authorityKeyID = null;
}
else
{
this.authorityKeyID = (byte[])authorityKeyID.clone();
}
}
/**
* Sets the certificateValid criterion. The specified date must fall within
* the certificate validity period for the X509Certificate. If
* null
, no certificateValid check will be done.
*
* Note that the Date supplied here is cloned to protect against subsequent
* modifications.
*
* @param certValid
* the Date to check (or null
)
*
* @see #getCertificateValid()
*/
public void setCertificateValid(Date certValid)
{
if (certValid == null)
{
this.certValid = null;
}
else
{
this.certValid = new Date(certValid.getTime());
}
}
/**
* Sets the privateKeyValid criterion. The specified date must fall within
* the private key validity period for the X509Certificate. If
* null
, no privateKeyValid check will be done.
*
* Note that the Date supplied here is cloned to protect against subsequent
* modifications.
*
* @param privateKeyValid
* the Date to check (or null
)
*
* @see #getPrivateKeyValid()
*/
public void setPrivateKeyValid(Date privateKeyValid)
{
if (privateKeyValid == null)
{
this.privateKeyValid = null;
}
else
{
this.privateKeyValid = new Date(privateKeyValid.getTime());
}
}
/**
* Sets the subjectPublicKeyAlgID criterion. The X509Certificate must
* contain a subject public key with the specified algorithm. If
* null
, no subjectPublicKeyAlgID check will be done.
*
* @param oid
* The object identifier (OID) of the algorithm to check for (or
* null
). An OID is represented by a set of
* nonnegative integers separated by periods.
*
* @exception 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
{
CertUtil.parseOID(oid);
subjectKeyAlgID = new ASN1ObjectIdentifier(oid);
}
/**
* Sets the subjectPublicKey criterion. The X509Certificate must contain the
* specified subject public key. If null, no subjectPublicKey check will be
* done.
*
* @param key
* the subject public key to check for (or null)
*
* @see #getSubjectPublicKey()
*/
public void setSubjectPublicKey(PublicKey key)
{
if (key == null)
{
subjectPublicKey = null;
subjectPublicKeyByte = null;
}
else
{
subjectPublicKey = key;
subjectPublicKeyByte = key.getEncoded();
}
}
/**
* Sets the subjectPublicKey criterion. The X509Certificate
* must contain the specified subject public key. If null
,
* no subjectPublicKey check will be done.
*
* Because this method allows the public key to be specified as a byte
* array, it may be used for unknown key types.
*
* If key is not 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.
*
*
*
* 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
*
*
*
*
* 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 null
)
*
* @exception 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;
subjectPublicKeyByte = null;
}
else
{
subjectPublicKey = null;
subjectPublicKeyByte = (byte[])key.clone();
// TODO
// try to generyte PublicKey Object from subjectPublicKeyByte
}
}
/**
* Sets the keyUsage criterion. The X509Certificate must allow the specified
* keyUsage values. If null, no keyUsage check will be done. Note that an
* X509Certificate that has no keyUsage extension implicitly allows all
* keyUsage values.
*
* 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 X509Certificate.getKeyUsage(). Or
* null
.
*
* @see #getKeyUsage()
*/
public void setKeyUsage(boolean[] keyUsage)
{
if (keyUsage == null)
{
this.keyUsage = null;
}
else
{
this.keyUsage = (boolean[])keyUsage.clone();
}
}
/**
* Sets the extendedKeyUsage criterion. The X509Certificate
* must allow the specified key purposes in its extended key usage
* extension. If keyPurposeSet
is empty or null
,
* no extendedKeyUsage check will be done. Note that an
* X509Certificate
that has no extendedKeyUsage extension
* implicitly allows all key purposes.
*
* Note that the Set is cloned to protect against subsequent modifications.
*
* Uses {@link org.bouncycastle.asn1.x509.KeyPurposeId KeyPurposeId}
*
* @param keyPurposeSet
* a Set
of key purpose OIDs in string format (or
* null
). Each OID is represented by a set of
* nonnegative integers separated by periods.
*
* @exception 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 keyPurposeSet) throws IOException
{
if (keyPurposeSet == null || keyPurposeSet.isEmpty())
{
this.keyPurposeSet = keyPurposeSet;
}
else
{
this.keyPurposeSet = new HashSet();
Iterator iter = keyPurposeSet.iterator();
Object obj;
KeyPurposeId purposeID;
while (iter.hasNext())
{
obj = iter.next();
if (obj instanceof String)
{
purposeID = (KeyPurposeId)keyPurposeIdMap.get((String)obj);
if (purposeID == null)
{
throw new IOException("unknown purposeID "
+ (String)obj);
}
this.keyPurposeSet.add(purposeID);
}
}
}
}
/**
* Enables/disables matching all of the subjectAlternativeNames specified in
* the {@link #setSubjectAlternativeNames setSubjectAlternativeNames} or
* {@link #addSubjectAlternativeName addSubjectAlternativeName} methods. If
* enabled, the X509Certificate
must contain all of the
* specified subject alternative names. If disabled, the X509Certificate
* must contain at least one of the specified subject alternative names.
*
* The matchAllNames flag is true
by default.
*
* @param matchAllNames
* if true
, the flag is enabled; if
* false
, the flag is disabled.
*
* @see #getMatchAllSubjectAltNames()
*/
public void setMatchAllSubjectAltNames(boolean matchAllNames)
{
matchAllSubjectAltNames = matchAllNames;
}
/**
* Sets the subjectAlternativeNames criterion. The
* X509Certificate
must contain all or at least one of the
* specified subjectAlternativeNames, depending on the value of the
* matchAllNames flag (see {@link #setMatchAllSubjectAltNames}).
*
* 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.
*
* The names
parameter (if not null
) is a
* Collection
with one entry for each name to be included in
* the subject alternative name criterion. Each entry is a 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. If null
is supplied as the value
* for this argument, no subjectAlternativeNames check will be performed.
*
* Each subject alternative name in the Collection
may be
* specified either as a String
or as an ASN.1 encoded byte
* array. For more details about the formats used, see
* {@link #addSubjectAlternativeName(int, String) addSubjectAlternativeName(int type, String name)}
* and
* {@link #addSubjectAlternativeName(int, byte[]) addSubjectAlternativeName(int type, byte [] name}).
*
* Note that the names
parameter can contain duplicate names
* (same name and name type), but they may be removed from the
* Collection
of names returned by the
* {@link #getSubjectAlternativeNames} method.
*
* Note that a deep copy is performed on the Collection to protect against
* subsequent modifications.
*
* @param names -
* a Collection of names (or null)
*
* @exception IOException
* if a parsing error occurs
*
* @see #getSubjectAlternativeNames()
*/
public void setSubjectAlternativeNames(Collection names) throws IOException
{
try
{
if (names == null || names.isEmpty())
{
subjectAltNames = null;
subjectAltNamesByte = null;
}
else
{
subjectAltNames = new HashSet();
subjectAltNamesByte = new HashSet();
Iterator iter = names.iterator();
List item;
int type;
Object data;
while (iter.hasNext())
{
item = (List)iter.next();
type = ((Integer)item.get(0)).intValue();
data = item.get(1);
if (data instanceof String)
{
addSubjectAlternativeName(type, (String)data);
}
else if (data instanceof byte[])
{
addSubjectAlternativeName(type, (byte[])data);
}
else
{
throw new IOException(
"parsing error: unknown data type");
}
}
}
}
catch (Exception ex)
{
throw new IOException("parsing exception:\n" + ex.toString());
}
}
/**
* Adds a name to the subjectAlternativeNames criterion. The
* X509Certificate
must contain all or at least one of the
* specified subjectAlternativeNames, depending on the value of the
* matchAllNames flag (see {@link #setMatchAllSubjectAltNames}).
*
* 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.
*
* 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 2459). 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, byte[]) addSubjectAlternativeName(int type, byte [] name)}
* method.
*
* @param type
* the name type (0-8, as specified in RFC 2459, section 4.2.1.7)
* @param name -
* the name in string form (not null)
*
* @exception IOException
* if a parsing error occurs
*/
public void addSubjectAlternativeName(int type, String name)
throws IOException
{
// TODO full implementation of CertUtil.parseGeneralName
byte[] encoded = CertUtil.parseGeneralName(type, name);
List tmpList = new ArrayList();
tmpList.add(Integers.valueOf(type));
tmpList.add(name);
subjectAltNames.add(tmpList);
tmpList.set(1, encoded);
subjectAltNamesByte.add(tmpList);
}
/**
* Adds a name to the subjectAlternativeNames criterion. The
* X509Certificate
must contain all or at least one of the
* specified subjectAlternativeNames, depending on the value of the
* matchAllNames flag (see {@link #setMatchAllSubjectAltNames}).
*
* 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.
*
* 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 2459 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.
*
*
*
* 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}
*
*
*
*
* Note that the byte array supplied here is cloned to protect against
* subsequent modifications.
*
* TODO: check encoded format
*
* @param type
* the name type (0-8, as listed above)
* @param name
* a byte array containing the name in ASN.1 DER encoded form
*
* @exception IOException
* if a parsing error occurs
*/
public void addSubjectAlternativeName(int type, byte[] name)
throws IOException
{
// TODO check encoded format
List tmpList = new ArrayList();
tmpList.add(Integers.valueOf(type));
tmpList.add(name.clone());
subjectAltNames.add(tmpList);
subjectAltNamesByte.add(tmpList);
}
/**
* Sets the name constraints criterion. The X509Certificate
* must have subject and subject alternative names that meet the specified
* name constraints.
*
* 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 2459 and
* X.509. The ASN.1 definition of this structure appears below.
*
*
*
* 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}
*
*
*
*
* Note that the byte array supplied here is cloned to protect against
* subsequent modifications.
*
* TODO: implement this
*
* @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 null
, in
* which case no name constraints check will be performed
*
* @exception IOException
* if a parsing error occurs
* @exception UnsupportedOperationException
* because this method is not supported
* @see #getNameConstraints()
*/
public void setNameConstraints(byte[] bytes) throws IOException
{
throw new UnsupportedOperationException();
}
/**
* Sets the basic constraints constraint. If the value is greater than or
* equal to zero, 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.
*
* 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
*
* @exception IllegalArgumentException
* if the value is less than -2
*
* @see #getBasicConstraints()
*/
public void setBasicConstraints(int minMaxPathLen)
{
if (minMaxPathLen < -2)
{
throw new IllegalArgumentException("minMaxPathLen must be >= -2");
}
this.minMaxPathLen = minMaxPathLen;
}
/**
* Sets the policy constraint. The X509Certificate must include at least one
* of the specified policies in its certificate policies extension. If
* certPolicySet is empty, then the X509Certificate must include at least
* some specified policy in its certificate policies extension. If
* certPolicySet is null, no policy check will be performed.
*
* Note that the Set is cloned to protect against subsequent modifications.
*
* TODO: implement match check for this
*
* @param certPolicySet
* a Set of certificate policy OIDs in string format (or null).
* Each OID is represented by a set of nonnegative integers
* separated by periods.
*
* @exception 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 certPolicySet) throws IOException
{
if (certPolicySet == null)
{
policy = null;
policyOID = null;
}
else
{
policyOID = new HashSet();
Iterator iter = certPolicySet.iterator();
Object item;
while (iter.hasNext())
{
item = iter.next();
if (item instanceof String)
{
CertUtil.parseOID((String)item);
policyOID.add(new ASN1ObjectIdentifier((String)item));
}
else
{
throw new IOException(
"certPolicySet contains null values or non String objects");
}
}
policy = new HashSet(certPolicySet);
}
}
/**
* Sets the pathToNames criterion. The X509Certificate
must
* not include name constraints that would prohibit building a path to the
* specified names.
*
* This method allows the caller to specify, with a single method call, the
* complete set of names which the X509Certificates
's name
* constraints must permit. The specified value replaces the previous value
* for the pathToNames criterion.
*
* 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.
*
* The names parameter (if not null
) is a
* Collection
with one entry for each name to be included in
* the pathToNames criterion. Each entry is a 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. If null
is supplied as the value for this argument,
* no pathToNames check will be performed.
*
* Each name in the Collection may be specified either as a String or as an
* ASN.1 encoded byte array. For more details about the formats used, see
* {@link #addPathToName(int, String) addPathToName(int type, String name)}
* and
* {@link #addPathToName(int, byte[]) addPathToName(int type, byte [] name)}.
*
* Note that the names parameter can contain duplicate names (same name and
* name type), but they may be removed from the Collection of names returned
* by the {@link #getPathToNames} method.
*
* Note that a deep copy is performed on the Collection to protect against
* subsequent modifications.
*
* TODO: implement this match check for this
*
* @param names
* a Collection with one entry per name (or null
)
*
* @exception IOException
* if a parsing error occurs
* @exception UnsupportedOperationException
* because this method is not supported
*
* @see #getPathToNames()
*/
public void setPathToNames(Collection names) throws IOException
{
try
{
if (names == null || names.isEmpty())
{
pathToNames = null;
pathToNamesByte = null;
}
else
{
pathToNames = new HashSet();
pathToNamesByte = new HashSet();
Iterator iter = names.iterator();
List item;
int type;
Object data;
while (iter.hasNext())
{
item = (List)iter.next();
type = ((Integer)item.get(0)).intValue();
data = item.get(1);
if (data instanceof String)
{
addPathToName(type, (String)data);
}
else if (data instanceof byte[])
{
addPathToName(type, (byte[])data);
}
else
{
throw new IOException(
"parsing error: unknown data type");
}
}
}
}
catch (Exception ex)
{
throw new IOException("parsing exception:\n" + ex.toString());
}
}
/**
* Adds a name to the pathToNames criterion. The
* X509Certificate
must not include name constraints that
* would prohibit building a path to the specified name.
*
* This method allows the caller to add a name to the set of names which the
* 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.
*
* 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 2459). 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, byte[]) addPathToName(int type, byte [] name)}
* method.
*
* TODO: implement this match check for this
*
* @param type
* the name type (0-8, as specified in RFC 2459, section 4.2.1.7)
* @param name
* the name in string form
*
* @exceptrion IOException if a parsing error occurs
*/
public void addPathToName(int type, String name) throws IOException
{
// TODO full implementation of CertUtil.parseGeneralName
byte[] encoded = CertUtil.parseGeneralName(type, name);
List tmpList = new ArrayList();
tmpList.add(Integers.valueOf(type));
tmpList.add(name);
pathToNames.add(tmpList);
tmpList.set(1, encoded);
pathToNamesByte.add(tmpList);
throw new UnsupportedOperationException();
}
/**
* Adds a name to the pathToNames criterion. The
* X509Certificate
must not include name constraints that
* would prohibit building a path to the specified name.
*
* This method allows the caller to add a name to the set of names which the
* 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.
*
* 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 2459 and X.509. The ASN.1 definition of this structure appears in
* the documentation for
* {@link #addSubjectAlternativeName(int,byte[]) addSubjectAlternativeName(int type, byte[] name)}.
*
* Note that the byte array supplied here is cloned to protect against
* subsequent modifications.
*
* TODO: implement this match check for this
*
* @param type
* the name type (0-8, as specified in RFC 2459, section 4.2.1.7)
* @param name
* a byte array containing the name in ASN.1 DER encoded form
*
* @exception IOException
* if a parsing error occurs
*/
public void addPathToName(int type, byte[] name) throws IOException
{
// TODO check encoded format
List tmpList = new ArrayList();
tmpList.add(Integers.valueOf(type));
tmpList.add(name.clone());
pathToNames.add(tmpList);
pathToNamesByte.add(tmpList);
}
/**
* Returns the certificateEquals criterion. The specified
* X509Certificate
must be equal to the
* X509Certificate
passed to the match method. If
* null
, this check is not applied.
*
* @retrun the X509Certificate
to match (or null
)
*
* @see #setCertificate(java.security.cert.X509Certificate)
*/
public X509Certificate getCertificate()
{
return x509Cert;
}
/**
* Returns the serialNumber criterion. The specified serial number must
* match the certificate serial number in the X509Certificate
.
* If null
, any certificate serial number will do.
*
* @return the certificate serial number to match (or null
)
*
* @see #setSerialNumber(java.math.BigInteger)
*/
public BigInteger getSerialNumber()
{
return serialNumber;
}
/**
* Returns the issuer criterion as a String. This distinguished name must
* match the issuer distinguished name in the X509Certificate
.
* If null
, the issuer criterion is disabled and any issuer
* distinguished name will do.
*
* If the value returned is not null
, it is a distinguished
* name, in RFC 2253 format.
*
* Uses {@link org.bouncycastle.asn1.x509.X509Name X509Name} for formatiing
* byte[] issuerDN to String.
*
* @return the required issuer distinguished name in RFC 2253 format (or
* null
)
*/
public String getIssuerAsString()
{
if (issuerDN instanceof String)
{
return new String((String)issuerDN);
}
else if (issuerDNX509 != null)
{
return issuerDNX509.toString();
}
return null;
}
/**
* Returns the issuer criterion as a byte array. This distinguished name
* must match the issuer distinguished name in the
* X509Certificate
. If null
, the issuer
* criterion is disabled and any issuer distinguished name will do.
*
* If the value returned is not 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[]) setIssuer(byte [] issuerDN)}.
*
* Note that the byte array returned is cloned to protect against subsequent
* modifications.
*
* Uses {@link org.bouncycastle.asn1.DEROutputStream DEROutputStream},
* {@link org.bouncycastle.asn1.x509.X509Name X509Name} to gnerate byte[]
* output for String issuerDN.
*
* @return a byte array containing the required issuer distinguished name in
* ASN.1 DER format (or null
)
*
* @exception IOException
* if an encoding error occurs
*/
public byte[] getIssuerAsBytes() throws IOException
{
if (issuerDN instanceof byte[])
{
return (byte[])((byte[])issuerDN).clone();
}
else if (issuerDNX509 != null)
{
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
DEROutputStream derOutStream = new DEROutputStream(outStream);
derOutStream.writeObject(issuerDNX509.toASN1Primitive());
derOutStream.close();
return outStream.toByteArray();
}
return null;
}
/**
* Returns the subject criterion as a String. This distinguished name must
* match the subject distinguished name in the X509Certificate
.
* If null
, the subject criterion is disabled and any
* subject distinguished name will do.
*
* If the value returned is not null
, it is a distinguished
* name, in RFC 2253 format.
*
* Uses {@link org.bouncycastle.asn1.x509.X509Name X509Name} for formatiing
* byte[] subjectDN to String.
*
* @return the required subject distinguished name in RFC 2253 format (or
* null
)
*/
public String getSubjectAsString()
{
if (subjectDN instanceof String)
{
return new String((String)subjectDN);
}
else if (subjectDNX509 != null)
{
return subjectDNX509.toString();
}
return null;
}
/**
* Returns the subject criterion as a byte array. This distinguished name
* must match the subject distinguished name in the
* X509Certificate
. If null
, the subject
* criterion is disabled and any subject distinguished name will do.
*
* If the value returned is not 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)}.
*
* Note that the byte array returned is cloned to protect against subsequent
* modifications.
*
* Uses {@link org.bouncycastle.asn1.DEROutputStream DEROutputStream},
* {@link org.bouncycastle.asn1.x509.X509Name X509Name} to gnerate byte[]
* output for String subjectDN.
*
* @return a byte array containing the required subject distinguished name
* in ASN.1 DER format (or null
)
*
* @exception IOException
* if an encoding error occurs
*/
public byte[] getSubjectAsBytes() throws IOException
{
if (subjectDN instanceof byte[])
{
return (byte[])((byte[])subjectDN).clone();
}
else if (subjectDNX509 != null)
{
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
DEROutputStream derOutStream = new DEROutputStream(outStream);
derOutStream.writeObject(subjectDNX509.toASN1Primitive());
derOutStream.close();
return outStream.toByteArray();
}
return null;
}
/**
* Returns the subjectKeyIdentifier criterion. The
* X509Certificate
must contain a SubjectKeyIdentifier
* extension with the specified value. If null
, no
* subjectKeyIdentifier check will be done.
*
* Note that the byte array returned is cloned to protect against subsequent
* modifications.
*
* @return the key identifier (or null
)
*
* @see #setSubjectKeyIdentifier
*/
public byte[] getSubjectKeyIdentifier()
{
if (subjectKeyID != null)
{
return (byte[])subjectKeyID.clone();
}
return null;
}
/**
* Returns the authorityKeyIdentifier criterion. The
* X509Certificate
must contain a AuthorityKeyIdentifier
* extension with the specified value. If null
, no
* authorityKeyIdentifier check will be done.
*
* Note that the byte array returned is cloned to protect against subsequent
* modifications.
*
* @return the key identifier (or null
)
*
* @see #setAuthorityKeyIdentifier
*/
public byte[] getAuthorityKeyIdentifier()
{
if (authorityKeyID != null)
{
return (byte[])authorityKeyID.clone();
}
return null;
}
/**
* Returns the certificateValid criterion. The specified date must fall
* within the certificate validity period for the
* X509Certificate
. If null
, no
* certificateValid check will be done.
*
* Note that the Date
returned is cloned to protect against
* subsequent modifications.
*
* @return the Date
to check (or null
)
*
* @see #setCertificateValid
*/
public Date getCertificateValid()
{
if (certValid != null)
{
return new Date(certValid.getTime());
}
return null;
}
/**
* Returns the privateKeyValid criterion. The specified date must fall
* within the private key validity period for the
* X509Certificate
. If null
, no
* privateKeyValid check will be done.
*
* Note that the Date
returned is cloned to protect against
* subsequent modifications.
*
* @return the Date
to check (or null
)
*
* @see #setPrivateKeyValid
*/
public Date getPrivateKeyValid()
{
if (privateKeyValid != null)
{
return new Date(privateKeyValid.getTime());
}
return null;
}
/**
* Returns the subjectPublicKeyAlgID criterion. The
* X509Certificate
must contain a subject public key with the
* specified algorithm. If null
, no subjectPublicKeyAlgID
* check will be done.
*
* @return the object identifier (OID) of the signature algorithm to check
* for (or null
). An OID is represented by a set of
* nonnegative integers separated by periods.
*
* @see #setSubjectPublicKeyAlgID
*/
public String getSubjectPublicKeyAlgID()
{
if (subjectKeyAlgID != null)
{
return subjectKeyAlgID.toString();
}
return null;
}
/**
* Returns the subjectPublicKey criterion. The X509Certificate
* must contain the specified subject public key. If null
,
* no subjectPublicKey check will be done.
*
* @return the subject public key to check for (or null
)
*
* @see #setSubjectPublicKey
*/
public PublicKey getSubjectPublicKey()
{
return subjectPublicKey;
}
/**
* Returns the keyUsage criterion. The X509Certificate
must
* allow the specified keyUsage values. If null, no keyUsage check will be
* done.
*
* 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 null
.
*
* @see #setKeyUsage
*/
public boolean[] getKeyUsage()
{
if (keyUsage != null)
{
return (boolean[])keyUsage.clone();
}
return null;
}
/**
* Returns the extendedKeyUsage criterion. The X509Certificate
* must allow the specified key purposes in its extended key usage
* extension. If the keyPurposeSet
returned is empty or
* null
, no extendedKeyUsage check will be done. Note that
* an X509Certificate
that has no extendedKeyUsage extension
* implicitly allows all key purposes.
*
* @return an immutable Set
of key purpose OIDs in string
* format (or null
)
* @see #setExtendedKeyUsage
*/
public Set getExtendedKeyUsage()
{
if (keyPurposeSet == null || keyPurposeSet.isEmpty())
{
return keyPurposeSet;
}
Set returnSet = new HashSet();
Iterator iter = keyPurposeSet.iterator();
while (iter.hasNext())
{
returnSet.add(iter.next().toString());
}
return Collections.unmodifiableSet(returnSet);
}
/**
* Indicates if the X509Certificate
must contain all or at
* least one of the subjectAlternativeNames specified in the
* {@link #setSubjectAlternativeNames setSubjectAlternativeNames} or
* {@link #addSubjectAlternativeName addSubjectAlternativeName} methods. If
* true
, the X509Certificate
must contain all
* of the specified subject alternative names. If false
, the
* X509Certificate
must contain at least one of the specified
* subject alternative names.
*
* @return true
if the flag is enabled; false
* if the flag is disabled. The flag is true
by
* default.
*
* @see #setMatchAllSubjectAltNames
*/
public boolean getMatchAllSubjectAltNames()
{
return matchAllSubjectAltNames;
}
/**
* Returns a copy of the subjectAlternativeNames criterion. The
* 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 null
,
* no subjectAlternativeNames check will be performed.
*
* If the value returned is not null
, it is a
* Collection
with one entry for each name to be included in
* the subject alternative name criterion. Each entry is a 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. Note that the Collection
returned
* may contain duplicate names (same name and name type).
*
* Each subject alternative name in the Collection
may be
* specified either as a 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)}.
*
* Note that a deep copy is performed on the Collection
to
* protect against subsequent modifications.
*
* @return a Collection
of names (or null
)
*
* @see #setSubjectAlternativeNames
*/
public Collection getSubjectAlternativeNames()
{
if (subjectAltNames != null)
{
return null;
}
Set returnAltNames = new HashSet();
List returnList;
Iterator iter = subjectAltNames.iterator();
List obj;
while (iter.hasNext())
{
obj = (List)iter.next();
returnList = new ArrayList();
returnList.add(obj.get(0));
if (obj.get(1) instanceof byte[])
{
returnList.add(((byte[])obj.get(1)).clone());
}
else
{
returnList.add(obj.get(1));
}
returnAltNames.add(returnList);
}
return returnAltNames;
}
/**
* Returns the name constraints criterion. The X509Certificate
* must have subject and subject alternative names that meet the specified
* name constraints.
*
* 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 2459 and X.509.
* The ASN.1 notation for this structure is supplied in the documentation
* for
* {@link #setNameConstraints(byte [] bytes) setNameConstraints(byte [] bytes)}.
*
* Note that the byte array returned is cloned to protect against subsequent
* modifications.
*
* TODO: implement this
*
* @return a byte array containing the ASN.1 DER encoding of a
* NameConstraints extension used for checking name constraints.
* null
if no name constraints check will be
* performed.
*
* @exception UnsupportedOperationException
* because this method is not supported
*
* @see #setNameConstraints
*/
public byte[] getNameConstraints()
{
throw new UnsupportedOperationException();
}
/**
* Returns the basic constraints constraint. If the value is greater than or
* equal to zero, the 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 minMaxPathLen;
}
/**
* Returns the policy criterion. The X509Certificate
must
* include at least one of the specified policies in its certificate
* policies extension. If the Set
returned is empty, then the
* X509Certificate
must include at least some specified
* policy in its certificate policies extension. If the Set
* returned is null
, no policy check will be performed.
*
* @return an immutable Set
of certificate policy OIDs in
* string format (or null
)
*
* @see #setPolicy
*/
public Set getPolicy()
{
if (policy == null)
{
return null;
}
return Collections.unmodifiableSet(policy);
}
/**
* Returns a copy of the pathToNames criterion. The
* X509Certificate
must not include name constraints that
* would prohibit building a path to the specified names. If the value
* returned is null
, no pathToNames check will be performed.
*
* If the value returned is not null
, it is a
* Collection
with one entry for each name to be included in
* the pathToNames criterion. Each entry is a 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. Note that the Collection
returned
* may contain duplicate names (same name and name type).
*
* Each name in the Collection
may be specified either as a
* 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)}.
*
* Note that a deep copy is performed on the Collection
to
* protect against subsequent modifications.
*
* @return a Collection
of names (or null
)
*
* @see #setPathToNames
*/
public Collection getPathToNames()
{
if (pathToNames == null)
{
return null;
}
Set returnPathToNames = new HashSet();
List returnList;
Iterator iter = pathToNames.iterator();
List obj;
while (iter.hasNext())
{
obj = (List)iter.next();
returnList = new ArrayList();
returnList.add(obj.get(0));
if (obj.get(1) instanceof byte[])
{
returnList.add(((byte[])obj.get(1)).clone());
}
else
{
returnList.add(obj.get(1));
}
returnPathToNames.add(returnList);
}
return returnPathToNames;
}
/**
* Return a printable representation of the CertSelector
.
*
* TODO: implement output for currently unsupported options(name
* constraints)
*
* Uses {@link org.bouncycastle.asn1.ASN1InputStream ASN1InputStream},
* {@link org.bouncycastle.asn1.ASN1Object ASN1Object},
* {@link org.bouncycastle.asn1.x509.KeyPurposeId KeyPurposeId}
*
* @return a String
describing the contents of the
* CertSelector
*/
public String toString()
{
StringBuffer sb = new StringBuffer();
sb.append("X509CertSelector: [\n");
if (x509Cert != null)
{
sb.append(" Certificate: ").append(x509Cert).append('\n');
}
if (serialNumber != null)
{
sb.append(" Serial Number: ").append(serialNumber).append('\n');
}
if (issuerDN != null)
{
sb.append(" Issuer: ").append(getIssuerAsString()).append('\n');
}
if (subjectDN != null)
{
sb.append(" Subject: ").append(getSubjectAsString()).append('\n');
}
try
{
if (subjectKeyID != null)
{
ByteArrayInputStream inStream = new ByteArrayInputStream(
subjectKeyID);
ASN1InputStream derInStream = new ASN1InputStream(inStream);
ASN1Object derObject = derInStream.readObject();
sb.append(" Subject Key Identifier: ")
.append(ASN1Dump.dumpAsString(derObject)).append('\n');
}
if (authorityKeyID != null)
{
ByteArrayInputStream inStream = new ByteArrayInputStream(
authorityKeyID);
ASN1InputStream derInStream = new ASN1InputStream(inStream);
ASN1Object derObject = derInStream.readObject();
sb.append(" Authority Key Identifier: ")
.append(ASN1Dump.dumpAsString(derObject)).append('\n');
}
}
catch (IOException ex)
{
sb.append(ex.getMessage()).append('\n');
}
if (certValid != null)
{
sb.append(" Certificate Valid: ").append(certValid).append('\n');
}
if (privateKeyValid != null)
{
sb.append(" Private Key Valid: ").append(privateKeyValid)
.append('\n');
}
if (subjectKeyAlgID != null)
{
sb.append(" Subject Public Key AlgID: ")
.append(subjectKeyAlgID).append('\n');
}
if (subjectPublicKey != null)
{
sb.append(" Subject Public Key: ").append(subjectPublicKey)
.append('\n');
}
if (keyUsage != null)
{
sb.append(" Key Usage: ").append(keyUsage).append('\n');
}
if (keyPurposeSet != null)
{
sb.append(" Extended Key Usage: ").append(keyPurposeSet)
.append('\n');
}
if (policy != null)
{
sb.append(" Policy: ").append(policy).append('\n');
}
sb.append(" matchAllSubjectAltNames flag: ")
.append(matchAllSubjectAltNames).append('\n');
if (subjectAltNamesByte != null)
{
sb.append(" SubjectAlternativNames: \n[");
Iterator iter = subjectAltNamesByte.iterator();
List obj;
try
{
while (iter.hasNext())
{
obj = (List)iter.next();
ByteArrayInputStream inStream = new ByteArrayInputStream(
(byte[])obj.get(1));
ASN1InputStream derInStream = new ASN1InputStream(inStream);
ASN1Object derObject = derInStream.readObject();
sb.append(" Type: ").append(obj.get(0)).append(" Data: ")
.append(ASN1Dump.dumpAsString(derObject)).append('\n');
}
}
catch (IOException ex)
{
sb.append(ex.getMessage()).append('\n');
}
sb.append("]\n");
}
if (pathToNamesByte != null)
{
sb.append(" PathToNamesNames: \n[");
Iterator iter = pathToNamesByte.iterator();
List obj;
try
{
while (iter.hasNext())
{
obj = (List)iter.next();
ByteArrayInputStream inStream = new ByteArrayInputStream(
(byte[])obj.get(1));
ASN1InputStream derInStream = new ASN1InputStream(inStream);
ASN1Object derObject = derInStream.readObject();
sb.append(" Type: ").append(obj.get(0)).append(" Data: ")
.append(ASN1Dump.dumpAsString(derObject)).append('\n');
}
}
catch (IOException ex)
{
sb.append(ex.getMessage()).append('\n');
}
sb.append("]\n");
}
sb.append(']');
return sb.toString();
}
/**
* Decides whether a Certificate
should be selected.
*
* TODO: implement missing tests (name constraints and path to names)
*
* Uses {@link org.bouncycastle.asn1.ASN1InputStream ASN1InputStream},
* {@link org.bouncycastle.asn1.ASN1Sequence ASN1Sequence},
* {@link org.bouncycastle.asn1.ASN1ObjectIdentifier ASN1ObjectIdentifier},
* {@link org.bouncycastle.asn1.ASN1Object ASN1Object},
* {@link org.bouncycastle.asn1.DERGeneralizedTime DERGeneralizedTime},
* {@link org.bouncycastle.asn1.x509.X509Name X509Name},
* {@link org.bouncycastle.asn1.x509.X509Extensions X509Extensions},
* {@link org.bouncycastle.asn1.x509.ExtendedKeyUsage ExtendedKeyUsage},
* {@link org.bouncycastle.asn1.x509.KeyPurposeId KeyPurposeId},
* {@link org.bouncycastle.asn1.x509.SubjectPublicKeyInfo SubjectPublicKeyInfo},
* {@link org.bouncycastle.asn1.x509.AlgorithmIdentifier AlgorithmIdentifier}
* to access X509 extensions
*
* @param cert
* the Certificate
to be checked
*
* @return true
if the Certificate
should be
* selected, false
otherwise
*/
public boolean match(Certificate cert)
{
boolean[] booleanArray;
List tempList;
Iterator tempIter;
if (!(cert instanceof X509Certificate))
{
return false;
}
X509Certificate certX509 = (X509Certificate)cert;
if (x509Cert != null && !x509Cert.equals(certX509))
{
return false;
}
if (serialNumber != null
&& !serialNumber.equals(certX509.getSerialNumber()))
{
return false;
}
try
{
if (issuerDNX509 != null)
{
if (!issuerDNX509.equals(PrincipalUtil
.getIssuerX509Principal(certX509), true))
{
return false;
}
}
if (subjectDNX509 != null)
{
if (!subjectDNX509.equals(PrincipalUtil
.getSubjectX509Principal(certX509), true))
{
return false;
}
}
}
catch (Exception ex)
{
return false;
}
if (subjectKeyID != null)
{
byte[] data = certX509
.getExtensionValue(X509Extensions.SubjectKeyIdentifier
.getId());
if (data == null)
{
return false;
}
try
{
ByteArrayInputStream inStream = new ByteArrayInputStream(data);
ASN1InputStream derInputStream = new ASN1InputStream(inStream);
byte[] testData = ((ASN1OctetString)derInputStream.readObject())
.getOctets();
if (!Arrays.equals(subjectKeyID, testData))
{
return false;
}
}
catch (IOException ex)
{
return false;
}
}
if (authorityKeyID != null)
{
byte[] data = certX509
.getExtensionValue(X509Extensions.AuthorityKeyIdentifier
.getId());
if (data == null)
{
return false;
}
try
{
ByteArrayInputStream inStream = new ByteArrayInputStream(data);
ASN1InputStream derInputStream = new ASN1InputStream(inStream);
byte[] testData = ((ASN1OctetString)derInputStream.readObject())
.getOctets();
if (!Arrays.equals(authorityKeyID, testData))
{
return false;
}
}
catch (IOException ex)
{
return false;
}
}
if (certValid != null)
{
if (certX509.getNotAfter() != null
&& certValid.after(certX509.getNotAfter()))
{
return false;
}
if (certX509.getNotBefore() != null
&& certValid.before(certX509.getNotBefore()))
{
return false;
}
}
if (privateKeyValid != null)
{
try
{
byte[] data = certX509
.getExtensionValue(X509Extensions.PrivateKeyUsagePeriod
.getId());
if (data != null)
{
ByteArrayInputStream inStream = new ByteArrayInputStream(
data);
ASN1InputStream derInputStream = new ASN1InputStream(inStream);
inStream = new ByteArrayInputStream(
((ASN1OctetString)derInputStream.readObject())
.getOctets());
derInputStream = new ASN1InputStream(inStream);
// TODO fix this, Sequence contains tagged objects
ASN1Sequence derObject = (ASN1Sequence)derInputStream
.readObject();
ASN1GeneralizedTime derDate = DERGeneralizedTime
.getInstance(derObject.getObjectAt(0));
SimpleDateFormat dateF = new SimpleDateFormat(
"yyyyMMddHHmmssZ");
if (privateKeyValid.before(dateF.parse(derDate.getTime())))
{
return false;
}
derDate = DERGeneralizedTime.getInstance(derObject
.getObjectAt(1));
if (privateKeyValid.after(dateF.parse(derDate.getTime())))
{
return false;
}
}
}
catch (Exception ex)
{
return false;
}
}
if (subjectKeyAlgID != null)
{
try
{
ByteArrayInputStream inStream = new ByteArrayInputStream(
certX509.getPublicKey().getEncoded());
ASN1InputStream derInputStream = new ASN1InputStream(inStream);
SubjectPublicKeyInfo publicKeyInfo = new SubjectPublicKeyInfo(
(ASN1Sequence)derInputStream.readObject());
AlgorithmIdentifier algInfo = publicKeyInfo.getAlgorithmId();
if (!algInfo.getObjectId().equals(subjectKeyAlgID))
{
return false;
}
}
catch (Exception ex)
{
return false;
}
}
if (subjectPublicKeyByte != null)
{
if (!Arrays.equals(subjectPublicKeyByte, certX509.getPublicKey()
.getEncoded()))
{
return false;
}
}
if (subjectPublicKey != null)
{
if (!subjectPublicKey.equals(certX509.getPublicKey()))
{
return false;
}
}
if (keyUsage != null)
{
booleanArray = certX509.getKeyUsage();
if (booleanArray != null)
{
for (int i = 0; i < keyUsage.length; i++)
{
if (keyUsage[i]
&& (booleanArray.length <= i || !booleanArray[i]))
{
return false;
}
}
}
}
if (keyPurposeSet != null && !keyPurposeSet.isEmpty())
{
try
{
byte[] data = certX509
.getExtensionValue(X509Extensions.ExtendedKeyUsage
.getId());
if (data != null)
{
ByteArrayInputStream inStream = new ByteArrayInputStream(
data);
ASN1InputStream derInputStream = new ASN1InputStream(inStream);
ExtendedKeyUsage extendedKeyUsage = ExtendedKeyUsage.getInstance(
(ASN1Sequence)derInputStream.readObject());
tempIter = keyPurposeSet.iterator();
while (tempIter.hasNext())
{
if (!extendedKeyUsage
.hasKeyPurposeId((KeyPurposeId)tempIter.next()))
{
return false;
}
}
}
}
catch (Exception ex)
{
return false;
}
}
if (minMaxPathLen != -1)
{
if (minMaxPathLen == -2 && certX509.getBasicConstraints() != -1)
{
return false;
}
if (minMaxPathLen >= 0
&& certX509.getBasicConstraints() < minMaxPathLen)
{
return false;
}
}
if (policyOID != null)
{
try
{
byte[] data = certX509
.getExtensionValue(X509Extensions.CertificatePolicies
.getId());
if (data == null)
{
return false;
}
if (!policyOID.isEmpty())
{
ByteArrayInputStream inStream = new ByteArrayInputStream(
data);
ASN1InputStream derInputStream = new ASN1InputStream(inStream);
inStream = new ByteArrayInputStream(
((ASN1OctetString)derInputStream.readObject())
.getOctets());
derInputStream = new ASN1InputStream(inStream);
Enumeration policySequence = ((ASN1Sequence)derInputStream
.readObject()).getObjects();
ASN1Sequence policyObject;
boolean test = false;
while (policySequence.hasMoreElements() && !test)
{
policyObject = (ASN1Sequence)policySequence
.nextElement();
if (policyOID.contains(policyObject.getObjectAt(0)))
{
test = true;
}
}
if (!test)
{
return false;
}
}
}
catch (Exception ex)
{
ex.printStackTrace();
return false;
}
}
if (subjectAltNamesByte != null)
{
try
{
byte[] data = certX509
.getExtensionValue(X509Extensions.SubjectAlternativeName
.getId());
if (data == null)
{
return false;
}
ByteArrayInputStream inStream = new ByteArrayInputStream(data);
ASN1InputStream derInputStream = new ASN1InputStream(inStream);
inStream = new ByteArrayInputStream(
((ASN1OctetString)derInputStream.readObject())
.getOctets());
derInputStream = new ASN1InputStream(inStream);
Enumeration altNamesSequence = ((ASN1Sequence)derInputStream
.readObject()).getObjects();
ASN1TaggedObject altNameObject;
boolean test = false;
Set testSet = new HashSet(subjectAltNamesByte);
List testList;
ASN1Object derData;
ByteArrayOutputStream outStream;
DEROutputStream derOutStream;
while (altNamesSequence.hasMoreElements() && !test)
{
altNameObject = (ASN1TaggedObject)altNamesSequence
.nextElement();
testList = new ArrayList(2);
testList.add(Integers.valueOf(altNameObject.getTagNo()));
derData = altNameObject.getObject();
outStream = new ByteArrayOutputStream();
derOutStream = new DEROutputStream(outStream);
derOutStream.writeObject(derData);
derOutStream.close();
testList.add(outStream.toByteArray());
if (testSet.remove(testList))
{
test = true;
}
if (matchAllSubjectAltNames && !testSet.isEmpty())
{
test = false;
}
}
if (!test)
{
return false;
}
}
catch (Exception ex)
{
ex.printStackTrace();
return false;
}
}
return true;
}
/**
* Returns a copy of this object.
*
* @return the copy
*/
public Object clone()
{
try
{
X509CertSelector copy = (X509CertSelector)super.clone();
if (issuerDN instanceof byte[])
{
copy.issuerDN = ((byte[])issuerDN).clone();
}
if (subjectDN instanceof byte[])
{
copy.subjectDN = ((byte[])subjectDN).clone();
}
if (subjectKeyID != null)
{
copy.subjectKeyID = (byte[])subjectKeyID.clone();
}
if (authorityKeyID != null)
{
copy.authorityKeyID = (byte[])authorityKeyID.clone();
}
if (subjectPublicKeyByte != null)
{
copy.subjectPublicKeyByte = (byte[])subjectPublicKeyByte
.clone();
}
if (keyUsage != null)
{
copy.keyUsage = (boolean[])keyUsage.clone();
}
if (keyPurposeSet != null)
{
copy.keyPurposeSet = new HashSet(keyPurposeSet);
}
if (policy != null)
{
copy.policy = new HashSet(policy);
copy.policyOID = new HashSet();
Iterator iter = policyOID.iterator();
while (iter.hasNext())
{
copy.policyOID.add(new ASN1ObjectIdentifier(
((ASN1ObjectIdentifier)iter.next()).getId()));
}
}
if (subjectAltNames != null)
{
copy.subjectAltNames = new HashSet(getSubjectAlternativeNames());
Iterator iter = subjectAltNamesByte.iterator();
List obj;
List cloneObj;
while (iter.hasNext())
{
obj = (List)iter.next();
cloneObj = new ArrayList();
cloneObj.add(obj.get(0));
cloneObj.add(((byte[])obj.get(1)).clone());
copy.subjectAltNamesByte.add(cloneObj);
}
}
if (pathToNames != null)
{
copy.pathToNames = new HashSet(getPathToNames());
Iterator iter = pathToNamesByte.iterator();
List obj;
List cloneObj;
while (iter.hasNext())
{
obj = (List)iter.next();
cloneObj = new ArrayList();
cloneObj.add(obj.get(0));
cloneObj.add(((byte[])obj.get(1)).clone());
copy.pathToNamesByte.add(cloneObj);
}
}
return copy;
}
catch (CloneNotSupportedException e)
{
/* Cannot happen */
throw new InternalError(e.toString());
}
}
}