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