Path: blob/master/test/jdk/javax/xml/crypto/dsig/X509KeySelector.java
41152 views
/*1* Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.io.InputStream;24import java.io.IOException;25import java.security.Key;26import java.security.KeyStore;27import java.security.KeyStoreException;28import java.security.PublicKey;29import java.security.cert.Certificate;30import java.security.cert.CertificateFactory;31import java.security.cert.CertSelector;32import java.security.cert.X509Certificate;33import java.security.cert.X509CertSelector;34import java.util.*;35import javax.security.auth.x500.X500Principal;36import javax.xml.crypto.*;37import javax.xml.crypto.dsig.*;38import javax.xml.crypto.dom.*;39import javax.xml.crypto.dsig.keyinfo.*;4041import org.jcp.xml.dsig.internal.dom.DOMRetrievalMethod;4243/**44* A <code>KeySelector</code> that returns {@link PublicKey}s. If the45* selector is created as trusted, it only returns public keys of trusted46* {@link X509Certificate}s stored in a {@link KeyStore}. Otherwise, it47* returns trusted or untrusted public keys (it doesn't care as long48* as it finds one).49*50* <p>This <code>KeySelector</code> uses the specified <code>KeyStore</code>51* to find a trusted <code>X509Certificate</code> that matches information52* specified in the {@link KeyInfo} passed to the {@link #select} method.53* The public key from the first match is returned. If no match,54* <code>null</code> is returned. See the <code>select</code> method for more55* information.56*57* @author Sean Mullan58*/59class X509KeySelector extends KeySelector {6061private KeyStore ks;62private boolean trusted = true;6364/**65* Creates a trusted <code>X509KeySelector</code>.66*67* @param keyStore the keystore68* @throws KeyStoreException if the keystore has not been initialized69* @throws NullPointerException if <code>keyStore</code> is70* <code>null</code>71*/72X509KeySelector(KeyStore keyStore) throws KeyStoreException {73this(keyStore, true);74}7576X509KeySelector(KeyStore keyStore, boolean trusted)77throws KeyStoreException {78if (keyStore == null) {79throw new NullPointerException("keyStore is null");80}81this.trusted = trusted;82this.ks = keyStore;83// test to see if KeyStore has been initialized84this.ks.size();85}8687/**88* Finds a key from the keystore satisfying the specified constraints.89*90* <p>This method compares data contained in {@link KeyInfo} entries91* with information stored in the <code>KeyStore</code>. The implementation92* iterates over the KeyInfo types and returns the first {@link PublicKey}93* of an X509Certificate in the keystore that is compatible with the94* specified AlgorithmMethod according to the following rules for each95* keyinfo type:96*97* X509Data X509Certificate: if it contains a <code>KeyUsage</code>98* extension that asserts the <code>digitalSignature</code> bit and99* matches an <code>X509Certificate</code> in the <code>KeyStore</code>.100* X509Data X509IssuerSerial: if the serial number and issuer DN match an101* <code>X509Certificate</code> in the <code>KeyStore</code>.102* X509Data X509SubjectName: if the subject DN matches an103* <code>X509Certificate</code> in the <code>KeyStore</code>.104* X509Data X509SKI: if the subject key identifier matches an105* <code>X509Certificate</code> in the <code>KeyStore</code>.106* KeyName: if the keyname matches an alias in the <code>KeyStore</code>.107* RetrievalMethod: supports rawX509Certificate and X509Data types. If108* rawX509Certificate type, it must match an <code>X509Certificate</code>109* in the <code>KeyStore</code>.110*111* @param keyInfo a <code>KeyInfo</code> (may be <code>null</code>)112* @param purpose the key's purpose113* @param method the algorithm method that this key is to be used for.114* Only keys that are compatible with the algorithm and meet the115* constraints of the specified algorithm should be returned.116* @param an <code>XMLCryptoContext</code> that may contain additional117* useful information for finding an appropriate key118* @return a key selector result119* @throws KeySelectorException if an exceptional condition occurs while120* attempting to find a key. Note that an inability to find a key is not121* considered an exception (<code>null</code> should be122* returned in that case). However, an error condition (ex: network123* communications failure) that prevented the <code>KeySelector</code>124* from finding a potential key should be considered an exception.125* @throws ClassCastException if the data type of <code>method</code>126* is not supported by this key selector127*/128public KeySelectorResult select(KeyInfo keyInfo,129KeySelector.Purpose purpose, AlgorithmMethod method,130XMLCryptoContext context) throws KeySelectorException {131132SignatureMethod sm = (SignatureMethod) method;133134try {135// return null if keyinfo is null or keystore is empty136if (keyInfo == null || ks.size() == 0) {137return new SimpleKeySelectorResult(null);138}139140// Iterate through KeyInfo types141for (XMLStructure kiType : keyInfo.getContent()) {142// check X509Data143if (kiType instanceof X509Data) {144X509Data xd = (X509Data) kiType;145KeySelectorResult ksr = x509DataSelect(xd, sm);146if (ksr != null) {147return ksr;148}149// check KeyName150} else if (kiType instanceof KeyName) {151KeyName kn = (KeyName) kiType;152Certificate cert = ks.getCertificate(kn.getName());153if (cert != null && algEquals(sm.getAlgorithm(),154cert.getPublicKey().getAlgorithm())) {155return new SimpleKeySelectorResult(cert.getPublicKey());156}157// check RetrievalMethod158} else if (kiType instanceof RetrievalMethod) {159RetrievalMethod rm = (RetrievalMethod) kiType;160try {161KeySelectorResult ksr = null;162if (rm.getType().equals163(X509Data.RAW_X509_CERTIFICATE_TYPE)) {164OctetStreamData data = (OctetStreamData)165rm.dereference(context);166CertificateFactory cf =167CertificateFactory.getInstance("X.509");168X509Certificate cert = (X509Certificate)169cf.generateCertificate(data.getOctetStream());170ksr = certSelect(cert, sm);171} else if (rm.getType().equals(X509Data.TYPE)) {172X509Data xd = (X509Data) ((DOMRetrievalMethod) rm).173dereferenceAsXMLStructure(context);174ksr = x509DataSelect(xd, sm);175} else {176// skip; keyinfo type is not supported177continue;178}179if (ksr != null) {180return ksr;181}182} catch (Exception e) {183throw new KeySelectorException(e);184}185}186}187} catch (KeyStoreException kse) {188// throw exception if keystore is uninitialized189throw new KeySelectorException(kse);190}191192// return null since no match could be found193return new SimpleKeySelectorResult(null);194}195196/**197* Searches the specified keystore for a certificate that matches the198* criteria specified in the CertSelector.199*200* @return a KeySelectorResult containing the cert's public key if there201* is a match; otherwise null202*/203private KeySelectorResult keyStoreSelect(CertSelector cs)204throws KeyStoreException {205Enumeration<String> aliases = ks.aliases();206while (aliases.hasMoreElements()) {207String alias = aliases.nextElement();208Certificate cert = ks.getCertificate(alias);209if (cert != null && cs.match(cert)) {210return new SimpleKeySelectorResult(cert.getPublicKey());211}212}213return null;214}215216/**217* Searches the specified keystore for a certificate that matches the218* specified X509Certificate and contains a public key that is compatible219* with the specified SignatureMethod.220*221* @return a KeySelectorResult containing the cert's public key if there222* is a match; otherwise null223*/224private KeySelectorResult certSelect(X509Certificate xcert,225SignatureMethod sm) throws KeyStoreException {226// skip non-signer certs227boolean[] keyUsage = xcert.getKeyUsage();228if (keyUsage != null && keyUsage[0] == false) {229return null;230}231String alias = ks.getCertificateAlias(xcert);232if (alias != null) {233PublicKey pk = ks.getCertificate(alias).getPublicKey();234// make sure algorithm is compatible with method235if (algEquals(sm.getAlgorithm(), pk.getAlgorithm())) {236return new SimpleKeySelectorResult(pk);237}238}239return null;240}241242/**243* Returns an OID of a public-key algorithm compatible with the specified244* signature algorithm URI.245*/246private String getPKAlgorithmOID(String algURI) {247if (algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1)) {248return "1.2.840.10040.4.1";249} else if (algURI.equalsIgnoreCase(SignatureMethod.RSA_SHA1)) {250return "1.2.840.113549.1.1";251} else {252return null;253}254}255256/**257* A simple KeySelectorResult containing a public key.258*/259private static class SimpleKeySelectorResult implements KeySelectorResult {260private final Key key;261SimpleKeySelectorResult(Key key) { this.key = key; }262public Key getKey() { return key; }263}264265/**266* Checks if a JCA/JCE public key algorithm name is compatible with267* the specified signature algorithm URI.268*/269//@@@FIXME: this should also work for key types other than DSA/RSA270private boolean algEquals(String algURI, String algName) {271if (algName.equalsIgnoreCase("DSA") &&272algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1)) {273return true;274} else if (algName.equalsIgnoreCase("RSA") &&275algURI.equalsIgnoreCase(SignatureMethod.RSA_SHA1)) {276return true;277} else {278return false;279}280}281282/**283* Searches the specified keystore for a certificate that matches an284* entry of the specified X509Data and contains a public key that is285* compatible with the specified SignatureMethod.286*287* @return a KeySelectorResult containing the cert's public key if there288* is a match; otherwise null289*/290private KeySelectorResult x509DataSelect(X509Data xd, SignatureMethod sm)291throws KeyStoreException, KeySelectorException {292293// convert signature algorithm to compatible public-key alg OID294String algOID = getPKAlgorithmOID(sm.getAlgorithm());295X509CertSelector subjectcs = new X509CertSelector();296try {297subjectcs.setSubjectPublicKeyAlgID(algOID);298} catch (IOException ioe) {299throw new KeySelectorException(ioe);300}301Collection<X509Certificate> certs = new ArrayList<>();302303for (Object o : xd.getContent()) {304// check X509IssuerSerial305if (o instanceof X509IssuerSerial) {306X509IssuerSerial xis = (X509IssuerSerial) o;307try {308subjectcs.setSerialNumber(xis.getSerialNumber());309String issuer = new X500Principal(xis.getIssuerName()).getName();310// strip off newline311if (issuer.endsWith("\n")) {312issuer = new String313(issuer.toCharArray(), 0, issuer.length()-1);314}315subjectcs.setIssuer(issuer);316} catch (IOException ioe) {317throw new KeySelectorException(ioe);318}319// check X509SubjectName320} else if (o instanceof String) {321String sn = (String) o;322try {323String subject = new X500Principal(sn).getName();324// strip off newline325if (subject.endsWith("\n")) {326subject = new String327(subject.toCharArray(), 0, subject.length()-1);328}329subjectcs.setSubject(subject);330} catch (IOException ioe) {331throw new KeySelectorException(ioe);332}333// check X509SKI334} else if (o instanceof byte[]) {335byte[] ski = (byte[]) o;336// DER-encode ski - required by X509CertSelector337byte[] encodedSki = new byte[ski.length+2];338encodedSki[0] = 0x04; // OCTET STRING tag value339encodedSki[1] = (byte) ski.length; // length340System.arraycopy(ski, 0, encodedSki, 2, ski.length);341subjectcs.setSubjectKeyIdentifier(encodedSki);342} else if (o instanceof X509Certificate) {343certs.add((X509Certificate)o);344// check X509CRL345// not supported: should use CertPath API346} else {347// skip all other entries348continue;349}350}351KeySelectorResult ksr = keyStoreSelect(subjectcs);352if (ksr != null) {353return ksr;354}355if (!certs.isEmpty() && !trusted) {356// try to find public key in certs in X509Data357for (X509Certificate cert : certs) {358if (subjectcs.match(cert)) {359return new SimpleKeySelectorResult(cert.getPublicKey());360}361}362}363return null;364}365}366367368