jdk/test/javax/xml/crypto/dsig/X509KeySelector.java
changeset 2 90ce3da70b43
child 5506 202f599c92aa
equal deleted inserted replaced
0:fd16c54261b3 2:90ce3da70b43
       
     1 /*
       
     2  * Copyright 2005 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    20  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    21  * have any questions.
       
    22  */
       
    23 
       
    24 import java.io.InputStream;
       
    25 import java.io.IOException;
       
    26 import java.security.Key;
       
    27 import java.security.KeyStore;
       
    28 import java.security.KeyStoreException;
       
    29 import java.security.PublicKey;
       
    30 import java.security.cert.Certificate;
       
    31 import java.security.cert.CertificateFactory;
       
    32 import java.security.cert.CertSelector;
       
    33 import java.security.cert.X509Certificate;
       
    34 import java.security.cert.X509CertSelector;
       
    35 import java.util.*;
       
    36 import javax.security.auth.x500.X500Principal;
       
    37 import javax.xml.crypto.*;
       
    38 import javax.xml.crypto.dsig.*;
       
    39 import javax.xml.crypto.dom.*;
       
    40 import javax.xml.crypto.dsig.keyinfo.*;
       
    41 
       
    42 import org.jcp.xml.dsig.internal.dom.DOMRetrievalMethod;
       
    43 
       
    44 /**
       
    45  * A <code>KeySelector</code> that returns {@link PublicKey}s. If the
       
    46  * selector is created as trusted, it only returns public keys of trusted
       
    47  * {@link X509Certificate}s stored in a {@link KeyStore}. Otherwise, it
       
    48  * returns trusted or untrusted public keys (it doesn't care as long
       
    49  * as it finds one).
       
    50  *
       
    51  * <p>This <code>KeySelector</code> uses the specified <code>KeyStore</code>
       
    52  * to find a trusted <code>X509Certificate</code> that matches information
       
    53  * specified in the {@link KeyInfo} passed to the {@link #select} method.
       
    54  * The public key from the first match is returned. If no match,
       
    55  * <code>null</code> is returned. See the <code>select</code> method for more
       
    56  * information.
       
    57  *
       
    58  * @author Sean Mullan
       
    59  */
       
    60 class X509KeySelector extends KeySelector {
       
    61 
       
    62     private KeyStore ks;
       
    63     private boolean trusted = true;
       
    64 
       
    65     /**
       
    66      * Creates a trusted <code>X509KeySelector</code>.
       
    67      *
       
    68      * @param keyStore the keystore
       
    69      * @throws KeyStoreException if the keystore has not been initialized
       
    70      * @throws NullPointerException if <code>keyStore</code> is
       
    71      *    <code>null</code>
       
    72      */
       
    73     X509KeySelector(KeyStore keyStore) throws KeyStoreException {
       
    74         this(keyStore, true);
       
    75     }
       
    76 
       
    77     X509KeySelector(KeyStore keyStore, boolean trusted)
       
    78         throws KeyStoreException {
       
    79         if (keyStore == null) {
       
    80             throw new NullPointerException("keyStore is null");
       
    81         }
       
    82         this.trusted = trusted;
       
    83         this.ks = keyStore;
       
    84         // test to see if KeyStore has been initialized
       
    85         this.ks.size();
       
    86     }
       
    87 
       
    88     /**
       
    89      * Finds a key from the keystore satisfying the specified constraints.
       
    90      *
       
    91      * <p>This method compares data contained in {@link KeyInfo} entries
       
    92      * with information stored in the <code>KeyStore</code>. The implementation
       
    93      * iterates over the KeyInfo types and returns the first {@link PublicKey}
       
    94      * of an X509Certificate in the keystore that is compatible with the
       
    95      * specified AlgorithmMethod according to the following rules for each
       
    96      * keyinfo type:
       
    97      *
       
    98      * X509Data X509Certificate: if it contains a <code>KeyUsage</code>
       
    99      *   extension that asserts the <code>digitalSignature</code> bit and
       
   100      *   matches an <code>X509Certificate</code> in the <code>KeyStore</code>.
       
   101      * X509Data X509IssuerSerial: if the serial number and issuer DN match an
       
   102      *    <code>X509Certificate</code> in the <code>KeyStore</code>.
       
   103      * X509Data X509SubjectName: if the subject DN matches an
       
   104      *    <code>X509Certificate</code> in the <code>KeyStore</code>.
       
   105      * X509Data X509SKI: if the subject key identifier matches an
       
   106      *    <code>X509Certificate</code> in the <code>KeyStore</code>.
       
   107      * KeyName: if the keyname matches an alias in the <code>KeyStore</code>.
       
   108      * RetrievalMethod: supports rawX509Certificate and X509Data types. If
       
   109      *    rawX509Certificate type, it must match an <code>X509Certificate</code>
       
   110      *    in the <code>KeyStore</code>.
       
   111      *
       
   112      * @param keyInfo a <code>KeyInfo</code> (may be <code>null</code>)
       
   113      * @param purpose the key's purpose
       
   114      * @param method the algorithm method that this key is to be used for.
       
   115      *    Only keys that are compatible with the algorithm and meet the
       
   116      *    constraints of the specified algorithm should be returned.
       
   117      * @param an <code>XMLCryptoContext</code> that may contain additional
       
   118      *    useful information for finding an appropriate key
       
   119      * @return a key selector result
       
   120      * @throws KeySelectorException if an exceptional condition occurs while
       
   121      *    attempting to find a key. Note that an inability to find a key is not
       
   122      *    considered an exception (<code>null</code> should be
       
   123      *    returned in that case). However, an error condition (ex: network
       
   124      *    communications failure) that prevented the <code>KeySelector</code>
       
   125      *    from finding a potential key should be considered an exception.
       
   126      * @throws ClassCastException if the data type of <code>method</code>
       
   127      *    is not supported by this key selector
       
   128      */
       
   129     public KeySelectorResult select(KeyInfo keyInfo,
       
   130         KeySelector.Purpose purpose, AlgorithmMethod method,
       
   131         XMLCryptoContext context) throws KeySelectorException {
       
   132 
       
   133         SignatureMethod sm = (SignatureMethod) method;
       
   134 
       
   135         try {
       
   136             // return null if keyinfo is null or keystore is empty
       
   137             if (keyInfo == null || ks.size() == 0) {
       
   138                 return new SimpleKeySelectorResult(null);
       
   139             }
       
   140 
       
   141             // Iterate through KeyInfo types
       
   142             Iterator i = keyInfo.getContent().iterator();
       
   143             while (i.hasNext()) {
       
   144                 XMLStructure kiType = (XMLStructure) i.next();
       
   145                 // check X509Data
       
   146                 if (kiType instanceof X509Data) {
       
   147                     X509Data xd = (X509Data) kiType;
       
   148                     KeySelectorResult ksr = x509DataSelect(xd, sm);
       
   149                     if (ksr != null) {
       
   150                         return ksr;
       
   151                     }
       
   152                 // check KeyName
       
   153                 } else if (kiType instanceof KeyName) {
       
   154                     KeyName kn = (KeyName) kiType;
       
   155                     Certificate cert = ks.getCertificate(kn.getName());
       
   156                     if (cert != null && algEquals(sm.getAlgorithm(),
       
   157                         cert.getPublicKey().getAlgorithm())) {
       
   158                         return new SimpleKeySelectorResult(cert.getPublicKey());
       
   159                     }
       
   160                 // check RetrievalMethod
       
   161                 } else if (kiType instanceof RetrievalMethod) {
       
   162                     RetrievalMethod rm = (RetrievalMethod) kiType;
       
   163                     try {
       
   164                         KeySelectorResult ksr = null;
       
   165                         if (rm.getType().equals
       
   166                             (X509Data.RAW_X509_CERTIFICATE_TYPE)) {
       
   167                             OctetStreamData data = (OctetStreamData)
       
   168                                 rm.dereference(context);
       
   169                             CertificateFactory cf =
       
   170                                 CertificateFactory.getInstance("X.509");
       
   171                             X509Certificate cert = (X509Certificate)
       
   172                                 cf.generateCertificate(data.getOctetStream());
       
   173                             ksr = certSelect(cert, sm);
       
   174                         } else if (rm.getType().equals(X509Data.TYPE)) {
       
   175                             X509Data xd = (X509Data) ((DOMRetrievalMethod) rm).
       
   176                                 dereferenceAsXMLStructure(context);
       
   177                             ksr = x509DataSelect(xd, sm);
       
   178                         } else {
       
   179                             // skip; keyinfo type is not supported
       
   180                             continue;
       
   181                         }
       
   182                         if (ksr != null) {
       
   183                             return ksr;
       
   184                         }
       
   185                     } catch (Exception e) {
       
   186                         throw new KeySelectorException(e);
       
   187                     }
       
   188                 }
       
   189             }
       
   190         } catch (KeyStoreException kse) {
       
   191             // throw exception if keystore is uninitialized
       
   192             throw new KeySelectorException(kse);
       
   193         }
       
   194 
       
   195         // return null since no match could be found
       
   196         return new SimpleKeySelectorResult(null);
       
   197     }
       
   198 
       
   199     /**
       
   200      * Searches the specified keystore for a certificate that matches the
       
   201      * criteria specified in the CertSelector.
       
   202      *
       
   203      * @return a KeySelectorResult containing the cert's public key if there
       
   204      *   is a match; otherwise null
       
   205      */
       
   206     private KeySelectorResult keyStoreSelect(CertSelector cs)
       
   207         throws KeyStoreException {
       
   208         Enumeration aliases = ks.aliases();
       
   209         while (aliases.hasMoreElements()) {
       
   210             String alias = (String) aliases.nextElement();
       
   211             Certificate cert = ks.getCertificate(alias);
       
   212             if (cert != null && cs.match(cert)) {
       
   213                 return new SimpleKeySelectorResult(cert.getPublicKey());
       
   214             }
       
   215         }
       
   216         return null;
       
   217     }
       
   218 
       
   219     /**
       
   220      * Searches the specified keystore for a certificate that matches the
       
   221      * specified X509Certificate and contains a public key that is compatible
       
   222      * with the specified SignatureMethod.
       
   223      *
       
   224      * @return a KeySelectorResult containing the cert's public key if there
       
   225      *   is a match; otherwise null
       
   226      */
       
   227     private KeySelectorResult certSelect(X509Certificate xcert,
       
   228         SignatureMethod sm) throws KeyStoreException {
       
   229         // skip non-signer certs
       
   230         boolean[] keyUsage = xcert.getKeyUsage();
       
   231         if (keyUsage != null && keyUsage[0] == false) {
       
   232             return null;
       
   233         }
       
   234         String alias = ks.getCertificateAlias(xcert);
       
   235         if (alias != null) {
       
   236             PublicKey pk = ks.getCertificate(alias).getPublicKey();
       
   237             // make sure algorithm is compatible with method
       
   238             if (algEquals(sm.getAlgorithm(), pk.getAlgorithm())) {
       
   239                 return new SimpleKeySelectorResult(pk);
       
   240             }
       
   241         }
       
   242         return null;
       
   243     }
       
   244 
       
   245     /**
       
   246      * Returns an OID of a public-key algorithm compatible with the specified
       
   247      * signature algorithm URI.
       
   248      */
       
   249     private String getPKAlgorithmOID(String algURI) {
       
   250         if (algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1)) {
       
   251             return "1.2.840.10040.4.1";
       
   252         } else if (algURI.equalsIgnoreCase(SignatureMethod.RSA_SHA1)) {
       
   253             return "1.2.840.113549.1.1";
       
   254         } else {
       
   255             return null;
       
   256         }
       
   257     }
       
   258 
       
   259     /**
       
   260      * A simple KeySelectorResult containing a public key.
       
   261      */
       
   262     private static class SimpleKeySelectorResult implements KeySelectorResult {
       
   263         private final Key key;
       
   264         SimpleKeySelectorResult(Key key) { this.key = key; }
       
   265         public Key getKey() { return key; }
       
   266     }
       
   267 
       
   268     /**
       
   269      * Checks if a JCA/JCE public key algorithm name is compatible with
       
   270      * the specified signature algorithm URI.
       
   271      */
       
   272     //@@@FIXME: this should also work for key types other than DSA/RSA
       
   273     private boolean algEquals(String algURI, String algName) {
       
   274         if (algName.equalsIgnoreCase("DSA") &&
       
   275             algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1)) {
       
   276             return true;
       
   277         } else if (algName.equalsIgnoreCase("RSA") &&
       
   278             algURI.equalsIgnoreCase(SignatureMethod.RSA_SHA1)) {
       
   279             return true;
       
   280         } else {
       
   281             return false;
       
   282         }
       
   283     }
       
   284 
       
   285     /**
       
   286      * Searches the specified keystore for a certificate that matches an
       
   287      * entry of the specified X509Data and contains a public key that is
       
   288      * compatible with the specified SignatureMethod.
       
   289      *
       
   290      * @return a KeySelectorResult containing the cert's public key if there
       
   291      *   is a match; otherwise null
       
   292      */
       
   293     private KeySelectorResult x509DataSelect(X509Data xd, SignatureMethod sm)
       
   294         throws KeyStoreException, KeySelectorException {
       
   295 
       
   296         // convert signature algorithm to compatible public-key alg OID
       
   297         String algOID = getPKAlgorithmOID(sm.getAlgorithm());
       
   298         X509CertSelector subjectcs = new X509CertSelector();
       
   299         try {
       
   300             subjectcs.setSubjectPublicKeyAlgID(algOID);
       
   301         } catch (IOException ioe) {
       
   302             throw new KeySelectorException(ioe);
       
   303         }
       
   304         Collection certs = new ArrayList();
       
   305 
       
   306         Iterator xi = xd.getContent().iterator();
       
   307         while (xi.hasNext()) {
       
   308             Object o = xi.next();
       
   309             // check X509IssuerSerial
       
   310             if (o instanceof X509IssuerSerial) {
       
   311                 X509IssuerSerial xis = (X509IssuerSerial) o;
       
   312                 try {
       
   313                     subjectcs.setSerialNumber(xis.getSerialNumber());
       
   314                     String issuer = new X500Principal(xis.getIssuerName()).getName();
       
   315                     // strip off newline
       
   316                     if (issuer.endsWith("\n")) {
       
   317                         issuer = new String
       
   318                             (issuer.toCharArray(), 0, issuer.length()-1);
       
   319                     }
       
   320                     subjectcs.setIssuer(issuer);
       
   321                 } catch (IOException ioe) {
       
   322                     throw new KeySelectorException(ioe);
       
   323                 }
       
   324             // check X509SubjectName
       
   325             } else if (o instanceof String) {
       
   326                 String sn = (String) o;
       
   327                 try {
       
   328                     String subject = new X500Principal(sn).getName();
       
   329                     // strip off newline
       
   330                     if (subject.endsWith("\n")) {
       
   331                         subject = new String
       
   332                             (subject.toCharArray(), 0, subject.length()-1);
       
   333                     }
       
   334                     subjectcs.setSubject(subject);
       
   335                 } catch (IOException ioe) {
       
   336                     throw new KeySelectorException(ioe);
       
   337                 }
       
   338             // check X509SKI
       
   339             } else if (o instanceof byte[]) {
       
   340                 byte[] ski = (byte[]) o;
       
   341                 // DER-encode ski - required by X509CertSelector
       
   342                 byte[] encodedSki = new byte[ski.length+2];
       
   343                 encodedSki[0] = 0x04; // OCTET STRING tag value
       
   344                 encodedSki[1] = (byte) ski.length; // length
       
   345                 System.arraycopy(ski, 0, encodedSki, 2, ski.length);
       
   346                 subjectcs.setSubjectKeyIdentifier(encodedSki);
       
   347             } else if (o instanceof X509Certificate) {
       
   348                 certs.add((X509Certificate) o);
       
   349             // check X509CRL
       
   350             // not supported: should use CertPath API
       
   351             } else {
       
   352                 // skip all other entries
       
   353                 continue;
       
   354             }
       
   355         }
       
   356         KeySelectorResult ksr = keyStoreSelect(subjectcs);
       
   357         if (ksr != null) {
       
   358             return ksr;
       
   359         }
       
   360         if (!certs.isEmpty() && !trusted) {
       
   361             // try to find public key in certs in X509Data
       
   362             Iterator i = certs.iterator();
       
   363             while (i.hasNext()) {
       
   364                 X509Certificate cert = (X509Certificate) i.next();
       
   365                 if (subjectcs.match(cert)) {
       
   366                     return new SimpleKeySelectorResult(cert.getPublicKey());
       
   367                 }
       
   368             }
       
   369         }
       
   370         return null;
       
   371     }
       
   372 }