java.security.cert.X509CertSelector Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.security.cert;
import java.io.IOException;
import java.math.BigInteger;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.security.auth.x500.X500Principal;
import libcore.util.EmptyArray;
import org.apache.harmony.security.asn1.ASN1OctetString;
import org.apache.harmony.security.utils.Array;
import org.apache.harmony.security.x509.AlgorithmIdentifier;
import org.apache.harmony.security.x509.CertificatePolicies;
import org.apache.harmony.security.x509.GeneralName;
import org.apache.harmony.security.x509.GeneralNames;
import org.apache.harmony.security.x509.NameConstraints;
import org.apache.harmony.security.x509.PolicyInformation;
import org.apache.harmony.security.x509.PrivateKeyUsagePeriod;
import org.apache.harmony.security.x509.SubjectPublicKeyInfo;
/**
* A certificate selector ({@code CertSelector} for selecting {@code
* X509Certificate}s that match the specified criteria.
*/
public class X509CertSelector implements CertSelector {
// match criteria
private X509Certificate certificateEquals;
private BigInteger serialNumber;
private X500Principal issuer;
private X500Principal subject;
private byte[] subjectKeyIdentifier;
private byte[] authorityKeyIdentifier;
private Date certificateValid;
private String subjectPublicKeyAlgID;
private Date privateKeyValid;
private byte[] subjectPublicKey;
private boolean[] keyUsage;
private Set extendedKeyUsage;
private boolean matchAllNames = true;
private int pathLen = -1;
private List[] subjectAltNames;
private NameConstraints nameConstraints;
private Set policies;
private ArrayList pathToNames;
// needed to avoid needless encoding/decoding work
private PublicKey subjectPublicKeyImpl;
private String issuerName;
private byte[] issuerBytes;
/**
* Creates a new {@code X509CertSelector}.
*/
public X509CertSelector() {}
/**
* Sets the certificate that a matching certificate must be equal to.
*
* @param certificate
* the certificate to match, or null to not check this criteria.
*/
public void setCertificate(X509Certificate certificate) {
certificateEquals = certificate;
}
/**
* Returns the certificate that a matching certificate must be equal to.
*
* @return the certificate to match, or null if this criteria is not
* checked.
*/
public X509Certificate getCertificate() {
return certificateEquals;
}
/**
* Sets the serial number that a certificate must match.
*
* @param serialNumber
* the serial number to match, or {@code null} to not check the
* serial number.
*/
public void setSerialNumber(BigInteger serialNumber) {
this.serialNumber = serialNumber;
}
/**
* Returns the serial number that a certificate must match.
*
* @return the serial number to match, or {@code null} if the serial number
* is not to be checked.
*/
public BigInteger getSerialNumber() {
return serialNumber;
}
/**
* Sets the issuer that a certificate must match.
*
* @param issuer
* the issuer to match, or {@code null} if the issuer is not to
* be checked.
*/
public void setIssuer(X500Principal issuer) {
this.issuer = issuer;
this.issuerName = null;
this.issuerBytes = null;
}
/**
* Returns the issuer that a certificate must match.
*
* @return the issuer that a certificate must match, or {@code null} if the
* issuer is not to be checked.
*/
public X500Principal getIssuer() {
return issuer;
}
/**
* Do not use, use {@link #getIssuer()} or
* {@link #getIssuerAsBytes()} instead. Sets the issuer that a certificate
* must match.
*
* @param issuerName
* the issuer in a RFC 2253 format string, or {@code null} to not
* check the issuer.
* @throws IOException
* if parsing the issuer fails.
*/
public void setIssuer(String issuerName) throws IOException {
if (issuerName == null) {
this.issuer = null;
this.issuerName = null;
this.issuerBytes = null;
return;
}
try {
this.issuer = new X500Principal(issuerName);
this.issuerName = issuerName;
this.issuerBytes = null;
} catch (IllegalArgumentException e) {
throw new IOException(e.getMessage());
}
}
/**
* Do not use, use {@link #getIssuer()} or
* {@link #getIssuerAsBytes()} instead. Returns the issuer that a
* certificate must match in a RFC 2253 format string.
*
* @return the issuer in a RFC 2253 format string, or {@code null} if the
* issuer is not to be checked.
*/
public String getIssuerAsString() {
if (issuer == null) {
return null;
}
if (issuerName == null) {
issuerName = issuer.getName();
}
return issuerName;
}
/**
* Sets the issuer that a certificate must match.
*
* @param issuerDN
* the distinguished issuer name in ASN.1 DER encoded format, or
* {@code null} to not check the issuer.
* @throws IOException
* if decoding the issuer fail.
*/
public void setIssuer(byte[] issuerDN) throws IOException {
if (issuerDN == null) {
issuer = null;
return;
}
try {
issuer = new X500Principal(issuerDN);
this.issuerName = null;
this.issuerBytes = new byte[issuerDN.length];
System.arraycopy(issuerDN, 0, this.issuerBytes, 0, issuerDN.length);
} catch (IllegalArgumentException e) {
throw new IOException(e.getMessage());
}
}
/**
* Returns the issuer that a certificate must match.
*
* @return the distinguished issuer name in ASN.1 DER encoded format, or
* {@code null} if the issuer is not to be checked.
* @throws IOException
* if encoding the issuer fails.
*/
public byte[] getIssuerAsBytes() throws IOException {
if (issuer == null) {
return null;
}
if (issuerBytes == null) {
issuerBytes = issuer.getEncoded();
}
byte[] result = new byte[issuerBytes.length];
System.arraycopy(issuerBytes, 0, result, 0, issuerBytes.length);
return result;
}
/**
* Set the subject that a certificate must match.
*
* @param subject
* the subject distinguished name or {@code null} to not check
* the subject.
*/
public void setSubject(X500Principal subject) {
this.subject = subject;
}
/**
* Returns the subject that a certificate must match.
*
* @return the subject distinguished name, or null if the subject is not to
* be checked.
*/
public X500Principal getSubject() {
return subject;
}
/**
* Do not use, use {@link #setSubject(byte[])} or
* {@link #setSubject(X500Principal)} instead. Returns the subject that a
* certificate must match.
*
* @param subjectDN
* the subject distinguished name in RFC 2253 format or {@code
* null} to not check the subject.
* @throws IOException
* if decoding the subject fails.
*/
public void setSubject(String subjectDN) throws IOException {
if (subjectDN == null) {
subject = null;
return;
}
try {
subject = new X500Principal(subjectDN);
} catch (IllegalArgumentException e) {
throw new IOException(e.getMessage());
}
}
/**
* Do not use, use {@link #getSubject()} or
* {@link #getSubjectAsBytes()} instead. Returns the subject that a
* certificate must match.
*
* @return the subject distinguished name in RFC 2253 format, or {@code
* null} if the subject is not to be checked.
*/
public String getSubjectAsString() {
if (subject == null) {
return null;
}
return subject.getName();
}
/**
* Sets the subject that a certificate must match.
*
* @param subjectDN
* the subject distinguished name in ASN.1 DER format, or {@code
* null} to not check the subject.
* @throws IOException
* if decoding the subject fails.
*/
public void setSubject(byte[] subjectDN) throws IOException {
if (subjectDN == null) {
subject = null;
return;
}
try {
subject = new X500Principal(subjectDN);
} catch (IllegalArgumentException e) {
throw new IOException(e.getMessage());
}
}
/**
* Returns the subject that a certificate must match.
*
* @return the subject distinguished name in ASN.1 DER format, or {@code
* null} if the subject is not to be checked.
* @throws IOException
* if encoding the subject fails.
*/
public byte[] getSubjectAsBytes() throws IOException {
if (subject == null) {
return null;
}
return subject.getEncoded();
}
/**
* Sets the criterion for the {@literal SubjectKeyIdentifier} extension.
*
* The {@code subjectKeyIdentifier} should be a single DER encoded value.
*
* @param subjectKeyIdentifier
* the subject key identifier or {@code null} to disable this
* check.
*/
public void setSubjectKeyIdentifier(byte[] subjectKeyIdentifier) {
if (subjectKeyIdentifier == null) {
this.subjectKeyIdentifier = null;
return;
}
this.subjectKeyIdentifier = new byte[subjectKeyIdentifier.length];
System.arraycopy(subjectKeyIdentifier, 0, this.subjectKeyIdentifier, 0,
subjectKeyIdentifier.length);
}
/**
* Returns the criterion for the {@literal SubjectKeyIdentifier} extension.
*
* @return the subject key identifier or {@code null} if it is not to be
* checked.
*/
public byte[] getSubjectKeyIdentifier() {
if (subjectKeyIdentifier == null) {
return null;
}
byte[] res = new byte[subjectKeyIdentifier.length];
System.arraycopy(subjectKeyIdentifier, 0, res, 0, res.length);
return res;
}
/**
* Sets the criterion for the {@literal AuthorityKeyIdentifier} extension.
*
* @param authorityKeyIdentifier
* the authority key identifier, or {@code null} to disable this
* check.
*/
public void setAuthorityKeyIdentifier(byte[] authorityKeyIdentifier) {
if (authorityKeyIdentifier == null) {
this.authorityKeyIdentifier = null;
return;
}
this.authorityKeyIdentifier = new byte[authorityKeyIdentifier.length];
System.arraycopy(authorityKeyIdentifier, 0,
this.authorityKeyIdentifier, 0,
authorityKeyIdentifier.length);
}
/**
* Returns the criterion for the {@literal AuthorityKeyIdentifier}
* extension.
*
* @return the authority key identifier, or {@code null} if it is not to be
* checked.
*/
public byte[] getAuthorityKeyIdentifier() {
if (authorityKeyIdentifier == null) {
return null;
}
byte[] res = new byte[authorityKeyIdentifier.length];
System.arraycopy(authorityKeyIdentifier, 0, res, 0, res.length);
return res;
}
/**
* Sets the criterion for the validity date of the certificate.
*
* The certificate must be valid at the specified date.
* @param certificateValid
* the validity date or {@code null} to not check the date.
*/
public void setCertificateValid(Date certificateValid) {
this.certificateValid = (certificateValid == null)
? null
: (Date) certificateValid.clone();
}
/**
* Returns the criterion for the validity date of the certificate.
*
* @return the validity date or {@code null} if the date is not to be
* checked.
*/
public Date getCertificateValid() {
return (certificateValid == null)
? null
: (Date) certificateValid.clone();
}
/**
* Sets the criterion for the validity date of the private key.
*
* The private key must be valid at the specified date.
*
* @param privateKeyValid
* the validity date or {@code null} to not check the date.
*/
public void setPrivateKeyValid(Date privateKeyValid) {
if (privateKeyValid == null) {
this.privateKeyValid = null;
return;
}
this.privateKeyValid = (Date) privateKeyValid.clone();
}
/**
* Returns the criterion for the validity date of the private key.
*
* The private key must be valid at the specified date.
*
* @return the validity date or {@code null} if the date is not to be
* checked.
*/
public Date getPrivateKeyValid() {
if (privateKeyValid != null) {
return (Date) privateKeyValid.clone();
}
return null;
}
private void checkOID(String oid) throws IOException {
int beg = 0;
int end = oid.indexOf('.', beg);
try {
int comp = Integer.parseInt(oid.substring(beg, end));
beg = end + 1;
if ((comp < 0) || (comp > 2)) {
throw new IOException("Bad OID: " + oid);
}
end = oid.indexOf('.', beg);
comp = Integer.parseInt(oid.substring(beg, end));
if ((comp < 0) || (comp > 39)) {
throw new IOException("Bad OID: " + oid);
}
} catch (IndexOutOfBoundsException e) {
throw new IOException("Bad OID: " + oid);
} catch (NumberFormatException e) {
throw new IOException("Bad OID: " + oid);
}
}
/**
* Sets the criterion for the subject public key signature algorithm.
*
* The certificate must contain a subject public key with the algorithm
* specified.
*
* @param oid
* the OID (object identifier) of the signature algorithm or
* {@code null} to not check the OID.
* @throws IOException
* if the specified object identifier is invalid.
*/
public void setSubjectPublicKeyAlgID(String oid) throws IOException {
if (oid == null) {
subjectPublicKeyAlgID = null;
return;
}
checkOID(oid);
subjectPublicKeyAlgID = oid;
}
/**
* Returns the criterion for the subject public key signature algorithm.
*
* @return the OID (object identifier) or the signature algorithm or {@code
* null} if it's not to be checked.
*/
public String getSubjectPublicKeyAlgID() {
return subjectPublicKeyAlgID;
}
/**
* Sets the criterion for the subject public key.
*
* @param key
* the subject public key or {@code null} to not check the key.
*/
public void setSubjectPublicKey(PublicKey key) {
subjectPublicKey = (key == null) ? null : key.getEncoded();
subjectPublicKeyImpl = key;
}
/**
* Sets the criterion for the subject public key.
*
* @param key
* the subject public key in ASN.1 DER encoded format or {@code null} to
* not check the key.
* @throws IOException
* if decoding the the public key fails.
*/
public void setSubjectPublicKey(byte[] key) throws IOException {
if (key == null) {
subjectPublicKey = null;
subjectPublicKeyImpl = null;
return;
}
subjectPublicKey = new byte[key.length];
System.arraycopy(key, 0, subjectPublicKey, 0, key.length);
subjectPublicKeyImpl =
((SubjectPublicKeyInfo) SubjectPublicKeyInfo.ASN1.decode(key))
.getPublicKey();
}
/**
* Returns the criterion for the subject public key.
*
* @return the subject public key or {@code null} if the key is not to be
* checked.
*/
public PublicKey getSubjectPublicKey() {
return subjectPublicKeyImpl;
}
/**
* Sets the criterion for the {@literal KeyUsage} extension.
*
* @param keyUsage
* the boolean array in the format as returned by
* {@link X509Certificate#getKeyUsage()}, or {@code null} to not
* check the key usage.
*/
public void setKeyUsage(boolean[] keyUsage) {
if (keyUsage == null) {
this.keyUsage = null;
return;
}
this.keyUsage = new boolean[keyUsage.length];
System.arraycopy(keyUsage, 0, this.keyUsage, 0, keyUsage.length);
}
/**
* Returns the criterion for the {@literal KeyUsage} extension.
*
* @return the boolean array in the format as returned by
* {@link X509Certificate#getKeyUsage()}, or {@code null} if the key
* usage is not to be checked.
*/
public boolean[] getKeyUsage() {
if (keyUsage == null) {
return null;
}
boolean[] result = new boolean[keyUsage.length];
System.arraycopy(keyUsage, 0, result, 0, keyUsage.length);
return result;
}
/**
* Sets the criterion for the {@literal ExtendedKeyUsage} extension.
*
* @param keyUsage
* the set of key usage OIDs, or {@code null} to not check it.
* @throws IOException
* if one of the OIDs is invalid.
*/
public void setExtendedKeyUsage(Set keyUsage)
throws IOException {
extendedKeyUsage = null;
if ((keyUsage == null) || (keyUsage.size() == 0)) {
return;
}
HashSet key_u = new HashSet();
for (String usage : keyUsage) {
checkOID(usage);
key_u.add(usage);
}
extendedKeyUsage = Collections.unmodifiableSet(key_u);
}
/**
* Returns the criterion for the {@literal ExtendedKeyUsage} extension.
*
* @return the set of key usage OIDs, or {@code null} if it's not to be
* checked.
*/
public Set getExtendedKeyUsage() {
return extendedKeyUsage;
}
/**
* Sets the flag for the matching behavior for subject alternative names.
*
* The flag indicates whether a certificate must contain all or at least one
* of the subject alternative names specified by {@link
* #setSubjectAlternativeNames} or {@link #addSubjectAlternativeName}.
*
* @param matchAllNames
* {@code true} if a certificate must contain all of the
* specified subject alternative names, otherwise {@code false}.
*/
public void setMatchAllSubjectAltNames(boolean matchAllNames) {
this.matchAllNames = matchAllNames;
}
/**
* Returns the flag for the matching behavior for subject alternative names.
*
* The flag indicates whether a certificate must contain all or at least one
* of the subject alternative names specified by {@link
* #setSubjectAlternativeNames} or {@link #addSubjectAlternativeName}.
*
* @return {@code true} if a certificate must contain all of the specified
* subject alternative names, otherwise {@code false}.
*/
public boolean getMatchAllSubjectAltNames() {
return matchAllNames;
}
/**
* Sets the criterion for subject alternative names.
*
* the certificate must contain all or at least one of the specified subject
* alternative names. The behavior is specified by
* {@link #getMatchAllSubjectAltNames}.
*
* The specified parameter {@code names} is a collection with an entry for
* each name to be included in the criterion. The name is specified as a
* {@code List}, the first entry must be an {@code Integer} specifying the
* name type (0-8), the second entry must be a {@code String} or a byte
* array specifying the name (in string or ASN.1 DER encoded form)
*
* @param names
* the names collection or {@code null} to not perform this check.
* @throws IOException
* if the decoding of a name fails.
*/
public void setSubjectAlternativeNames(Collection> names) throws IOException {
subjectAltNames = null;
if ((names == null) || (names.size() == 0)) {
return;
}
for (List> name : names) {
int tag = (Integer) name.get(0);
Object value = name.get(1);
if (value instanceof String) {
addSubjectAlternativeName(tag, (String) value);
} else if (value instanceof byte[]) {
addSubjectAlternativeName(tag, (byte[]) value);
} else {
throw new IOException("name neither a String nor a byte[]");
}
}
}
/**
* Adds a subject alternative name to the respective criterion.
*
* @param tag
* the type of the name
* @param name
* the name in string format.
* @throws IOException
* if parsing the name fails.
*/
public void addSubjectAlternativeName(int tag, String name)
throws IOException {
GeneralName alt_name = new GeneralName(tag, name);
// create only if there was not any errors
if (subjectAltNames == null) {
subjectAltNames = new ArrayList[9];
}
if (subjectAltNames[tag] == null) {
subjectAltNames[tag] = new ArrayList();
}
subjectAltNames[tag].add(alt_name);
}
/**
* Adds a subject alternative name to the respective criterion.
*
* @param tag
* the type of the name.
* @param name
* the name in ASN.1 DER encoded form.
* @throws IOException
* if the decoding of the name fails.
*/
public void addSubjectAlternativeName(int tag, byte[] name)
throws IOException {
GeneralName alt_name = new GeneralName(tag, name);
// create only if there was not any errors
if (subjectAltNames == null) {
subjectAltNames = new ArrayList[9];
}
if (subjectAltNames[tag] == null) {
subjectAltNames[tag] = new ArrayList();
}
subjectAltNames[tag].add(alt_name);
}
/**
* Returns the criterion for subject alternative names.
*
* the certificate must contain all or at least one of the specified subject
* alternative names. The behavior is specified by
* {@link #getMatchAllSubjectAltNames}.
*
* The subject alternative names is a collection with an entry for each name
* included in the criterion. The name is specified as a {@code List}, the
* first entry is an {@code Integer} specifying the name type (0-8), the
* second entry is byte array specifying the name in ASN.1 DER encoded form)
*
* @return the names collection or {@code null} if none specified.
*/
public Collection> getSubjectAlternativeNames() {
if (subjectAltNames == null) {
return null;
}
ArrayList> result = new ArrayList>();
for (int tag=0; tag<9; tag++) {
if (subjectAltNames[tag] != null) {
for (int name=0; name list = new ArrayList