package org.openstack.atlas.util.ca.util;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.CertPath;
import java.security.cert.CertPathBuilder;
import java.security.cert.CertPathBuilderException;
import java.security.cert.CertStore;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.PKIXCertPathBuilderResult;
import java.security.cert.TrustAnchor;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.bouncycastle.jce.PKCS10CertificationRequest;
import org.bouncycastle.jce.provider.X509CertificateObject;
import org.openstack.atlas.util.ca.CertUtils;
import org.openstack.atlas.util.ca.CsrUtils;
import org.openstack.atlas.util.ca.RSAKeyUtils;
import org.openstack.atlas.util.ca.exceptions.NotAnX509CertificateException;
import org.openstack.atlas.util.ca.exceptions.RsaException;
import org.openstack.atlas.util.ca.primitives.RsaConst;
import org.openstack.atlas.util.ca.exceptions.X509PathBuildException;
// Use this one instead of X509Chainer
public class X509PathBuilder<E extends X509Certificate> {
private Set<E> rootCAs;
private Set<E> intermediates;
static {
RsaConst.init();
}
public X509PathBuilder() {
rootCAs = new HashSet<E>();
intermediates = new HashSet<E>();
}
public X509PathBuilder(Set<E> rootCAs, Set<E> intermediates) {
this.rootCAs = new HashSet<E>(rootCAs);
this.intermediates = new HashSet<E>(intermediates);
}
public void clear() {
rootCAs = new HashSet<E>();
intermediates = new HashSet<E>();
}
public X509BuiltPath<E> buildPath(E userCrt) throws X509PathBuildException {
return buildPath(userCrt, null);
}
public X509BuiltPath<E> buildPath(E userCrt, Date date) throws X509PathBuildException {
List<E> discoveredPath = new ArrayList<E>();
// Build Crt Store
Set<E> colStoreCrts = new HashSet<E>();
colStoreCrts.addAll(intermediates);
colStoreCrts.add(userCrt); // Don't forget to add the End cert
colStoreCrts.removeAll(rootCAs); // rootCAs are the endpoint so remove them incase they are in the intermediates
CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(colStoreCrts);
CertStore crtStore;
try {
crtStore = CertStore.getInstance("Collection", ccsp, "SUN");
} catch (InvalidAlgorithmParameterException ex) {
throw new X509PathBuildException("InvalidAlgorithmParemeter when initializing CollectionStore", ex);
} catch (NoSuchAlgorithmException ex) {
throw new X509PathBuildException("NoSuchAlgorithmException when initializing CollectionStore", ex);
} catch (NoSuchProviderException ex) {
throw new X509PathBuildException("NoSuchProviderException when initializing CollectionStore", ex);
}
X509CertSelector userCrtSelector = new X509CertSelector();
userCrtSelector.setCertificate(userCrt);
// Build trusted roots;
Set<TrustAnchor> anchors = new HashSet<TrustAnchor>();
for (E x509 : rootCAs) {
TrustAnchor ta = new TrustAnchor(x509, null);
anchors.add(ta);
}
// Setup the path builder
PKIXBuilderParameters pbp;
try {
pbp = new PKIXBuilderParameters(anchors, userCrtSelector);
} catch (InvalidAlgorithmParameterException ex) {
throw new X509PathBuildException("InvalidAlgorithmParameter when initializing PKIXBuilderParameters", ex);
}
pbp.addCertStore(crtStore);
pbp.setRevocationEnabled(false);
pbp.setMaxPathLength(25);
pbp.setDate(date);
CertPathBuilder pathBuilder;
try {
pathBuilder = CertPathBuilder.getInstance("PKIX", "SUN");
} catch (NoSuchAlgorithmException ex) {
throw new X509PathBuildException("NoSuchAlgorithmException when initializing pathBuilder", ex);
} catch (NoSuchProviderException ex) {
throw new X509PathBuildException("NoSuchProviderException when initializing pathBuilder", ex);
}
PKIXCertPathBuilderResult buildResponse;
try {
buildResponse = (PKIXCertPathBuilderResult) pathBuilder.build(pbp);
} catch (CertPathBuilderException ex) {
throw new X509PathBuildException(ex);
} catch (InvalidAlgorithmParameterException ex) {
throw new X509PathBuildException(ex);
}
CertPath builtCrtPath = buildResponse.getCertPath();
Iterator crtIterator = builtCrtPath.getCertificates().iterator();
while (crtIterator.hasNext()) {
Object obj = crtIterator.next();
if (!(obj instanceof X509Certificate)) {
String fmt = "Object of type %s does not appear to be a of X509Certificate or a subtype";
String msg = String.format(fmt, obj.getClass().getSimpleName());
throw new IllegalStateException(msg);
} else {
discoveredPath.add((E) obj);
}
}
TrustAnchor topAnchor;
TrustAnchor mostTrustedAnchor = buildResponse.getTrustAnchor();
Object obj = mostTrustedAnchor.getTrustedCert();
E topCrt = (E) mostTrustedAnchor.getTrustedCert();
X509BuiltPath<E> builtPath = new X509BuiltPath<E>(discoveredPath, topCrt);
return builtPath;
}
// Usefull for pretending to be a CA when you want to test a Chain
public static List<X509ChainEntry> newChain(List<String> subjNames, int keySize, Date notBefore, Date notAfter) throws NotAnX509CertificateException, RsaException {
List<X509ChainEntry> chain = new ArrayList<X509ChainEntry>();
String rootSubj = subjNames.get(0);
KeyPair rootKey = RSAKeyUtils.genKeyPair(keySize);
PKCS10CertificationRequest rootCsr = CsrUtils.newCsr(rootSubj, rootKey, true);
Object obj = CertUtils.selfSignCsrCA(rootCsr, rootKey, notBefore, notAfter);
if (!(obj instanceof X509CertificateObject)) {
String fmt = "Could not generate X509CertificateObject for subj \"%s\"";
String msg = String.format(fmt, rootSubj);
throw new NotAnX509CertificateException(msg);
}
X509CertificateObject rootCrt = (X509CertificateObject) obj;
chain.add(new X509ChainEntry(rootKey, rootCsr, rootCrt));
List<String> subjNamesForSubChain = new ArrayList<String>();
for (int i = 1; i < subjNames.size(); i++) {
subjNamesForSubChain.add(subjNames.get(i));
}
chain.addAll(newChain(rootKey, rootCrt, subjNamesForSubChain, keySize, notBefore, notAfter));
return chain;
}
// Usefull for pretending to be a CA when you want to test a Chain
public static List<X509ChainEntry> newChain(KeyPair rootKey, X509CertificateObject rootCert, List<String> subjNames, int keySize, Date notBefore, Date notAfter) throws NotAnX509CertificateException, RsaException {
List<X509ChainEntry> chain = new ArrayList<X509ChainEntry>();
X509CertificateObject caCrt = rootCert;
X509CertificateObject crt;
Object obj;
PKCS10CertificationRequest csr;
KeyPair caKey = rootKey;
KeyPair key;
for (String subjName : subjNames) {
key = RSAKeyUtils.genKeyPair(keySize);
csr = CsrUtils.newCsr(subjName, key, true);
obj = CertUtils.signCSR(csr, caKey, caCrt, notBefore, notAfter, null);
if (!(obj instanceof X509CertificateObject)) {
String fmt = "Could not generate X509CertificateObject for subj \"%s\"";
String msg = String.format(fmt, subjName);
throw new NotAnX509CertificateException(msg);
}
crt = (X509CertificateObject) obj;
chain.add(new X509ChainEntry(key, csr, crt));
caCrt = crt;
caKey = key;
}
return chain;
}
public Set<E> getRootCAs() {
return rootCAs;
}
public void setRootCAs(Set<E> rootCAs) {
this.rootCAs = rootCAs;
}
public Set<E> getIntermediates() {
return intermediates;
}
public void setIntermediates(Set<E> intermediates) {
this.intermediates = intermediates;
}
}