
ro.kuberam.libs.java.crypto.toDo.X509KeySelector Maven / Gradle / Ivy
/**
* EXPath Cryptographic Module
* Java Library providing an EXPath Cryptographic Module
* Copyright (C) 2015 Kuberam
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package ro.kuberam.libs.java.crypto.toDo;
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.Enumeration;
import java.util.Iterator;
import javax.security.auth.x500.X500Principal;
import javax.xml.crypto.*;
import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dsig.keyinfo.*;
/**
* A KeySelector
that returns {@link PublicKey}s of trusted
* {@link X509Certificate}s stored in a {@link KeyStore}.
*
* This KeySelector
uses the specified KeyStore
* to find a trusted X509Certificate
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,
* null
is returned. See the select
method for more
* information.
*
* NOTE!: This X509KeySelector requires J2SE 1.4 because it uses the
* java.security.cert.X509CertSelector & javax.security.auth.x500.X500Principal
* classes to parse X.500 DNs and match on certificate attributes.
*
* @author Sean Mullan
*/
public class X509KeySelector extends KeySelector {
private KeyStore ks;
/**
* Creates an X509KeySelector
.
*
* @param keyStore the keystore
* @throws KeyStoreException if the keystore has not been initialized
* @throws NullPointerException if keyStore
is
* null
*/
public X509KeySelector(final KeyStore keyStore) throws KeyStoreException {
if (keyStore == null) {
throw new NullPointerException("keyStore is null");
}
this.ks = keyStore;
// test to see if KeyStore has been initialized
this.ks.size();
}
/**
* Finds a key from the keystore satisfying the specified constraints.
*
* This method compares data contained in {@link KeyInfo} entries
* with information stored in the KeyStore
. 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 KeyUsage
* extension that asserts the digitalSignature
bit and
* matches an X509Certificate
in the KeyStore
.
* X509Data X509IssuerSerial: if the serial number and issuer DN match an
* X509Certificate
in the KeyStore
.
* X509Data X509SubjectName: if the subject DN matches an
* X509Certificate
in the KeyStore
.
* X509Data X509SKI: if the subject key identifier matches an
* X509Certificate
in the KeyStore
.
* KeyName: if the keyname matches an alias in the KeyStore
.
* RetrievalMethod: supports rawX509Certificate and X509Data types. If
* rawX509Certificate type, it must match an X509Certificate
* in the KeyStore
.
*
* @param keyInfo a KeyInfo
(may be null
)
* @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 context XMLCryptoContext
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 (null
should be
* returned in that case). However, an error condition (ex: network
* communications failure) that prevented the KeySelector
* from finding a potential key should be considered an exception.
* @throws ClassCastException if the data type of method
* is not supported by this key selector
*/
@Override
public KeySelectorResult select(final KeyInfo keyInfo,
final KeySelector.Purpose purpose, final AlgorithmMethod method,
final XMLCryptoContext context) throws KeySelectorException {
final 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
final Iterator i = keyInfo.getContent().iterator();
while (i.hasNext()) {
final XMLStructure kiType = (XMLStructure) i.next();
// check X509Data
if (kiType instanceof X509Data) {
final X509Data xd = (X509Data) kiType;
final KeySelectorResult ksr = x509DataSelect(xd, sm);
if (ksr != null) {
return ksr;
}
// check KeyName
} else if (kiType instanceof KeyName) {
final KeyName kn = (KeyName) kiType;
final 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) {
final RetrievalMethod rm = (RetrievalMethod) kiType;
try {
KeySelectorResult ksr = null;
if (rm.getType().equals
(X509Data.RAW_X509_CERTIFICATE_TYPE)) {
final OctetStreamData data = (OctetStreamData)
rm.dereference(context);
final CertificateFactory cf =
CertificateFactory.getInstance("X.509");
final X509Certificate cert = (X509Certificate)
cf.generateCertificate(data.getOctetStream());
ksr = certSelect(cert, sm);
} else if (rm.getType().equals(X509Data.TYPE)) {
final NodeSetData nd = (NodeSetData)
rm.dereference(context);
// convert nd to X509Data
// ksr = x509DataSelect(xd, sm);
} else {
// skip; keyinfo type is not supported
continue;
}
if (ksr != null) {
return ksr;
}
} catch (final Exception e) {
throw new KeySelectorException(e);
}
}
}
} catch (final 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(final CertSelector cs)
throws KeyStoreException {
final Enumeration aliases = ks.aliases();
while (aliases.hasMoreElements()) {
final String alias = (String) aliases.nextElement();
final 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(final X509Certificate xcert,
final SignatureMethod sm) throws KeyStoreException {
// skip non-signer certs
final boolean[] keyUsage = xcert.getKeyUsage();
if (keyUsage[0] == false) {
return null;
}
final String alias = ks.getCertificateAlias(xcert);
if (alias != null) {
final 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(final 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(final Key key) {
this.key = key;
}
@Override
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(final String algURI, final 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(final X509Data xd, final SignatureMethod sm)
throws KeyStoreException, KeySelectorException {
// convert signature algorithm to compatible public-key alg OID
final String algOID = getPKAlgorithmOID(sm.getAlgorithm());
KeySelectorResult ksr = null;
final Iterator xi = xd.getContent().iterator();
while (xi.hasNext()) {
ksr = null;
final Object o = xi.next();
// check X509Certificate
if (o instanceof X509Certificate) {
final X509Certificate xcert = (X509Certificate) o;
ksr = certSelect(xcert, sm);
// check X509IssuerSerial
} else if (o instanceof X509IssuerSerial) {
final X509IssuerSerial xis = (X509IssuerSerial) o;
final X509CertSelector xcs = new X509CertSelector();
try {
xcs.setSubjectPublicKeyAlgID(algOID);
xcs.setSerialNumber(xis.getSerialNumber());
xcs.setIssuer(new X500Principal
(xis.getIssuerName()).getName());
} catch (final IOException ioe) {
throw new KeySelectorException(ioe);
}
ksr = keyStoreSelect(xcs);
// check X509SubjectName
} else if (o instanceof String) {
final String sn = (String) o;
final X509CertSelector xcs = new X509CertSelector();
try {
xcs.setSubjectPublicKeyAlgID(algOID);
xcs.setSubject(new X500Principal(sn).getName());
} catch (final IOException ioe) {
throw new KeySelectorException(ioe);
}
ksr = keyStoreSelect(xcs);
// check X509SKI
} else if (o instanceof byte[]) {
final byte[] ski = (byte[]) o;
final X509CertSelector xcs = new X509CertSelector();
try {
xcs.setSubjectPublicKeyAlgID(algOID);
} catch (final IOException ioe) {
throw new KeySelectorException(ioe);
}
// DER-encode ski - required by X509CertSelector
final 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);
xcs.setSubjectKeyIdentifier(encodedSki);
ksr = keyStoreSelect(xcs);
// check X509CRL
// not supported: should use CertPath API
} else {
// skip all other entries
continue;
}
if (ksr != null) {
return ksr;
}
}
return null;
}
public static void main(final String[] args) throws Exception {
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy