I have a Root CA certificate that has to sign a certificate that I am creating.
public static X509Certificate generateCertificate(final PublicKey publicKey,
final PrivateKey privateKey,
final String signingAlgorithm, GeneralNames names)
throws IOException, CertificateException, OperatorCreationException, NoSuchAlgorithmException
{
final X500NameBuilder subject = new X500NameBuilder(RFC4519Style.INSTANCE);
subject.addRDN(BCStyle.C, COUNTRY_NAME);
subject.addRDN(BCStyle.ST, STATE_NAME);
subject.addRDN(BCStyle.L, LOCALITY_NAME);
subject.addRDN(BCStyle.O, ORGANIZATION_NAME);
subject.addRDN(BCStyle.OU, ORGANIZATION_UNIT_NAME);
subject.addRDN(BCStyle.E, EMAIL_ADDRESS);
final X500NameBuilder issuer = new X500NameBuilder(RFC4519Style.INSTANCE);
issuer.addRDN(BCStyle.C, COUNTRY_NAME);
issuer.addRDN(BCStyle.ST, STATE_NAME);
issuer.addRDN(BCStyle.L, LOCALITY_NAME);
issuer.addRDN(BCStyle.O, ORGANIZATION_NAME);
issuer.addRDN(BCStyle.OU, ISSUER);
final BigInteger sn = new BigInteger(SERIAL_NUMBER_LENGTH, new SecureRandom());
final Date validFrom = Calendar.getInstance().getTime();
final Calendar c = Calendar.getInstance();
c.add(Calendar.YEAR, YEARS_VALID);
final Date validUntil = c.getTime();
File file = new File("PATH TO ROOT CA");
CertificateFactory fact = CertificateFactory.getInstance("X.509");
FileInputStream in = new FileInputStream(file);
X509Certificate cer = (X509Certificate) fact.generateCertificate(in);
final JcaContentSignerBuilder builder = new JcaContentSignerBuilder(signingAlgorithm);
ContentSigner signer = builder.build(privateKey);
final X509v3CertificateBuilder certBuilder=new JcaX509v3CertificateBuilder(cer, sn, validFrom, validUntil, subject.build(), publicKey);
BasicConstraints constr = new BasicConstraints(false);
KeyUsage usage = new KeyUsage(KeyUsage.digitalSignature | KeyUsage.nonRepudiation | KeyUsage.keyEncipherment);
certBuilder.addExtension(Extension.keyUsage, false, usage);
certBuilder.addExtension(Extension.subjectAlternativeName, false, names);
certBuilder.addExtension(Extension.basicConstraints, false, constr);
// certBuilder.addExtension(Extension.authorityKeyIdentifier, false,utiles.createAuthorityKeyIdentifier(cer));
certBuilder.addExtension(Extension.subjectKeyIdentifier, false,
new SubjectKeyIdentifier(publicKey.getEncoded()));
final byte[] certBytes = certBuilder.build(signer).getEncoded();
final CertificateFactory certificateFactory = CertificateFactory.getInstance(CERTIFICATE_TYPE);
return (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(certBytes));
}
I have tried a number of things but I can't find exactly a solution. Also I don't understand what i have to set on Subject Key Identifier extension.
It seems you're not getting the X.509 certificate generation process right:
You generate a Certificate Signing Request (CSR), which is basically your certificate's structure, not yet signed by a CA;
A Certificate Authority (in this case, your root CA) signs this CSR.
Have a look at Sign CSR using Bouncy Castle, which is probably what you're looking for.
Related
I'm trying to store a private key in the android keystore but i keep getting an error, what am i doing wrong here ? I'm self signing the certificate because i need one to store the key but i don't need it.
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(1280);
KeyPair pair = keyPairGen.generateKeyPair();
PrivateKey privKey = pair.getPrivate();
PublicKey publicKey = pair.getPublic();
Certificate certificate = selfSign(pair, "myCer");
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
ks.setKeyEntry("MyPrivateKey", privKey, null, new Certificate[] { certificate });
Self signed certificate method :
public final static Certificate selfSign(KeyPair keyPair, String subjectDN) throws OperatorCreationException, CertificateException, IOException, CertIOException, CertificateException {
Provider bcProvider = new BouncyCastleProvider();
Security.addProvider(bcProvider);
long now = System.currentTimeMillis();
Date startDate = new Date(now);
X500Name dnName = new X500Name("CN=" + subjectDN);
BigInteger certSerialNumber = new BigInteger(Long.toString(now)); // <-- Using the current timestamp as the certificate serial number
Calendar calendar = Calendar.getInstance();
calendar.setTime(startDate);
calendar.add(Calendar.YEAR, 2); // <-- 1 Yr validity
Date endDate = calendar.getTime();
String signatureAlgorithm = "SHA256WithRSA"; // <-- Use appropriate signature algorithm based on your keyPair algorithm.
ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgorithm).build(keyPair.getPrivate());
JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(dnName, certSerialNumber, startDate, endDate, dnName, keyPair.getPublic());
// Extensions --------------------------
// Basic Constraints
BasicConstraints basicConstraints = new BasicConstraints(true); // <-- true for CA, false for EndEntity
certBuilder.addExtension(new ASN1ObjectIdentifier("2.5.29.19"), true, basicConstraints); // Basic Constraints is usually marked as critical.
// -------------------------------------
return new JcaX509CertificateConverter().setProvider(bcProvider).getCertificate(certBuilder.build(contentSigner));
}
I'm currently working on a project that involves getting an Elliptic Curve Certificate / Private Key package in either PEM or DER format from Vault and needing to import it into a Java Keystore. I can find plenty of information on doing this with keytool, but am unable to find information about how to successfully convert a DER encoded string into a PrivateKeyEntry to insert it into the keystore.
Below is my non-working code. certificate , key, and issuingCa are all PEM or DER encoded Strings (I can specify which format I want to get back from the issuer and pass whichever I can get to work)
private KeyStore packKeystore(String certificate, String key, String issuingCa, String name) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
// Create the keystore
KeyStore retVal = KeyStore.getInstance(KeyStore.getDefaultType());
retVal.load(null, sslKeystorePassword.toCharArray());
var cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME);
var cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certificate.getBytes()));
retVal.setCertificateEntry(name, cert);
Certificate issuer = null;
if (issuingCa != null) {
issuer = cf.generateCertificate(new ByteArrayInputStream(issuingCa.getBytes(StandardCharsets.UTF_8)));
}
if (key != null) {
var certs = new HashSet<Certificate>();
certs.add(issuer);
certs.add(cert);
PrivateKeyEntry pk = /// How do I create this from what I have????
retVal.setKeyEntry( pk, certs.toArray());
}
return retVal;
}
After some experimentation and research I've learned that the PrivateKey class doesn't like the "old" PEM format where the private key looks like ---- BEGIN EC PRIVATE KEY-----.
I was eventually able to parse a PrivateKey object from a keypair like this:
var parsedKey = new org.bouncycastle.openssl.PEMParser(new StringReader(key)).readObject();
var pair = new org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter().getKeyPair((org.bouncycastle.openssl.PEMKeyPair) parsedKey);
retVal.setKeyEntry(name, pair.getPrivate(), "".toCharArray(), certArray);
From there I get an error on setKeyEntry about the certificate algorithm not matching the private key algorithm. Upon inspection it seems that the Certificate object says the algo is EC and the PK object says the algo is ECDSA.
I eventually solved it this way. Note the key must be in base64 encoded DER format:
private PrivateKey convertECPrivateKeyString(String key) {
...
byte[] keyBytes = null;
if (key != null) {
keyBytes = Base64.getDecoder().decode(key);
}
try (var asnStream = new ASN1InputStream(keyBytes)) {
var primitive = asnStream.readObject();
asnStream.close();
if (primitive instanceof ASN1Sequence) {
var sequence = (ASN1Sequence) primitive;
var pKey = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(sequence);
var pkInfo = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, pKey.getParameters()), pKey);
return new JcaPEMKeyConverter().getPrivateKey(pkInfo);
}
}
...
}
Hello I am using the class X509V1CertificateGenerator to generate a certificate of the type X509Certificate.
now the class X509V1CertificateGenerator is deprecated and the recommanded alternative is X509v1CertificateBuilder but I do not know how to do the Migration.
Here is the Code:
X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
// set the necessary X500-fields
X500Principal dnName = new X500Principal("CN=MyServerName");
certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
certGen.setIssuerDN(dnName);
// expire-date
Calendar expireDate = Calendar.getInstance();
certGen.setNotBefore(expireDate.getTime());
// expires in 25 years
expireDate.add(Calendar.YEAR, 25);
certGen.setNotAfter(expireDate.getTime());
certGen.setSubjectDN(dnName); // note: same as issuer
certGen.setPublicKey(pair.getPublic());
// set the right signature-algorithm ->RSA/DSA
if (this.algorithm)
certGen.setSignatureAlgorithm("MD5withRSA");
else
certGen.setSignatureAlgorithm("SHA1withDSA");
// generate the X509-certificate
X509Certificate cert = certGen.generate(pair.getPrivate(), "BC");
What should I do to do the Migration?
Try this extracted from keycloak CertificateUtils
SubjectPublicKeyInfo subPubKeyInfo = SubjectPublicKeyInfo.getInstance(pair.getPublic().getEncoded());
X509v1CertificateBuilder builder = new X509v1CertificateBuilder(
subjectDN,
serialNumber,
validityStartDate,
validityEndDate,
subjectDN,
subPubKeyInfo);
AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(signatureAlgorithm);
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
ContentSigner contentSigner =
new BcRSAContentSignerBuilder(sigAlgId, digAlgId)
.build(PrivateKeyFactory.createKey(pair.getPrivate().getEncoded()));
X509CertificateHolder holder = builder.build(contentSigner);
X509Certificate cert = JcaX509CertificateConverter().getCertificate(holder);
When I use the openssl command to create a certificate, it has the option to secure the private key with a password. However, when I programmatically create a certificate and public/private key pair in Java, the Java API only has a way to set a password on the keystone but not on the private key (unlike the openssl command). So is there no way to encrypt the private key with a password like in the openssl command?
If it helps, here's how I'm creating the private key and certificate in Java:
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(KEY_SIZE, new SecureRandom());
KeyPair keyPair = keyPairGenerator.generateKeyPair();
X509CertInfo info = new X509CertInfo();
CertificateValidity validityInterval = generateValidityInterval();
BigInteger serialNumber = new BigInteger(SERIAL_NUMBER_SIZE, new SecureRandom());
X500Name owner = new X500Name(DN);
info.set(X509CertInfo.VALIDITY, validityInterval);
info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(serialNumber));
info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner));
info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner));
info.set(X509CertInfo.KEY, new CertificateX509Key(keyPair.getPublic()));
info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
AlgorithmId certificateAlgorithm = new AlgorithmId(CERTIFICATE_ALGORITHM);
info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(certificateAlgorithm));
X509CertImpl certificate = new X509CertImpl(info);
certificate.sign(keyPair.getPrivate(), new AlgorithmId(SIGNATURE_ALGORITHM).getName());
AlgorithmId x509Algorithm = (AlgorithmId) certificate.get(X509CertImpl.SIG_ALG);
info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, x509Algorithm);
certificate = new X509CertImpl(info);
certificate.sign(keyPair.getPrivate(), new AlgorithmId(SIGNATURE_ALGORITHM).getName());
The question is how to generate certificate chains programmatically in Java. In other words, I would like to perform in java the operations detailed here: http://fusesource.com/docs/broker/5.3/security/i382664.html
Besically, I can create the RSA keys for a new client:
private KeyPair genRSAKeyPair(){
// Get RSA key factory:
KeyPairGenerator kpg = null;
try {
kpg = KeyPairGenerator.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
log.error(e.getMessage());
e.printStackTrace();
return null;
}
// Generate RSA public/private key pair:
kpg.initialize(RSA_KEY_LEN);
KeyPair kp = kpg.genKeyPair();
return kp;
}
and I generate the corresponding certificate:
private X509Certificate generateCertificate(String dn, KeyPair pair, int days, String algorithm)
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));
info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner));
info.set(X509CertInfo.ISSUER, new CertificateIssuerName(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));
// Sign the cert to identify the algorithm that's used.
X509CertImpl cert = new X509CertImpl(info);
cert.sign(privkey, algorithm);
// Update the algorith, 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;
}
Then I generate the cert signing request and I save it to csrFile file:
public static void writeCertReq(File csrFile, String alias, String keyPass, KeyStore ks)
throws KeyStoreException,
NoSuchAlgorithmException,
InvalidKeyException,
IOException,
CertificateException,
SignatureException,
UnrecoverableKeyException {
Object objs[] = getPrivateKey(ks, alias, keyPass.toCharArray());
PrivateKey privKey = (PrivateKey) objs[0];
PKCS10 request = null;
Certificate cert = ks.getCertificate(alias);
request = new PKCS10(cert.getPublicKey());
String sigAlgName = "MD5WithRSA";
Signature signature = Signature.getInstance(sigAlgName);
signature.initSign(privKey);
X500Name subject = new X500Name(((X509Certificate) cert).getSubjectDN().toString());
X500Signer signer = new X500Signer(signature, subject);
request.encodeAndSign(signer);
request.print(System.out);
FileOutputStream fos = new FileOutputStream(csrFile);
PrintStream ps = new PrintStream(fos);
request.print(ps);
fos.close();
}
where
private static Object[] getPrivateKey(KeyStore ks, String alias, char keyPass[])
throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException {
key = null;
key = ks.getKey(alias, keyPass);
return (new Object[]{ (PrivateKey) key, keyPass });
}
Now I should sign the CSR with the CA private key, but I cannot see how to achive that in java. I have "my own" CA private key in my jks.
Besides, once I manage to sign the CSR I should chain the CA cert with the signed CSR: how that can be done in java?
I would prefer not to use bc or other external libs, just "sun.security" classes.
Thank you.
I believe the code example in the post http://www.pixelstech.net/article/1406726666-Generate-certificate-in-Java----2 will show you how to generate certificate chain with pure Java. It doesn't require you to use Bouncy Castle.
Sorry, but despite your desires, and besides writing all of your crypto code and including it with your project (not recommended), I'd recommend using Bouncy Castle here.
Specifically, please refer to https://stackoverflow.com/a/7366757/751158 - which includes code for exactly what you're looking to do.
I see you've already gone over to the BouncyCastle side of the house but just in case anyone else was wondering; you can add the cert chain to the entry when putting the key into the KeyStore. For example
// build your certs
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load([keystore stream], password.toCharArray());// or null, null if it's a brand new store
X509Certificate[] chain = new X509Certificate[2];
chain[0] = _clientCert;
chain[1] = _caCert;
keyStore.setKeyEntry("Alias", _clientCertKey, password.toCharArray(), chain);
keyStore.store([output stream], password.toCharArray());
// do other stuff