package be.neutrinet.ispng.vpn.ca;
import be.neutrinet.ispng.VPN;
import be.neutrinet.ispng.vpn.api.VPNClientCertificate;
import org.apache.log4j.Logger;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.*;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.bc.BcX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.util.io.pem.PemObject;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.math.BigInteger;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Date;
public class CA {
public final static String SIGNING_ALGORITHM = "SHA512withRSA";
private static CA instance;
private KeyStore keyStore;
private PrivateKey caKey;
private X509Certificate caCert;
private CA() {
try {
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream(VPN.cfg.getProperty("ca.keyStore")),
VPN.cfg.get("ca.keyStorePassword").toString().toCharArray());
caKey = (PrivateKey) keyStore.getKey("ca", VPN.cfg.get("ca.keyPassword").toString().toCharArray());
caCert = (X509Certificate) keyStore.getCertificate("ca");
} catch (Exception ex) {
Logger.getLogger(VPNClientCertificate.class).error("Failed to load ca key", ex);
}
}
public static CA get() {
if (instance == null) instance = new CA();
return instance;
}
// loosely based on http://stackoverflow.com/questions/7230330/sign-csr-using-bouncy-castle
// returns signed certificate in DER format
public BigInteger signCSR(PKCS10CertificationRequest csr, Date expiration) throws Exception {
try {
// Certificate serials should be random (hash)
//http://crypto.stackexchange.com/questions/257/unpredictability-of-x-509-serial-numbers
SecureRandom random = new SecureRandom();
byte[] serial = new byte[16];
random.nextBytes(serial);
BigInteger bigserial = new BigInteger(serial);
bigserial = bigserial.abs();
AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(SIGNING_ALGORITHM);
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
// http://stackoverflow.com/questions/7567837/attributes-reversed-in-certificate-subject-and-issuer
X500Name issuer = new JcaX509CertificateHolder((X509Certificate) caCert).getSubject();
X509v3CertificateBuilder certgen = new X509v3CertificateBuilder(
issuer,
bigserial,
new Date(),
expiration,
X500Name.getInstance(csr.getSubject()),
csr.getSubjectPublicKeyInfo());
// Constraints and usage
BasicConstraints basicConstraints = new BasicConstraints(false);
KeyUsage keyUsage = new KeyUsage(KeyUsage.digitalSignature);
ExtendedKeyUsage eku = new ExtendedKeyUsage(KeyPurposeId.id_kp_clientAuth);
certgen.addExtension(Extension.basicConstraints, false, basicConstraints);
certgen.addExtension(Extension.keyUsage, false, keyUsage);
certgen.addExtension(Extension.extendedKeyUsage, false, eku);
// Identifiers
BcX509ExtensionUtils extensionUtils = new BcX509ExtensionUtils();
org.bouncycastle.asn1.x509.SubjectKeyIdentifier subjectKeyIdentifier = extensionUtils.createSubjectKeyIdentifier(csr.getSubjectPublicKeyInfo());
AuthorityKeyIdentifier authorityKeyIdentifier = new AuthorityKeyIdentifier(new GeneralNames
(new GeneralName(issuer)), caCert.getSerialNumber());
certgen.addExtension(Extension.subjectKeyIdentifier, false, subjectKeyIdentifier);
certgen.addExtension(Extension.authorityKeyIdentifier, false, authorityKeyIdentifier);
ContentSigner signer = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(PrivateKeyFactory.createKey(caKey.getEncoded()));
X509CertificateHolder holder = certgen.build(signer);
byte[] certencoded = holder.toASN1Structure().getEncoded();
PemObject po = new PemObject("CERTIFICATE", certencoded);
FileOutputStream fos = new FileOutputStream(VPN.cfg.getProperty("ca.storeDir", "ca") + "/" + bigserial.toString() + ".crt");
JcaPEMWriter pw = new JcaPEMWriter(new OutputStreamWriter(fos));
pw.writeObject(po);
pw.close();
return bigserial;
} catch (Exception ex) {
Logger.getLogger(getClass()).error("Failed to validate CSR and sign CSR", ex);
}
return null;
}
}