* @param algorithm the signing algorithm, eg "SHA256withRSA"
* @param san SubjectAlternativeName extension (optional)
*/
private static X509Certificate generateCertificate(String dn, KeyPair pair, int days, String algorithm, String san) throws GeneralSecurityException, IOException {
PrivateKey privkey = pair.getPrivate();
X509CertInfo info = new X509CertInfo();
Date from = new Date();
Date to = new Date(from.getTime() + days * 86400000l);
CertificateValidity interval = new CertificateValidity(from, to);
BigInteger sn = new BigInteger(64, new SecureRandom());
X500Name owner = new X500Name(dn);
info.set(X509CertInfo.VALIDITY, interval);
info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn));
// Change of behaviour in JDK8:
// https://bugs.openjdk.java.net/browse/JDK-8040820
// https://bugs.openjdk.java.net/browse/JDK-7198416
String version = System.getProperty("java.version");
if (version == null || version.matches("^(1\\.)?[7].*")) {
// Java 7 code. To remove with Java 8 migration
info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner));
info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner));
} else {
// Java 8 and later code
info.set(X509CertInfo.SUBJECT, owner);
info.set(X509CertInfo.ISSUER, owner);
}
info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic()));
info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
AlgorithmId algo = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo));
CertificateExtensions ext = new CertificateExtensions();
// Critical: Not CA, max path len 0
ext.set(BasicConstraintsExtension.NAME, new BasicConstraintsExtension(true, false, 0));
// Critical: only allow TLS ("serverAuth" = 1.3.6.1.5.5.7.3.1)
ext.set(ExtendedKeyUsageExtension.NAME, new ExtendedKeyUsageExtension(true,
new Vector<ObjectIdentifier>(Arrays.asList(new ObjectIdentifier("1.3.6.1.5.5.7.3.1")))));
if (san != null) {
int colonpos;
String[] ps = san.split(",");
GeneralNames gnames = new GeneralNames();
for(String item: ps) {
colonpos = item.indexOf(':');
if (colonpos < 0) {
throw new IllegalArgumentException("Illegal item " + item + " in " + san);
}
String t = item.substring(0, colonpos);
String v = item.substring(colonpos+1);
gnames.add(createGeneralName(t, v));
}
// Non critical
ext.set(SubjectAlternativeNameExtension.NAME, new SubjectAlternativeNameExtension(false, gnames));
}
info.set(X509CertInfo.EXTENSIONS, ext);
// Sign the cert to identify the algorithm that's used.
X509CertImpl cert = new X509CertImpl(info);
cert.sign(privkey, algorithm);
// Update the algorithm, and resign.
algo = (AlgorithmId)cert.get(X509CertImpl.SIG_ALG);
info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, algo);
cert = new X509CertImpl(info);
cert.sign(privkey, algorithm);
return cert;
}