package org.openstack.atlas.util.ca;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.DEREncodable;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.Attribute;
import org.bouncycastle.asn1.x509.X509Extension;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.PKCS10CertificationRequest;
import org.bouncycastle.jce.provider.JCERSAPublicKey;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;
import org.openstack.atlas.util.ca.exceptions.PemException;
import org.openstack.atlas.util.ca.primitives.RsaConst;
import java.security.cert.CertificateException;
import java.security.cert.CertificateParsingException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.jce.provider.X509CertificateObject;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.openstack.atlas.util.ca.PemUtils;
import org.openstack.atlas.util.ca.RSAKeyUtils;
import org.openstack.atlas.util.ca.exceptions.NotAnRSAKeyException;
import org.openstack.atlas.util.ca.exceptions.RsaException;
import org.openstack.atlas.util.ca.zeus.ErrorEntry;
import org.openstack.atlas.util.ca.zeus.ErrorType;
public class CertUtils {
public static final String ISSUER_NOT_AFTER_FAIL = "Issuer Cert Expired";
public static final String ISSUER_NOT_BEFORE_FAIL = "Sssuer Cert Premature";
public static final String SUBJECT_NOT_AFTER_FAIL = "Subject Cert Expired";
public static final String SUBJECT_NOT_BEFORE_FAIL = "subject Cert Premature";
public static final int DEFAULT_NOT_AFTER_YEARS = 2;
public static final long DAY_IN_MILLIS = (long) 24 * 60 * 60 * 1000;
static {
RsaConst.init();
}
public static X509Certificate signCSR(PKCS10CertificationRequest req, KeyPair kp, X509Certificate caCrt, int days, BigInteger serial) throws RsaException {
long nowMillis = System.currentTimeMillis();
Date notBefore = new Date(nowMillis);
Date notAfter = new Date((long) days * DAY_IN_MILLIS + nowMillis);
return signCSR(req, kp, caCrt, notBefore, notAfter, serial);
}
public static X509Certificate signCSR(PKCS10CertificationRequest req, KeyPair kp, X509Certificate caCrt, Date notBeforeIn, Date notAfterIn,
BigInteger serial) throws RsaException {
int i;
Date notBefore;
Date notAfter;
PublicKey caPub;
PrivateKey caPriv;
PublicKey crtPub;
BigInteger serialNum;
X509Certificate crt = null;
JcaX509v3CertificateBuilder certBuilder;
ContentSigner signer;
X500Principal issuer;
X500Principal subject;
AuthorityKeyIdentifierStructure authKeyId;
SubjectKeyIdentifierStructure subjKeyId;
caPub = kp.getPublic();
caPriv = kp.getPrivate();
notBefore = notBeforeIn;
notAfter = notAfterIn;
try {
crtPub = req.getPublicKey();
} catch (GeneralSecurityException ex) {
throw new RsaException("Unable to fetch public key from CSR", ex);
}
JcaContentSignerBuilder sigBuilder = new JcaContentSignerBuilder(RsaConst.DEFAULT_SIGNATURE_ALGO);
sigBuilder.setProvider("BC");
try {
signer = sigBuilder.build(caPriv);
} catch (OperatorCreationException ex) {
throw new RsaException("Error creating signature", ex);
}
try {
if (!req.verify()) {
throw new RsaException("CSR was invalid");
}
} catch (GeneralSecurityException ex) {
throw new RsaException("Could not verify CSR", ex);
}
// If the user left a blank serial number then use the current time for the serial number
serialNum = (serial == null) ? BigInteger.valueOf(System.currentTimeMillis()) : serial;
byte[] encodedSubject = req.getCertificationRequestInfo().getSubject().toASN1Object().getDEREncoded();
subject = new X500Principal(encodedSubject);
//subject = new X500Principal(req.getCertificationRequestInfo().getSubject().toString());
issuer = caCrt.getSubjectX500Principal();
certBuilder = new JcaX509v3CertificateBuilder(issuer, serialNum,
notBefore, notAfter, subject, crtPub);
// Add any x509 extensions from the request
ASN1Set attrs = req.getCertificationRequestInfo().getAttributes();
X509Extension ext;
if (attrs != null) {
for (i = 0; i < attrs.size(); i++) {
Attribute attr = Attribute.getInstance(attrs.getObjectAt(i));
if (attr.getAttrType().equals(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest)) {
DEREncodable extsDer = attr.getAttrValues().getObjectAt(0);
X509Extensions exts = X509Extensions.getInstance(extsDer);
for (ASN1ObjectIdentifier oid : exts.getExtensionOIDs()) {
ext = exts.getExtension(oid);
certBuilder.addExtension(oid, ext.isCritical(), ext.getParsedValue());
}
}
}
}
try {
authKeyId = new AuthorityKeyIdentifierStructure(caCrt);
subjKeyId = new SubjectKeyIdentifierStructure(crtPub);
} catch (InvalidKeyException ex) {
throw new RsaException("Ivalid public key when attempting to encode Subjectkey identifier", ex);
} catch (CertificateParsingException ex) {
throw new RsaException("Unable to build AuthorityKeyIdentifier", ex);
}
certBuilder.addExtension(X509Extension.authorityKeyIdentifier, false, authKeyId.getDERObject());
certBuilder.addExtension(X509Extension.subjectKeyIdentifier, false, subjKeyId.getDERObject());
X509CertificateHolder certHolder = certBuilder.build(signer);
try {
crt = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder);
} catch (CertificateException ex) {
throw new RsaException("Unable to build Certificate", ex);
}
return crt;
}
public static X509Certificate selfSignCsrCA(PKCS10CertificationRequest req, KeyPair kp, Date notBefore, Date notAfter) throws RsaException {
PrivateKey priv;
PublicKey pub;
String msg;
X509Certificate cert = null;
SubjectKeyIdentifierStructure subjKeyId;
int i;
try {
if (!req.verify()) {
throw new RsaException("CSR was invalid");
}
} catch (GeneralSecurityException ex) {
throw new RsaException("Could not verify CSR", ex);
}
X500Name subject = X500Name.getInstance(req.getCertificationRequestInfo().getSubject());
X500Name issuer = X500Name.getInstance(req.getCertificationRequestInfo().getSubject());
priv = kp.getPrivate();
pub = kp.getPublic();
JcaContentSignerBuilder sigBuilder = new JcaContentSignerBuilder(RsaConst.DEFAULT_SIGNATURE_ALGO);
sigBuilder.setProvider("BC");
ContentSigner signer;
try {
signer = sigBuilder.build(priv);
} catch (OperatorCreationException ex) {
throw new RsaException("Error creating signature", ex);
}
BigInteger serial = BigInteger.ONE;
JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(issuer, serial, notBefore, notAfter, subject, pub);
certBuilder.addExtension(X509Extension.basicConstraints, true, new BasicConstraints(true));//This is a CA crt
try {
subjKeyId = new SubjectKeyIdentifierStructure(pub);
} catch (InvalidKeyException ex) {
throw new RsaException("Ivalid public key when attempting to encode Subjectkey identifier", ex);
}
certBuilder.addExtension(X509Extension.subjectKeyIdentifier, false, subjKeyId.getDERObject());
X509CertificateHolder certHolder = certBuilder.build(signer);
try {
cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder);
} catch (CertificateException ex) {
throw new RsaException("Error generating x509 certificate", ex);
}
return cert;
}
public static List<ErrorEntry> verifyIssuerAndSubjectCert(X509Certificate issuerCert, X509Certificate subjectCert) {
return verifyIssuerAndSubjectCert(issuerCert, subjectCert, true);
}
public static List<ErrorEntry> verifyIssuerAndSubjectCert(X509Certificate issuerCert, X509Certificate subjectCert, boolean isDateFatal) {
ErrorEntry errorEntry;
List<ErrorEntry> errorList = new ArrayList<ErrorEntry>();
PublicKey parentPub = null;
try {
parentPub = (PublicKey) issuerCert.getPublicKey();
} catch (ClassCastException ex) {
errorEntry = new ErrorEntry(ErrorType.SIGNATURE_ERROR, "error getting Public key from isser cert", true, ex);
errorList.add(errorEntry);
}
try {
subjectCert.checkValidity();
} catch (CertificateExpiredException ex) {
errorList.add(new ErrorEntry(ErrorType.EXPIRED_CERT, SUBJECT_NOT_AFTER_FAIL, isDateFatal, ex));
} catch (CertificateNotYetValidException ex) {
errorList.add(new ErrorEntry(ErrorType.PREMATURE_CERT, SUBJECT_NOT_BEFORE_FAIL, isDateFatal, ex));
}
try {
issuerCert.checkValidity();
} catch (CertificateExpiredException ex) {
errorList.add(new ErrorEntry(ErrorType.EXPIRED_CERT, ISSUER_NOT_AFTER_FAIL, isDateFatal, ex));
} catch (CertificateNotYetValidException ex) {
errorList.add(new ErrorEntry(ErrorType.PREMATURE_CERT, ISSUER_NOT_BEFORE_FAIL, isDateFatal, ex));
}
if (parentPub == null) {
return errorList; // Can't test anyfuther if we failed to extract the parent pubKey
}
try {
subjectCert.verify(parentPub);
} catch (CertificateException ex) {
errorList.add(new ErrorEntry(ErrorType.SIGNATURE_ERROR, "signature Algo mismatch", true, ex));
} catch (NoSuchAlgorithmException ex) {
errorList.add(new ErrorEntry(ErrorType.SIGNATURE_ERROR, "Unrecognized signature Algo", true, ex));
} catch (InvalidKeyException ex) {
errorList.add(new ErrorEntry(ErrorType.SIGNATURE_ERROR, "Signing key mismatch", true, ex));
} catch (NoSuchProviderException ex) {
errorList.add(new ErrorEntry(ErrorType.SIGNATURE_ERROR, "Application doesn't know how to verify signature", true, ex));
} catch (SignatureException ex) {
errorList.add(new ErrorEntry(ErrorType.SIGNATURE_ERROR, "Signature does not match", true, ex));
}
return errorList;
}
public static List<ErrorEntry> verifyIssuerAndSubjectCert(byte[] issuerCertPem, byte[] subjectCertPem) {
return verifyIssuerAndSubjectCert(issuerCertPem, subjectCertPem, true);
}
public static List<ErrorEntry> verifyIssuerAndSubjectCert(byte[] issuerCertPem, byte[] subjectCertPem, boolean isDateFatal) {
List<ErrorEntry> errorList = new ArrayList<ErrorEntry>();
ErrorEntry errorEntry;
X509Certificate issuerCert = null;
X509Certificate subjectCert = null;
if (issuerCertPem == null) {
errorList.add(new ErrorEntry(ErrorType.UNREADABLE_CERT, "isserCertPem was null", true, null));
}
if (subjectCertPem == null) {
errorList.add(new ErrorEntry(ErrorType.UNREADABLE_CERT, "SubjectCertPem was null", true, null));
}
if (issuerCertPem == null || subjectCertPem == null) {
return errorList;
}
try {
issuerCert = (X509Certificate) PemUtils.fromPem(issuerCertPem);
} catch (PemException ex) {
errorList.add(new ErrorEntry(ErrorType.UNREADABLE_CERT, "Error decodeing issuer Cert data to X509Certificate", true, ex));
} catch (ClassCastException ex) {
errorList.add(new ErrorEntry(ErrorType.UNREADABLE_CERT, "Error decodeing issuer Cert data to X509Certificate", true, ex));
}
try {
subjectCert = (X509Certificate) PemUtils.fromPem(subjectCertPem);
} catch (PemException ex) {
errorList.add(new ErrorEntry(ErrorType.UNREADABLE_CERT, "Error decodeing subject Cert data to X509Certificate", true, ex));
} catch (ClassCastException ex) {
errorList.add(new ErrorEntry(ErrorType.UNREADABLE_CERT, "Error decodeing issuer Cert data to X509Certificate", true, ex));
}
if (issuerCert == null || subjectCert == null) {
return errorList;
}
return verifyIssuerAndSubjectCert(issuerCert, subjectCert, isDateFatal);
}
@Deprecated
public static String certToStr(X509Certificate cert) {
StringBuilder sb = new StringBuilder(RsaConst.PAGESIZE);
String subjectStr = cert.getSubjectX500Principal().toString();
String issuerStr = cert.getIssuerX500Principal().toString();
JCERSAPublicKey pub = (JCERSAPublicKey) cert.getPublicKey();
String pubKeyClass = pub.getClass().getCanonicalName();
BigInteger n = pub.getModulus();
BigInteger e = pub.getPublicExponent();
sb.append(String.format("subject = \"%s\"\n", subjectStr));
sb.append(String.format("issuer = \"%s\"\n", issuerStr));
sb.append(String.format("pubKeyClass = \"%s\"\n", pubKeyClass));
sb.append(String.format(" n = %s\n", n));
sb.append(String.format(" e = %s\n", e));
sb.append(String.format("ShortPub = %s\n", RSAKeyUtils.shortPub(pub)));
sb.append(String.format("pubModSize = %d\n", RSAKeyUtils.modSize(pub)));
String valid = "Valid";
try {
cert.checkValidity();
} catch (CertificateExpiredException ex) {
valid = "Not After Fail";
} catch (CertificateNotYetValidException ex) {
valid = "Not Before Fail";
}
String selfSigned = (isSelfSigned(cert)) ? "true" : "false";
sb.append(String.format("isSelfSigned = %s\n", selfSigned));
return sb.toString();
}
public static boolean isSelfSigned(X509Certificate cert) {
PublicKey pubKey = cert.getPublicKey();
try {
cert.verify(pubKey);
} catch (CertificateException ex) {
return false;
} catch (NoSuchAlgorithmException ex) {
return false;
} catch (InvalidKeyException ex) {
return false;
} catch (NoSuchProviderException ex) {
return false;
} catch (SignatureException ex) {
return false;
}
return true;
}
public static boolean isSelfSigned(byte[] certPem) {
X509Certificate cert;
try {
cert = (X509Certificate) PemUtils.fromPem(certPem);
} catch (PemException ex) {
return false;
} catch (ClassCastException ex) {
return false;
}
return isSelfSigned(cert);
}
public static boolean isCertExpired(X509Certificate x509, Date date) {
Date dateObj = date;
if (date == null) {
dateObj = new Date(System.currentTimeMillis());
}
Date x509after = x509.getNotAfter();
boolean isExpiredCert = x509after.before(dateObj);
return isExpiredCert;
}
public static boolean isCertPremature(X509Certificate x509, Date date) {
Date dateObj = date;
if (date == null) {
dateObj = new Date(System.currentTimeMillis());
}
Date x509Before = x509.getNotBefore();
boolean isPrematureCert = x509Before.after(dateObj);
return isPrematureCert;
}
public static boolean isCertDateValid(X509Certificate x509, Date date) {
return !isCertPremature(x509, date) && !isCertExpired(x509, date);
}
public static X509Certificate quickSelfSign(KeyPair kpIn, String subjectName, Date notBefore, Date notAfter) throws RsaException {
KeyPair kp;
kp = kpIn;
if (kp == null) {
kp = RSAKeyUtils.genKeyPair(RSAKeyUtils.DEFAULT_KEY_SIZE); // If you pass in null you'll never see the key again
}
PKCS10CertificationRequest csr = CsrUtils.newCsr(subjectName, kp, true);
X509Certificate x509 = CertUtils.selfSignCsrCA(csr, kp, notBefore, notAfter);
return x509;
}
public static Set<X509Certificate> getExpiredCerts(Set<X509Certificate> certs, Date date) {
Set<X509Certificate> expiredCerts = new HashSet<X509Certificate>();
for (X509Certificate x509 : certs) {
if (isCertExpired(x509, date)) {
expiredCerts.add(x509);
}
}
return expiredCerts;
}
public static List<ErrorEntry> validateKeyMatchesCrt(KeyPair kp, X509CertificateObject x509obj) {
List<ErrorEntry> errors = new ArrayList<ErrorEntry>();
Object obj = kp.getPublic();
if (!(obj instanceof JCERSAPublicKey)) {
errors.add(new ErrorEntry(ErrorType.KEY_CERT_MISMATCH, "Could not retrieve public key from keypair", true, null));
return errors;
}
JCERSAPublicKey pubKey = (JCERSAPublicKey) obj;
return validateKeyMatchesCert(pubKey, x509obj);
}
public static List<ErrorEntry> validateKeyMatchesCert(JCERSAPublicKey key, X509CertificateObject x509obj) {
List<ErrorEntry> errors = new ArrayList<ErrorEntry>();
JCERSAPublicKey certPub;
Object obj = x509obj.getPublicKey();
if (!(obj instanceof JCERSAPublicKey)) {
errors.add(new ErrorEntry(ErrorType.UNREADABLE_CERT, "Unable to extract public RSA key from x509 certificate", true, null));
return errors;
}
certPub = (JCERSAPublicKey) obj;
if (!(certPub.getModulus().equals(key.getModulus()))) {
errors.add(new ErrorEntry(ErrorType.KEY_CERT_MISMATCH, "Modulus between user cert and key did not match", true, null));
}
if (!(certPub.getPublicExponent().equals(key.getPublicExponent()))) {
errors.add(new ErrorEntry(ErrorType.KEY_CERT_MISMATCH, "Cert and key public exponent do not match", true, null));
}
return errors;
}
public static List<ErrorEntry> validateKeySignsCert(KeyPair kp, X509CertificateObject x509obj) {
List<ErrorEntry> errors = new ArrayList<ErrorEntry>();
Object obj = kp.getPublic();
if (!(obj instanceof JCERSAPublicKey)) {
errors.add(new ErrorEntry(ErrorType.SIGNATURE_ERROR, "Could not retrieve public key from keypair", true, null));
return errors;
}
JCERSAPublicKey pubKey = (JCERSAPublicKey) obj;
return validateKeySignsCert(pubKey, x509obj);
}
public static List<ErrorEntry> validateKeySignsCert(JCERSAPublicKey key, X509CertificateObject x509obj) {
List<ErrorEntry> errors = new ArrayList<ErrorEntry>();
JCERSAPublicKey certPub;
Object obj = x509obj.getPublicKey();
if (!(obj instanceof JCERSAPublicKey)) {
errors.add(new ErrorEntry(ErrorType.SIGNATURE_ERROR, "Unable to extract public RSA key from x509 certificate", true, null));
return errors;
}
try {
x509obj.verify(key);
} catch (CertificateException ex) {
errors.add(new ErrorEntry(ErrorType.SIGNATURE_ERROR, "Unknown CertificateException", true, ex));
} catch (NoSuchAlgorithmException ex) {
errors.add(new ErrorEntry(ErrorType.SIGNATURE_ERROR, "Unknown signing Algorithm", true, ex));
} catch (InvalidKeyException ex) {
errors.add(new ErrorEntry(ErrorType.SIGNATURE_ERROR, "Key does not match certificate", true, ex));
} catch (NoSuchProviderException ex) {
errors.add(new ErrorEntry(ErrorType.UNKNOWN, "Could not find BouncyCastle provider", true, ex));
} catch (SignatureException ex) {
errors.add(new ErrorEntry(ErrorType.SIGNATURE_ERROR, "Ivalid Signature", true, ex));
}
return errors;
}
public static Set<X509Certificate> getPrematureCerts(Set<X509Certificate> certs, Date date) {
Set<X509Certificate> prematureCerts = new HashSet<X509Certificate>();
for (X509Certificate x509 : certs) {
if (isCertPremature(x509, date)) {
prematureCerts.add(x509);
}
}
return prematureCerts;
}
public static Set<X509Certificate> getValidDateCerts(Set<X509Certificate> certs, Date date) {
Set<X509Certificate> validDateCerts = new HashSet<X509Certificate>();
for (X509Certificate x509 : certs) {
if (isCertDateValid(x509, date)) {
validDateCerts.add(x509);
}
}
return validDateCerts;
}
}