diff options
Diffstat (limited to 'libjava/classpath/java/security/cert')
-rw-r--r-- | libjava/classpath/java/security/cert/X509CertSelector.java | 1419 | ||||
-rw-r--r-- | libjava/classpath/java/security/cert/X509Certificate.java | 4 |
2 files changed, 817 insertions, 606 deletions
diff --git a/libjava/classpath/java/security/cert/X509CertSelector.java b/libjava/classpath/java/security/cert/X509CertSelector.java index 154ed2e4d98..7a7db086b0a 100644 --- a/libjava/classpath/java/security/cert/X509CertSelector.java +++ b/libjava/classpath/java/security/cert/X509CertSelector.java @@ -1,5 +1,5 @@ /* X509CertSelector.java -- selects X.509 certificates by criteria. - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -40,9 +40,17 @@ package java.security.cert; import gnu.classpath.SystemProperties; import gnu.java.security.OID; +import gnu.java.security.x509.GnuPKIExtension; +import gnu.java.security.x509.ext.CertificatePolicies; +import gnu.java.security.x509.ext.Extension; +import gnu.java.security.x509.ext.GeneralName; +import gnu.java.security.x509.ext.GeneralSubtree; +import gnu.java.security.x509.ext.NameConstraints; +import gnu.java.security.x509.ext.GeneralName.Kind; import java.io.IOException; import java.math.BigInteger; +import java.net.InetAddress; import java.security.KeyFactory; import java.security.PublicKey; import java.security.spec.X509EncodedKeySpec; @@ -88,6 +96,48 @@ public class X509CertSelector implements CertSelector, Cloneable private static final String SUBJECT_KEY_ID = "2.5.29.14"; private static final String NAME_CONSTRAINTS_ID = "2.5.29.30"; + private static boolean checkOid(int[] oid) + { + return (oid != null && oid.length > 2 && + (oid[0] >= 0 && oid[0] <= 2) && (oid[1] >= 0 && oid[1] <= 39)); + } + + private static GeneralName makeName(int id, String name) throws IOException + { + byte[] nameBytes = null; + GeneralName.Kind kind = GeneralName.Kind.forTag(id); + switch (Kind.forTag(id)) + { + case dNSName: + case rfc822Name: + case uniformResourceIdentifier: + nameBytes = name.getBytes("ASCII"); + break; + + case iPAddress: + InetAddress addr = InetAddress.getByName(name); + nameBytes = addr.getAddress(); + break; + + case registeredId: + OID oid = new OID(name); + nameBytes = oid.getDER(); + break; + + case directoryName: + X500Principal xname = new X500Principal(name); + nameBytes = xname.getEncoded(); + break; + + case ediPartyName: + case x400Address: + case otherName: + throw new IOException("cannot decode string representation of " + + kind); + } + return new GeneralName(kind, nameBytes); + } + private int basicConstraints; private X509Certificate cert; private BigInteger serialNo; @@ -100,14 +150,12 @@ public class X509CertSelector implements CertSelector, Cloneable private OID sigId; private PublicKey subjectKey; private X509EncodedKeySpec subjectKeySpec; - private Set keyPurposeSet; - private List altNames; + private Set<String> keyPurposeSet; + private List<GeneralName> altNames; private boolean matchAllNames; private byte[] nameConstraints; - private Set policy; - - // Constructors. - // ------------------------------------------------------------------------ + private Set<OID> policy; + private List<GeneralName> pathToNames; /** * Creates a new X.509 certificate selector. The new selector will be @@ -119,285 +167,280 @@ public class X509CertSelector implements CertSelector, Cloneable basicConstraints = -1; } - // Instance methods. - // ------------------------------------------------------------------------ - /** - * Returns the certificate criterion, or <code>null</code> if this value - * was not set. + * Add a name to match in the NameConstraints extension. The argument is + * the DER-encoded bytes of a GeneralName structure. + * + * See the method {@link #addSubjectAlternativeName(int, byte[])} for the + * format of the GeneralName structure. * - * @return The certificate. + * @param id The name identifier. Must be between 0 and 8. + * @param name The DER-encoded bytes of the name to match. + * @throws IOException If the name DER is malformed. */ - public X509Certificate getCertificate() + public void addPathToName(int id, byte[] name) throws IOException { - return cert; + GeneralName generalName = new GeneralName(GeneralName.Kind.forTag(id), name); + if (pathToNames == null) + pathToNames = new LinkedList<GeneralName>(); + pathToNames.add(generalName); } /** - * Sets the certificate criterion. If set, only certificates that are - * equal to the certificate passed here will be accepted. + * Add a name to match in the NameConstraints extension. This method will + * only recognize certain types of name that have convenient string + * encodings. For robustness, you should use the {@link + * #addPathToName(int, byte[])} method whenever possible. * - * @param cert The certificate. + * @param id The name identifier. Must be between 0 and 8. + * @param name The name. + * @throws IOException If the name cannot be decoded. */ - public void setCertificate(X509Certificate cert) + public void addPathToName(int id, String name) throws IOException { - this.cert = cert; + GeneralName generalName = makeName(id, name); + if (pathToNames == null) + pathToNames = new LinkedList<GeneralName>(); + pathToNames.add(generalName); } /** - * Returns the serial number criterion, or <code>null</code> if this - * value was not set. + * Add a name, as DER-encoded bytes, to the subject alternative names + * criterion. + * + * The name is a GeneralName structure, which has the ASN.1 format: + * + * <pre> + 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> * - * @return The serial number. + * @param id The type of name this is. + * @param name The DER-encoded name. + * @throws IOException If the name is not a valid DER sequence. */ - public BigInteger getSerialNumber() + public void addSubjectAlternativeName(int id, byte[] name) + throws IOException { - return serialNo; + GeneralName generalName = new GeneralName(GeneralName.Kind.forTag(id), name); + if (altNames == null) + altNames = new LinkedList<GeneralName>(); + altNames.add(generalName); } /** - * Sets the serial number of the desired certificate. Only certificates that - * contain this serial number are accepted. + * Add a name to the subject alternative names criterion. This method will + * only recognize certain types of name that have convenient string + * encodings. For robustness, you should use the {@link + * #addSubjectAlternativeName(int, byte[])} method whenever possible. + * + * This method can only decode certain name kinds of names as strings. * - * @param serialNo The serial number. + * @param id The type of name this is. Must be in the range [0,8]. + * @param name The name. + * @throws IOException If the id is out of range, or if the name + * is null. */ - public void setSerialNumber(BigInteger serialNo) + public void addSubjectAlternativeName(int id, String name) + throws IOException { - this.serialNo = serialNo; + GeneralName generalName = makeName(id, name); + if (altNames == null) + altNames = new LinkedList<GeneralName>(); + altNames.add(generalName); } - /** - * Returns the issuer criterion as a string, or <code>null</code> if this - * value was not set. - * - * @return The issuer. - */ - public String getIssuerAsString() + public Object clone() { - if (issuer != null) - return issuer.getName(); - else - return null; + try + { + return super.clone(); + } + catch (CloneNotSupportedException shouldNotHappen) + { + throw new Error(shouldNotHappen); + } } /** - * Returns the issuer criterion as a sequence of DER bytes, or - * <code>null</code> if this value was not set. + * Returns the authority key identifier criterion, or <code>null</code> if + * this value was not set. Note that the byte array is cloned to prevent + * modification. * - * @return The issuer. + * @return The authority key identifier. */ - public byte[] getIssuerAsBytes() throws IOException + public byte[] getAuthorityKeyIdentifier() { - if (issuer != null) - return issuer.getEncoded(); + if (authKeyId != null) + return (byte[]) authKeyId.clone(); else return null; } /** - * Sets the issuer, specified as a string representation of the issuer's - * distinguished name. Only certificates issued by this issuer will - * be accepted. + * Returns the basic constraints criterion, or -1 if this value is not set. * - * @param name The string representation of the issuer's distinguished name. - * @throws IOException If the given name is incorrectly formatted. + * @return The basic constraints. */ - public void setIssuer(String name) throws IOException + public int getBasicConstraints() { - if (name != null) - { - try - { - issuer = new X500Principal(name); - } - catch (IllegalArgumentException iae) - { - throw new IOException(iae.getMessage()); - } - } - else - issuer = null; + return basicConstraints; } /** - * Sets the issuer, specified as the DER encoding of the issuer's - * distinguished name. Only certificates issued by this issuer will - * be accepted. + * Returns the certificate criterion, or <code>null</code> if this value + * was not set. * - * @param name The DER encoding of the issuer's distinguished name. - * @throws IOException If the given name is incorrectly formatted. + * @return The certificate. */ - public void setIssuer(byte[] name) throws IOException + public X509Certificate getCertificate() { - if (name != null) - { - try - { - issuer = new X500Principal(name); - } - catch (IllegalArgumentException iae) - { - throw new IOException(iae.getMessage()); - } - } - else - issuer = null; + return cert; } /** - * Returns the subject criterion as a string, of <code>null</code> if - * this value was not set. + * Returns the date at which certificates must be valid, or <code>null</code> + * if this criterion was not set. * - * @return The subject. + * @return The target certificate valitity date. */ - public String getSubjectAsString() + public Date getCertificateValid() { - if (subject != null) - return subject.getName(); + if (certValid != null) + return (Date) certValid.clone(); else return null; } /** - * Returns the subject criterion as a sequence of DER bytes, or - * <code>null</code> if this value is not set. + * Returns the set of extended key purpose IDs, as an unmodifiable set + * of OID strings. Returns <code>null</code> if this criterion is not + * set. * - * @return The subject. + * @return The set of key purpose OIDs (strings). */ - public byte[] getSubjectAsBytes() throws IOException + public Set<String> getExtendedKeyUsage() { - if (subject != null) - return subject.getEncoded(); + if (keyPurposeSet != null) + return Collections.unmodifiableSet(keyPurposeSet); else return null; } /** - * Sets the subject, specified as a string representation of the - * subject's distinguished name. Only certificates with the given - * subject will be accepted. + * Returns the issuer criterion as a sequence of DER bytes, or + * <code>null</code> if this value was not set. * - * @param name The string representation of the subject's distinguished name. - * @throws IOException If the given name is incorrectly formatted. + * @return The issuer. */ - public void setSubject(String name) throws IOException + public byte[] getIssuerAsBytes() throws IOException { - if (name != null) - { - try - { - subject = new X500Principal(name); - } - catch (IllegalArgumentException iae) - { - throw new IOException(iae.getMessage()); - } - } + if (issuer != null) + return issuer.getEncoded(); else - subject = null; + return null; } /** - * Sets the subject, specified as the DER encoding of the subject's - * distinguished name. Only certificates with the given subject will - * be accepted. + * Returns the issuer criterion as a string, or <code>null</code> if this + * value was not set. * - * @param name The DER encoding of the subject's distinguished name. - * @throws IOException If the given name is incorrectly formatted. + * @return The issuer. */ - public void setSubject(byte[] name) throws IOException + public String getIssuerAsString() { - if (name != null) - { - try - { - subject = new X500Principal(name); - } - catch (IllegalArgumentException iae) - { - throw new IOException(iae.getMessage()); - } - } + if (issuer != null) + return issuer.getName(); else - subject = null; + return null; } /** - * Returns the subject key identifier criterion, or <code>null</code> if - * this value was not set. Note that the byte array is cloned to prevent - * modification. + * Returns the public key usage criterion, or <code>null</code> if this + * value is not set. Note that the array is cloned to prevent modification. * - * @return The subject key identifier. + * @return The public key usage. */ - public byte[] getSubjectKeyIdentifier() + public boolean[] getKeyUsage() { - if (subjectKeyId != null) - return (byte[]) subjectKeyId.clone(); + if (keyUsage != null) + return (boolean[]) keyUsage.clone(); else return null; } /** - * Sets the subject key identifier criterion, or <code>null</code> to clear - * this criterion. Note that the byte array is cloned to prevent modification. + * Returns whether or not all specified alternative names must match. + * If false, a certificate is considered a match if <em>one</em> of the + * specified alternative names matches. * - * @param subjectKeyId The subject key identifier. + * @return true if all names must match. */ - public void setSubjectKeyIdentifier(byte[] subjectKeyId) + public boolean getMatchAllSubjectAltNames() { - this.subjectKeyId = subjectKeyId != null ? (byte[]) subjectKeyId.clone() : - null; + return matchAllNames; } /** - * Returns the authority key identifier criterion, or <code>null</code> if - * this value was not set. Note that the byte array is cloned to prevent + * Returns the name constraints criterion, or <code>null</code> if this + * value is not set. Note that the byte array is cloned to prevent * modification. * - * @return The authority key identifier. + * @return The name constraints. */ - public byte[] getAuthorityKeyIdentifier() + public byte[] getNameConstraints() { - if (authKeyId != null) - return (byte[]) authKeyId.clone(); + if (nameConstraints != null) + return (byte[]) nameConstraints.clone(); else return null; } - /** - * Sets the authority key identifier criterion, or <code>null</code> to clear - * this criterion. Note that the byte array is cloned to prevent modification. - * - * @param authKeyId The authority key identifier. - */ - public void setAuthorityKeyIdentifier(byte[] authKeyId) + public Collection<List<?>> getPathToNames() { - this.authKeyId = authKeyId != null ? (byte[]) authKeyId.clone() : null; - } - - /** - * Returns the date at which certificates must be valid, or <code>null</code> - * if this criterion was not set. - * - * @return The target certificate valitity date. - */ - public Date getCertificateValid() - { - if (certValid != null) - return (Date) certValid.clone(); - else - return null; + if (pathToNames != null) + { + List<List<?>> names = new ArrayList<List<?>>(pathToNames.size()); + for (GeneralName name : pathToNames) + { + List<Object> n = new ArrayList<Object>(2); + n.add(name.kind().tag()); + n.add(name.name()); + names.add(n); + } + + return names; + } + return null; } /** - * Sets the date at which certificates must be valid. Specify - * <code>null</code> to clear this criterion. + * Returns the certificate policy extension that will be matched by this + * selector, or null if the certificate policy will not be matched. * - * @param certValid The certificate validity date. + * @return The policy to be matched, or null. */ - public void setCertificateValid(Date certValid) + public Set<String> getPolicy() { - this.certValid = certValid != null ? (Date) certValid.clone() : null; + Set<OID> p = this.policy; + if (p != null) + { + Set<String> strings = new HashSet<String>(p.size()); + for (OID o : p) + { + strings.add(o.toString()); + } + return strings; + } + return null; } /** @@ -418,59 +461,83 @@ public class X509CertSelector implements CertSelector, Cloneable } /** - * This method, and its related X.509 certificate extension — the - * private key usage period — is not supported under the Internet - * PKI for X.509 certificates (PKIX), described in RFC 3280. As such, this - * method is not supported either. + * Returns the serial number criterion, or <code>null</code> if this + * value was not set. * - * <p>Do not use this method. It is not deprecated, as it is not deprecated - * in the Java standard, but it is basically a no-operation. + * @return The serial number. + */ + public BigInteger getSerialNumber() + { + return serialNo; + } + + /** + * Get the subject alternative names criterion. The collection returned + * is a collection of pairs: the first element is an {@link Integer} + * containing the name type, and the second is a byte array containing + * the DER-encoded name bytes. * - * @param UNUSED Is silently ignored. + * @return The subject alternative names criterion. Returns null if this + * criterion is not set. */ - public void setPrivateKeyValid(Date UNUSED) + public Collection<List<?>> getSubjectAlternativeNames() { + if (altNames != null) + { + List<List<?>> names = new ArrayList<List<?>>(altNames.size()); + for (GeneralName name : altNames) + { + List<Object> n = new ArrayList<Object>(2); + n.add(name.kind().tag()); + n.add(name.name()); + names.add(n); + } + return names; + } + return null; } /** - * Returns the public key algorithm ID that matching certificates must have, - * or <code>null</code> if this criterion was not set. + * Returns the subject criterion as a sequence of DER bytes, or + * <code>null</code> if this value is not set. * - * @return The public key algorithm ID. + * @return The subject. */ - public String getSubjectPublicKeyAlgID() + public byte[] getSubjectAsBytes() throws IOException { - return String.valueOf(sigId); + if (subject != null) + return subject.getEncoded(); + else + return null; } /** - * Sets the public key algorithm ID that matching certificates must have. - * Specify <code>null</code> to clear this criterion. + * Returns the subject criterion as a string, of <code>null</code> if + * this value was not set. * - * @param sigId The public key ID. - * @throws IOException If the specified ID is not a valid object identifier. + * @return The subject. */ - public void setSubjectPublicKeyAlgID(String sigId) throws IOException + public String getSubjectAsString() { - if (sigId != null) - { - try - { - OID oid = new OID(sigId); - int[] comp = oid.getIDs(); - if (!checkOid(comp)) - throw new IOException("malformed OID: " + sigId); - this.sigId = oid; - } - catch (IllegalArgumentException iae) - { - IOException ioe = new IOException("malformed OID: " + sigId); - ioe.initCause(iae); - throw ioe; - } - } + if (subject != null) + return subject.getName(); else - this.sigId = null; + return null; + } + + /** + * Returns the subject key identifier criterion, or <code>null</code> if + * this value was not set. Note that the byte array is cloned to prevent + * modification. + * + * @return The subject key identifier. + */ + public byte[] getSubjectKeyIdentifier() + { + if (subjectKeyId != null) + return (byte[]) subjectKeyId.clone(); + else + return null; } /** @@ -485,101 +552,282 @@ public class X509CertSelector implements CertSelector, Cloneable } /** - * Sets the subject public key criterion as an opaque representation. - * Specify <code>null</code> to clear this criterion. + * Returns the public key algorithm ID that matching certificates must have, + * or <code>null</code> if this criterion was not set. * - * @param key The public key. + * @return The public key algorithm ID. */ - public void setSubjectPublicKey(PublicKey key) + public String getSubjectPublicKeyAlgID() { - this.subjectKey = key; - if (key == null) - { - subjectKeySpec = null; - return; - } - try - { - KeyFactory enc = KeyFactory.getInstance("X.509"); - subjectKeySpec = (X509EncodedKeySpec) - enc.getKeySpec(key, X509EncodedKeySpec.class); - } - catch (Exception x) - { - subjectKey = null; - subjectKeySpec = null; - } + return String.valueOf(sigId); } /** - * Sets the subject public key criterion as a DER-encoded key. Specify - * <code>null</code> to clear this value. + * Match a certificate. This method will check the given certificate + * against all the enabled criteria of this selector, and will return + * <code>true</code> if the given certificate matches. * - * @param key The DER-encoded key bytes. - * @throws IOException If the argument is not a valid DER-encoded key. + * @param certificate The certificate to check. + * @return true if the certificate matches all criteria. */ - public void setSubjectPublicKey(byte[] key) throws IOException + public boolean match(Certificate certificate) { - if (key == null) + if (!(certificate instanceof X509Certificate)) + return false; + X509Certificate cert = (X509Certificate) certificate; + if (this.cert != null) { - subjectKey = null; - subjectKeySpec = null; - return; + try + { + byte[] e1 = this.cert.getEncoded(); + byte[] e2 = cert.getEncoded(); + if (!Arrays.equals(e1, e2)) + return false; + } + catch (CertificateEncodingException cee) + { + return false; + } } - try + if (serialNo != null) { - subjectKeySpec = new X509EncodedKeySpec(key); - KeyFactory enc = KeyFactory.getInstance("X.509"); - subjectKey = enc.generatePublic(subjectKeySpec); + if (!serialNo.equals(cert.getSerialNumber())) + return false; } - catch (Exception x) + if (certValid != null) { - subjectKey = null; - subjectKeySpec = null; - IOException ioe = new IOException(x.getMessage()); - ioe.initCause(x); - throw ioe; + try + { + cert.checkValidity(certValid); + } + catch (CertificateException ce) + { + return false; + } } + if (issuer != null) + { + if (!issuer.equals(cert.getIssuerX500Principal())) + return false; + } + if (subject != null) + { + if (!subject.equals(cert.getSubjectX500Principal())) + return false; + } + if (sigId != null) + { + if (!sigId.toString().equals(cert.getSigAlgOID())) + return false; + } + if (subjectKeyId != null) + { + byte[] b = cert.getExtensionValue(SUBJECT_KEY_ID); + if (!Arrays.equals(b, subjectKeyId)) + return false; + } + if (authKeyId != null) + { + byte[] b = cert.getExtensionValue(AUTH_KEY_ID); + if (!Arrays.equals(b, authKeyId)) + return false; + } + if (keyUsage != null) + { + boolean[] b = cert.getKeyUsage(); + if (!Arrays.equals(b, keyUsage)) + return false; + } + if (basicConstraints >= 0) + { + if (cert.getBasicConstraints() != basicConstraints) + return false; + } + if (keyPurposeSet != null) + { + List kp = null; + try + { + kp = cert.getExtendedKeyUsage(); + } + catch (CertificateParsingException cpe) + { + return false; + } + if (kp == null) + return false; + for (Iterator it = keyPurposeSet.iterator(); it.hasNext(); ) + { + if (!kp.contains(it.next())) + return false; + } + } + if (altNames != null) + { + Collection<List<?>> an = null; + try + { + an = cert.getSubjectAlternativeNames(); + } + catch (CertificateParsingException cpe) + { + return false; + } + if (an == null) + return false; + int match = 0; + for (GeneralName name : altNames) + { + for (List<?> list : an) + { + try + { + Integer id = (Integer) list.get(0); + Object val = list.get(1); + GeneralName n = null; + if (val instanceof String) + n = makeName(id, (String) val); + else if (val instanceof byte[]) + { + n = new GeneralName(GeneralName.Kind.forTag(id), + (byte[]) val); + } + else + continue; + if (name.equals(n)) + match++; + } + catch (Exception e) + { + continue; + } + } + if (match == 0 || (matchAllNames && match < altNames.size())) + return false; + } + } + if (nameConstraints != null) + { + byte[] nc = cert.getExtensionValue(NAME_CONSTRAINTS_ID); + if (!Arrays.equals(nameConstraints, nc)) + return false; + } + + if (policy != null) + { + CertificatePolicies policies = null; + if (cert instanceof GnuPKIExtension) + { + policies = (CertificatePolicies) + ((GnuPKIExtension) cert).getExtension(CertificatePolicies.ID).getValue(); + } + else + { + byte[] policiesDer = + cert.getExtensionValue(CertificatePolicies.ID.toString()); + try + { + policies = new CertificatePolicies(policiesDer); + } + catch (IOException ioe) + { + // ignored + } + } + + if (policies == null) + return false; + if (!policies.getPolicies().containsAll(policy)) + return false; + } + + if (pathToNames != null) + { + NameConstraints nc = null; + if (cert instanceof GnuPKIExtension) + { + Extension e = + ((GnuPKIExtension) cert).getExtension(NameConstraints.ID); + if (e != null) + nc = (NameConstraints) e.getValue(); + } + else + { + byte[] b = cert.getExtensionValue(NameConstraints.ID.toString()); + if (b != null) + { + try + { + nc = new NameConstraints(b); + } + catch (IOException ioe) + { + } + } + } + + if (nc == null) + return false; + + int match = 0; + for (GeneralName name : pathToNames) + { + for (GeneralSubtree subtree : nc.permittedSubtrees()) + { + if (name.equals(subtree.base())) + match++; + } + } + if (match == 0 || (matchAllNames && match < pathToNames.size())) + return false; + } + + return true; } /** - * Returns the public key usage criterion, or <code>null</code> if this - * value is not set. Note that the array is cloned to prevent modification. + * Sets the authority key identifier criterion, or <code>null</code> to clear + * this criterion. Note that the byte array is cloned to prevent modification. * - * @return The public key usage. + * @param authKeyId The authority key identifier. */ - public boolean[] getKeyUsage() + public void setAuthorityKeyIdentifier(byte[] authKeyId) { - if (keyUsage != null) - return (boolean[]) keyUsage.clone(); - else - return null; + this.authKeyId = authKeyId != null ? (byte[]) authKeyId.clone() : null; } /** - * Sets the public key usage criterion. Specify <code>null</code> to clear - * this value. + * Sets the basic constraints criterion. Specify -1 to clear this parameter. * - * @param keyUsage The public key usage. + * @param basicConstraints The new basic constraints value. */ - public void setKeyUsage(boolean[] keyUsage) + public void setBasicConstraints(int basicConstraints) { - this.keyUsage = keyUsage != null ? (boolean[]) keyUsage.clone() : null; + if (basicConstraints < -1) + basicConstraints = -1; + this.basicConstraints = basicConstraints; } /** - * Returns the set of extended key purpose IDs, as an unmodifiable set - * of OID strings. Returns <code>null</code> if this criterion is not - * set. + * Sets the certificate criterion. If set, only certificates that are + * equal to the certificate passed here will be accepted. * - * @return The set of key purpose OIDs (strings). + * @param cert The certificate. */ - public Set<String> getExtendedKeyUsage() + public void setCertificate(X509Certificate cert) { - if (keyPurposeSet != null) - return Collections.unmodifiableSet(keyPurposeSet); - else - return null; + this.cert = cert; + } + + /** + * Sets the date at which certificates must be valid. Specify + * <code>null</code> to clear this criterion. + * + * @param certValid The certificate validity date. + */ + public void setCertificateValid(Date certValid) + { + this.certValid = certValid != null ? (Date) certValid.clone() : null; } /** @@ -596,7 +844,7 @@ public class X509CertSelector implements CertSelector, Cloneable this.keyPurposeSet = null; return; } - Set s = new HashSet(); + Set<String> s = new HashSet<String>(); for (Iterator it = keyPurposeSet.iterator(); it.hasNext(); ) { Object o = it.next(); @@ -620,119 +868,77 @@ public class X509CertSelector implements CertSelector, Cloneable } /** - * Returns whether or not all specified alternative names must match. - * If false, a certificate is considered a match if <em>one</em> of the - * specified alternative names matches. - * - * @return true if all names must match. - */ - public boolean getMatchAllSubjectAltNames() - { - return matchAllNames; - } - - /** - * Sets whether or not all subject alternative names must be matched. - * If false, then a certificate will be considered a match if one - * alternative name matches. - * - * @param matchAllNames Whether or not all alternative names must be - * matched. - */ - public void setMatchAllSubjectAltNames(boolean matchAllNames) - { - this.matchAllNames = matchAllNames; - } - - /** - * Sets the subject alternative names critertion. Each element of the - * argument must be a {@link java.util.List} that contains exactly two - * elements: the first an {@link Integer}, representing the type of - * name, and the second either a {@link String} or a byte array, - * representing the name itself. + * Sets the issuer, specified as the DER encoding of the issuer's + * distinguished name. Only certificates issued by this issuer will + * be accepted. * - * @param altNames The alternative names. - * @throws IOException If any element of the argument is invalid. + * @param name The DER encoding of the issuer's distinguished name. + * @throws IOException If the given name is incorrectly formatted. */ - public void setSubjectAlternativeNames(Collection<List<?>> altNames) - throws IOException + public void setIssuer(byte[] name) throws IOException { - if (altNames == null) - { - this.altNames = null; - return; - } - List l = new ArrayList(altNames.size()); - for (Iterator it = altNames.iterator(); it.hasNext(); ) + if (name != null) { - Object o = it.next(); - if (!(o instanceof List) || ((List) o).size() != 2 || - !(((List) o).get(0) instanceof Integer) || - !(((List) o).get(1) instanceof String) || - !(((List) o).get(1) instanceof byte[])) - throw new IOException("illegal alternative name: " + o); - Integer i = (Integer) ((List) o).get(0); - if (i.intValue() < 0 || i.intValue() > 8) - throw new IOException("illegal alternative name: " + o + - ", bad id: " + i); - l.add(new ArrayList((List) o)); + try + { + issuer = new X500Principal(name); + } + catch (IllegalArgumentException iae) + { + throw new IOException(iae.getMessage()); + } } - this.altNames = l; + else + issuer = null; } /** - * Add a name to the subject alternative names criterion. + * Sets the issuer, specified as a string representation of the issuer's + * distinguished name. Only certificates issued by this issuer will + * be accepted. * - * @param id The type of name this is. Must be in the range [0,8]. - * @param name The name. - * @throws IOException If the id is out of range, or if the name - * is null. + * @param name The string representation of the issuer's distinguished name. + * @throws IOException If the given name is incorrectly formatted. */ - public void addSubjectAlternativeName(int id, String name) - throws IOException + public void setIssuer(String name) throws IOException { - if (id < 0 || id > 8 || name == null) - throw new IOException("illegal alternative name"); - if (altNames == null) - altNames = new LinkedList(); - ArrayList l = new ArrayList(2); - l.add(Integer.valueOf(id)); - l.add(name); - altNames.add(l); + if (name != null) + { + try + { + issuer = new X500Principal(name); + } + catch (IllegalArgumentException iae) + { + throw new IOException(iae.getMessage()); + } + } + else + issuer = null; } /** - * Add a name, as DER-encoded bytes, to the subject alternative names - * criterion. + * Sets the public key usage criterion. Specify <code>null</code> to clear + * this value. * - * @param id The type of name this is. + * @param keyUsage The public key usage. */ - public void addSubjectAlternativeName(int id, byte[] name) - throws IOException + public void setKeyUsage(boolean[] keyUsage) { - if (id < 0 || id > 8 || name == null) - throw new IOException("illegal alternative name"); - if (altNames == null) - altNames = new LinkedList(); - ArrayList l = new ArrayList(2); - l.add(Integer.valueOf(id)); - l.add(name); - altNames.add(l); + this.keyUsage = keyUsage != null ? (boolean[]) keyUsage.clone() : null; } /** - * Returns the name constraints criterion, or <code>null</code> if this - * value is not set. Note that the byte array is cloned to prevent - * modification. + * Sets whether or not all subject alternative names must be matched. + * If false, then a certificate will be considered a match if one + * alternative name matches. * - * @return The name constraints. + * @param matchAllNames Whether or not all alternative names must be + * matched. */ - public byte[] getNameConstraints() + public void setMatchAllSubjectAltNames(boolean matchAllNames) { - if (nameConstraints != null) - return (byte[]) nameConstraints.clone(); - else - return null; + this.matchAllNames = matchAllNames; } /** @@ -747,280 +953,302 @@ public class X509CertSelector implements CertSelector, Cloneable public void setNameConstraints(byte[] nameConstraints) throws IOException { - // FIXME check if the argument is valid. + // Check if the input is well-formed... + new NameConstraints(nameConstraints); + + // But we just compare raw byte arrays. this.nameConstraints = nameConstraints != null ? (byte[]) nameConstraints.clone() : null; } + + /** + * Sets the pathToNames criterion. The argument is a collection of + * pairs, the first element of which is an {@link Integer} giving + * the ID of the name, and the second element is either a {@link String} + * or a byte array. + * + * See {@link #addPathToName(int, byte[])} and {@link #addPathToName(int, String)} + * for how these arguments are handled. + * + * @param names The names. + * @throws IOException If any argument is malformed. + */ + public void setPathToNames(Collection<List<?>> names) throws IOException + { + if (names == null || names.size() == 0) + { + pathToNames = null; + } + else + { + pathToNames = new ArrayList<GeneralName>(names.size()); + for (List<?> name : names) + { + Integer id = (Integer) name.get(0); + Object name2 = name.get(1); + if (name2 instanceof String) + addPathToName(id, (String) name2); + else if (name2 instanceof byte[]) + addPathToName(id, (byte[]) name2); + else + throw new IOException("invalid name type: " + + name2.getClass().getName()); + } + } + } /** - * Returns the basic constraints criterion, or -1 if this value is not set. + * Sets the certificate policy to match, or null if this criterion should + * not be checked. Each element if the set must be a dotted-decimal form + * of certificate policy object identifier. * - * @return The basic constraints. + * @param policy The policy to match. + * @throws IOException If some element of the policy is not a valid + * policy extenison OID. */ - public int getBasicConstraints() + public void setPolicy(Set<String> policy) throws IOException { - return basicConstraints; + if (policy != null) + { + HashSet<OID> p = new HashSet<OID>(policy.size()); + for (String s : policy) + { + try + { + OID oid = new OID(s); + int[] i = oid.getIDs(); + if (!checkOid(i)) + throw new IOException("invalid OID"); + p.add(oid); + } + catch (IOException ioe) + { + throw ioe; + } + catch (Exception x) + { + IOException ioe = new IOException("invalid OID"); + ioe.initCause(x); + throw ioe; + } + } + this.policy = p; + } + else + this.policy = null; } /** - * Sets the basic constraints criterion. Specify -1 to clear this parameter. + * This method, and its related X.509 certificate extension — the + * private key usage period — is not supported under the Internet + * PKI for X.509 certificates (PKIX), described in RFC 3280. As such, this + * method is not supported either. * - * @param basicConstraints The new basic constraints value. + * <p>Do not use this method. It is not deprecated, as it is not deprecated + * in the Java standard, but it is basically a no-operation. + * + * @param UNUSED Is silently ignored. */ - public void setBasicConstraints(int basicConstraints) + public void setPrivateKeyValid(Date UNUSED) { - if (basicConstraints < -1) - basicConstraints = -1; - this.basicConstraints = basicConstraints; } - // The last two criteria not yet implemented are certificate policies - // and path-to-names. Both of these are somewhat advanced extensions - // (you could probably count the applications that actually use them - // on one hand), and they both have no support in the X509Certificate - // class. - // - // Not having support in X509Certificate is not always a problem; for - // example, we can compare DER-encoded values as byte arrays for some - // extensions. We can't, however, compare them if they are specified - // in a set (as policies are). We need to parse the actual value in the - // certificate, and check it against the specified set. - - // FIXME -// public void setPolicy(Set<String> policy) throws IOException -// { -// if (policy != null) -// { -// for (Iterator it = policy.iterator(); it.hasNext(); ) -// try -// { -// OID oid = new OID((String) it.next()); -// int[] i = oid.getIDs(); -// if (!checkOid(i)) -// throw new IOException("invalid OID"); -// } -// catch (Exception x) -// { -// throw new IOException("invalid OID"); -// } -// } -// this.policy = policy != null ? new HashSet(policy) : null; -// } - - // FIXME -// public void setPathToNames(Collection<List<?>> names) throws IOException -// { -// if (names == null) -// { -// this.names = null; -// return; -// } -// for (Iterator it = names.iterator(); it.hasNext(); ) -// { -// try -// { -// List l = (List) it.next(); -// if (l.get(1) instanceof String) -// addPathToName(((Integer)l.get(0)).intValue(), (String)l.get(1)); -// else -// addPathToName(((Integer)l.get(0)).intValue(), (byte[])l.get(1)); -// } -// catch (Exception x) -// { -// this.names = null; -// throw new IOException("invalid names"); -// } -// } -// } - - // FIXME -// public void addPathToName(int id, String name) throws IOException -// { -// } - - // FIXME -// public void addPathToName(int id, byte[] name) throws IOException -// { -// } - - // FIXME -// public Collection<List<?>> getSubjectAlternativeNames() -// { -// return null; -// } - - // FIXME -// public Set<String> getPolicy() -// { -// return null; -// } - - // FIXME -// public Collection<List<?>> getPathToNames() -// { -// return null; -// } + /** + * Sets the serial number of the desired certificate. Only certificates that + * contain this serial number are accepted. + * + * @param serialNo The serial number. + */ + public void setSerialNumber(BigInteger serialNo) + { + this.serialNo = serialNo; + } /** - * Match a certificate. This method will check the given certificate - * against all the enabled criteria of this selector, and will return - * <code>true</code> if the given certificate matches. + * Sets the subject, specified as the DER encoding of the subject's + * distinguished name. Only certificates with the given subject will + * be accepted. * - * @param certificate The certificate to check. - * @return true if the certificate matches all criteria. + * @param name The DER encoding of the subject's distinguished name. + * @throws IOException If the given name is incorrectly formatted. */ - public boolean match(Certificate certificate) + public void setSubject(byte[] name) throws IOException { - if (!(certificate instanceof X509Certificate)) - return false; - X509Certificate cert = (X509Certificate) certificate; - if (this.cert != null) + if (name != null) { try { - byte[] e1 = this.cert.getEncoded(); - byte[] e2 = cert.getEncoded(); - if (!Arrays.equals(e1, e2)) - return false; + subject = new X500Principal(name); } - catch (CertificateEncodingException cee) + catch (IllegalArgumentException iae) { - return false; + throw new IOException(iae.getMessage()); } } - if (serialNo != null) - { - if (!serialNo.equals(cert.getSerialNumber())) - return false; - } - if (certValid != null) + else + subject = null; + } + + /** + * Sets the subject, specified as a string representation of the + * subject's distinguished name. Only certificates with the given + * subject will be accepted. + * + * @param name The string representation of the subject's distinguished name. + * @throws IOException If the given name is incorrectly formatted. + */ + public void setSubject(String name) throws IOException + { + if (name != null) { try { - cert.checkValidity(certValid); + subject = new X500Principal(name); } - catch (CertificateException ce) + catch (IllegalArgumentException iae) { - return false; + throw new IOException(iae.getMessage()); } } - if (issuer != null) + else + subject = null; + } + + /** + * Sets the subject alternative names critertion. Each element of the + * argument must be a {@link java.util.List} that contains exactly two + * elements: the first an {@link Integer}, representing the type of + * name, and the second either a {@link String} or a byte array, + * representing the name itself. + * + * @param altNames The alternative names. + * @throws IOException If any element of the argument is invalid. + */ + public void setSubjectAlternativeNames(Collection<List<?>> altNames) + throws IOException + { + if (altNames == null || altNames.isEmpty()) { - if (!issuer.equals(cert.getIssuerX500Principal())) - return false; + this.altNames = null; + return; } - if (subject != null) + List<GeneralName> l = new ArrayList<GeneralName>(altNames.size()); + for (List<?> list : altNames) { - if (!subject.equals(cert.getSubjectX500Principal())) - return false; + Integer id = (Integer) list.get(0); + Object value = list.get(1); + GeneralName name = null; + if (value instanceof String) + name = makeName(id, (String) value); + else if (value instanceof byte[]) + name = new GeneralName(GeneralName.Kind.forTag(id), (byte[]) value); + else + throw new IOException("invalid name type: " + value.getClass().getName()); + l.add(name); } - if (sigId != null) + this.altNames = l; + } + + /** + * Sets the subject key identifier criterion, or <code>null</code> to clear + * this criterion. Note that the byte array is cloned to prevent modification. + * + * @param subjectKeyId The subject key identifier. + */ + public void setSubjectKeyIdentifier(byte[] subjectKeyId) + { + this.subjectKeyId = subjectKeyId != null ? (byte[]) subjectKeyId.clone() : + null; + } + + /** + * Sets the subject public key criterion as a DER-encoded key. Specify + * <code>null</code> to clear this value. + * + * @param key The DER-encoded key bytes. + * @throws IOException If the argument is not a valid DER-encoded key. + */ + public void setSubjectPublicKey(byte[] key) throws IOException + { + if (key == null) { - if (!sigId.toString().equals(cert.getSigAlgOID())) - return false; + subjectKey = null; + subjectKeySpec = null; + return; } - if (subjectKeyId != null) + try { - byte[] b = cert.getExtensionValue(SUBJECT_KEY_ID); - if (!Arrays.equals(b, subjectKeyId)) - return false; + subjectKeySpec = new X509EncodedKeySpec(key); + KeyFactory enc = KeyFactory.getInstance("X.509"); + subjectKey = enc.generatePublic(subjectKeySpec); } - if (authKeyId != null) + catch (Exception x) { - byte[] b = cert.getExtensionValue(AUTH_KEY_ID); - if (!Arrays.equals(b, authKeyId)) - return false; + subjectKey = null; + subjectKeySpec = null; + IOException ioe = new IOException(x.getMessage()); + ioe.initCause(x); + throw ioe; } - if (keyUsage != null) + } + + /** + * Sets the subject public key criterion as an opaque representation. + * Specify <code>null</code> to clear this criterion. + * + * @param key The public key. + */ + public void setSubjectPublicKey(PublicKey key) + { + this.subjectKey = key; + if (key == null) { - boolean[] b = cert.getKeyUsage(); - if (!Arrays.equals(b, keyUsage)) - return false; + subjectKeySpec = null; + return; } - if (basicConstraints >= 0) + try { - if (cert.getBasicConstraints() != basicConstraints) - return false; + KeyFactory enc = KeyFactory.getInstance("X.509"); + subjectKeySpec = (X509EncodedKeySpec) + enc.getKeySpec(key, X509EncodedKeySpec.class); } - if (keyPurposeSet != null) + catch (Exception x) { - List kp = null; - try - { - kp = cert.getExtendedKeyUsage(); - } - catch (CertificateParsingException cpe) - { - return false; - } - if (kp == null) - return false; - for (Iterator it = keyPurposeSet.iterator(); it.hasNext(); ) - { - if (!kp.contains(it.next())) - return false; - } + subjectKey = null; + subjectKeySpec = null; } - if (altNames != null) + } + + /** + * Sets the public key algorithm ID that matching certificates must have. + * Specify <code>null</code> to clear this criterion. + * + * @param sigId The public key ID. + * @throws IOException If the specified ID is not a valid object identifier. + */ + public void setSubjectPublicKeyAlgID(String sigId) throws IOException + { + if (sigId != null) { - Collection an = null; try { - an = cert.getSubjectAlternativeNames(); - } - catch (CertificateParsingException cpe) - { - return false; + OID oid = new OID(sigId); + int[] comp = oid.getIDs(); + if (!checkOid(comp)) + throw new IOException("malformed OID: " + sigId); + this.sigId = oid; } - if (an == null) - return false; - int match = 0; - for (Iterator it = altNames.iterator(); it.hasNext(); ) + catch (IllegalArgumentException iae) { - List l = (List) it.next(); - Integer id = (Integer) l.get(0); - String s = null; - byte[] b = null; - if (l.get(1) instanceof String) - s = (String) l.get(1); - else if (l.get(1) instanceof byte[]) - b = (byte[]) l.get(1); - else - return false; - for (Iterator it2 = an.iterator(); it2.hasNext(); ) - { - Object o = it2.next(); - if (!(o instanceof List)) - continue; - List l2 = (List) o; - if (l2.size() != 2) - continue; - if (!id.equals(l2.get(0))) - continue; - if (s != null && (l2.get(1) instanceof String) && - s.equals(l2.get(1))) - match++; - else if (b != null && (l2.get(1) instanceof byte[]) && - Arrays.equals(b, (byte[]) l2.get(1))) - match++; - } - if (match == 0 || (matchAllNames && match != altNames.size())) - return false; + IOException ioe = new IOException("malformed OID: " + sigId); + ioe.initCause(iae); + throw ioe; } } - if (nameConstraints != null) - { - byte[] nc = cert.getExtensionValue(NAME_CONSTRAINTS_ID); - if (!Arrays.equals(nameConstraints, nc)) - return false; - } - - // FIXME check policies. - // FIXME check path-to-names. - - return true; + else + this.sigId = null; } - + public String toString() { StringBuffer str = new StringBuffer(X509CertSelector.class.getName()); @@ -1080,28 +1308,11 @@ public class X509CertSelector implements CertSelector, Cloneable str.append(" alternative names = ").append(altNames).append(eol); if (nameConstraints != null) str.append(" name constraints = <blob of data>").append(eol); + if (policy != null) + str.append(" policy = ").append(policy).append(eol); + if (pathToNames != null) + str.append(" pathToNames = ").append(pathToNames).append(eol); str.append("}").append(nl); return str.toString(); } - - public Object clone() - { - try - { - return super.clone(); - } - catch (CloneNotSupportedException shouldNotHappen) - { - throw new Error(shouldNotHappen); - } - } - - // Own methods. - // ------------------------------------------------------------------------- - - private static boolean checkOid(int[] oid) - { - return (oid != null && oid.length > 2 && - (oid[0] >= 0 && oid[0] <= 2) && (oid[1] >= 0 && oid[1] <= 39)); - } } diff --git a/libjava/classpath/java/security/cert/X509Certificate.java b/libjava/classpath/java/security/cert/X509Certificate.java index bc1b5c2351c..b398e093e6a 100644 --- a/libjava/classpath/java/security/cert/X509Certificate.java +++ b/libjava/classpath/java/security/cert/X509Certificate.java @@ -1,5 +1,5 @@ /* X509Certificate.java --- X.509 Certificate class - Copyright (C) 1999,2003 Free Software Foundation, Inc. + Copyright (C) 1999,2003, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -137,7 +137,7 @@ import java.util.List; * @author Casey Marshall (rsdio@metastatic.org) */ public abstract class X509Certificate - extends java.security.cert.Certificate // XXX workaround for gcj bug #17845 + extends Certificate implements X509Extension { private static final long serialVersionUID = -2491127588187038216L; |