From certificate Alias to PEM File with private key included using Java - java

I have this code to generate a CER file using the alias:
public class TestFromAliasToCER {
public static final int KEY_SIZE = 1024;
public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
public static final String END_CERT = "-----END CERTIFICATE-----";
public final static String LINE_SEPARATOR = System.getProperty("line.separator");
public static void main(String[] args) throws FileNotFoundException, IOException, NoSuchAlgorithmException, NoSuchProviderException, KeyStoreException, CertificateException {
KeyStore keyStore = KeyStore.getInstance ("Windows-MY");
keyStore.load (null, null);
Enumeration<String> aux = keyStore.aliases();
String alias = aux.nextElement();
X509Certificate certificate = (X509Certificate) keyStore.getCertificate (alias);
String certString = formatCrtFileContents(certificate);
PrintWriter out = new PrintWriter("cert.CER");
out.println(certString);
out.close();
}
public static String formatCrtFileContents(final Certificate certificate) throws CertificateEncodingException {
final Base64.Encoder encoder = Base64.getMimeEncoder(64, LINE_SEPARATOR.getBytes());
final byte[] rawCrtText = certificate.getEncoded();
final String encodedCertText = new String(encoder.encode(rawCrtText));
final String prettified_cert = BEGIN_CERT + LINE_SEPARATOR + encodedCertText + LINE_SEPARATOR + END_CERT;
return prettified_cert;
}
}
This creates the cer file with
-----BEGIN CERTIFICATE-----
data
-----END CERTIFICATE-----
I want to be able to create a PEM Certificate with the private key included, is it possible? If not, why?
I'm not restricted to Java only and free to use any Java API, but preferable with the least user interaction as possible.

Although I don't see it documented, according to the source the SunMSCAPI provider implements only a stub for getEncoded and cannot export Windows privatekey so you can't do this with JCA.
You could of course write JNI or JNA to call Windows CAPI, but that's not simple.
To use existing tools without user interaction you can use Runtime or ProcessBuilder to
run certutil with arguments -exportpfx -user -p password certid filename
run powershell and tell it to select an object in cert:\currentuser\my and invoke the Export('PFX','password') method -- examples for machine rather than user cert here
or in (only) recent powershell use Export-PFXCertificate cmdlet documentation here
and after any of these, extract from pkcs12 to PEM with openssl pkcs12, or if you prefer with Java by:
load the PKCS12 keystore and get the PrivateKey entry
call getEncoded and encode the result in folded (MIME) base64 like you did for the certificate except use -----BEGIN/END PRIVATE KEY-----
Warning: Java produces an unencrypted (PKCS8) privatekey, so make certain no unauthorized user or program ever has access to this file, your disk/filesystem or any backup(s).

A digital certificate doesn't have the private key inside it (the private key is not part of the certificate fields). The certificate and the private key are separate entities, although they're related (one can't exist without the other).
If you take a look at the certificate fields in RFC 5280, you'll see that only the public key is part of it:
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING }
TBSCertificate ::= SEQUENCE {
... lots of fields
subjectPublicKeyInfo SubjectPublicKeyInfo,
... lots of other fields
}
The subjectPublicKeyInfo is the public key, and there's no field for the private key.
That's because certificates are meant to be public (you can have more details on why they're public taking a look at how a Public Key Infrastructure works).
Although the certificate is public, there's always a correspondent private key somewhere, usually held by the certificate's owner (and ideally by no one else).
Anyway, the file you've got (with BEGIN CERTIFICATE and END CERTIFICATE headers) in only the digital certificate (but not the private key).
If you have the private key and the corresponding certificate, you can create a file that contains both. The most common formats for such file are: JKS (also known as Keystore) and PFX.
There are also another "format": the Windows repository (the one you're reading when you do KeyStore.getInstance("Windows-MY")). I don't know exactly in what format its files are, but the KeyStore class abstracts it.
If the private key is present, it will be together with its corresponding certificate, in the same alias. You can check if the key is present with this code:
String alias = aux.nextElement();
if (keyStore.isKeyEntry(alias)) { // alias contains a private key
Key key = keyStore.getKey(alias, "password".toCharArray()); // need to know the password
// key is the private key
// cert is the key's corresponding certificate
X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);
} else if (keyStore.isCertificateEntry(alias)) { // alias doesn't contain a key
X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);
}
After having the key, you can save it to another keystore with the following code:
// create another keystore
KeyStore output = KeyStore.getInstance("JKS");
// "alias" - choose to whatever name you want
// privateKey is the object you've got from keyStore.getKey()
// "password" is the password for this alias
// cert will be stored in the same alias
output.setKeyEntry("alias", privateKey, "password".toCharArray(), new Certificate[] { cert });
// save the keystore to a file
output.store(new FileOutputStream("outputfile.jks"), "keystore password".toCharArray());
The code above creates the file outputfile.jks containing the certificate and the private key.
If you want the file to be a PFX, you can change the code above to:
// PKCS12 == PFX format
KeyStore output = KeyStore.getInstance("PKCS12");
// alternative: in pfx, I think that alias can't have specific passwords
// so you can use this as it doesn't require a password for the alias entry
output.setKeyEntry("alias", privateKey.getEncoded(), new Certificate[] { cert });
// change file extension to ".pfx"
output.store(new FileOutputStream("outputfile.pfx"), "keystore password".toCharArray());

Related

PEMParser bouncy castle read certificate and private key from PEM

I have created a PEMParser object to which I pass a PEM string containing chain certificate, encrypted private key etc. Which is the way to retrieve the decrypted private key and the certificate X509? I have tried with :
pemParser.readObject()
I am able to get a x509CertificateHolder (how to get the certificate from it?) and at the second call an encrypted privatekey info.
Thanks in advance.
assuming that your pem file has two entries first is cert and second is key.
another assumption is that key is of type pkcs8 and it is password protected.
first call to pemParser.readObject() is done with assumption that first entry in pem file is of x509 certificate
second call to pemParser.readObject() is done with assumption that second entry in pem file is of pkcs8 password protected key
variable certificate will contain the x509 certificate and variable finalKey will contain the private key
private void getCertAndKeyFromPemFile(String fileName, String keyPassword) throws Exception {
Security.addProvider(new BouncyCastleProvider());
PEMParser pemParser = new PEMParser(new FileReader(fileName));
JcaX509CertificateConverter x509Converter = new JcaX509CertificateConverter().setProvider(new BouncyCastleProvider());
X509Certificate certificate =x509Converter.getCertificate((X509CertificateHolder) pemParser.readObject());
PKCS8EncryptedPrivateKeyInfo privateKeyInfo = (PKCS8EncryptedPrivateKeyInfo) pemParser.readObject();
InputDecryptorProvider decryptorProvider = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(keyPassword.toCharArray());
PrivateKey finalKey = new JcaPEMKeyConverter().getPrivateKey(privateKeyInfo.decryptPrivateKeyInfo(decryptorProvider));
}

JWT[ RSA ], api owner shared private key

SO I have to write a Rest client for an integration we are working on with one of our client.
They gave us a private key and told us to create a JWT of the json payload.
Key:
"-----BEGIN RSA PRIVATE KEY-----\nYYYYYYYYYYYYYYYYYYYYYYYYYYYYY edited ..YYYYY==\n---
--END RSA PRIVATE KEY-----\n"
Question:
Is it correct to share private key, is there any java example I can use to create JWT using RSA?
Courtesy - https://wstutorial.com/misc/jwt-java-public-key-rsa.html
public String generateJwtToken(PrivateKey privateKey) {
String token = Jwts.builder().setSubject("adam")
.setExpiration(new Date(2018, 1, 1))
.setIssuer("info#wstutorial.com")
.claim("groups", new String[] { "user", "admin" })
// RS256 with privateKey
.signWith(SignatureAlgorithm.RS256, privateKey).compact();
return token;
}
No, it's not correct to share a private key. Never.
It's called private for a reason.
It is also not correct to create a token on client side.
The only correct thing here is, that you would need the private key to sign the token, but that's not the task of the client. It defeats the whole purpose of a JWT, because you could write into it (e.g. roles, expiration time), whatever you want. It seems the API owner trusts you, and probably it's an API that is not public, but anyway, I would recommend to do it in the right way. They should implement an endpoint to request a token.
Usage examples can usually be found on the websites of the jwt libraries.
RSA keys are public / private key pairs. The private key can be used for signing JWTs, and the public key can be used to verify the signature of those JWTs.
The private key should not be shared with anyone. Doing so would allow random people to access your client API.
I've had good experience using Nimbus to sign JWT with RSA. You can see some examples here: https://connect2id.com/products/nimbus-jose-jwt/examples/jwt-with-rsa-signature.
better late than never
It’s generally a bad idea to share private key. But let's assume you own the private key and you want to use it instead to generate a Keypair each time.
Here an example how to use an existing private key, which is stored in P12 file.
#Test
public void readP12() {
char[] keyStorePassword = "1234567890".toCharArray();
try {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
InputStream keyStoreData = new FileInputStream(LOCATION + "\\keystore.p12");
keyStore.load(keyStoreData, keyStorePassword);
KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(keyStorePassword);
KeyStore.Entry keyEntry = keyStore.getEntry("1", entryPassword);
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)
keyStore.getEntry("1", entryPassword);
PrivateKey privateKey = privateKeyEntry.getPrivateKey();
PublicKey publicKey = privateKeyEntry.getCertificate().getPublicKey();
String token = generateJwtToken(privateKey);
System.out.println(token);
printStructure(token, publicKey);
} catch (Exception e) {
// tbd
}
}
You can see how to save private key in p12 file

extract stored passphrase from the oracle keystore

Our customer complains about stored encryption key in the Tomcat context.xml as plain text (well, he is definitely right at this point).
And he wants to use external keystore to store this encryption key.
I was able to create a keystore and put a symmetric key in there with following command:
keytool -importpassword -alias encryption-key -keystore your.keystore -storetype pkcs12
This keystore has the 'PSCS12' type and, actually, can store symmetric keys.
My stored password has an alias, which is 'encryption-key'.
'your.keystore' is a keystore file.
But i have a problem - i can not extract it.
If i will try to extract if from the java code - then i will need to provide salt and iterations count, like this:
final SecretKey secretKey = (SecretKey) keyStore.getKey(alias, password.toCharArray());
System.out.println("[*] Encryption algorithm: " + secretKey.getAlgorithm());
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
AlgorithmParameterSpec algorithmParameterSpec = new PBEParameterSpec(SALT, ITERATION_COUNT);
cipher.init(Cipher.DECRYPT_MODE, secretKey, algorithmParameterSpec);
String decryptedData = Arrays.toString(cipher.doFinal(secretKey.getEncoded()));
System.out.println("Decrypted Key: " + decryptedData);
But i'm not sure which values i should provide to it, because i was storing my passphrase using the command line.
Encryption algorithm that are being used is PBEWithMD5AndDES.
I can see my stored passphrase in a debugger session, i can actually see even a passphrase length, but i can not decrypt it.
So, what are my options here? Customer wants to have a standard implementation (JCA).
How can i extract my passphrase that was generated with a command above?
forget it, i'm stupid. it turns out that i always had the right value, it just was in the HEX format.
So, if you want to have a keystore and put there some value (just a string, not keys pair), then you will need to:
$ keytool -importpassword -alias encryption-key -keystore your.keystore -storetype pkcs12 -storepass testtest # create a keystore and store a single value
where -importpassword is used to store single passphrase
-alias is an alias for your passphrase
-keystore is a keystore file obviously
- storetype pkcs12 is used to store symmetric key (just a passphrase, not a key pair)
-storepass is a password for your keystore (not for your passphrase)
Then you can use following code example to extract your key:
import javax.crypto.SecretKey;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
public class Main {
private static final String WORKING_DIRECTORY = "/path/to/directory/where/keystore/is/placed/";
private static final String FILE_NAME = "your.keystore";
private static final String KEYSTORE_PASSWORD = "testtest";
private static final String SECRET_KEY_ALIAS = "encryption-key";
public static void main(String[] argv) throws Exception {
final FileInputStream is = new FileInputStream(WORKING_DIRECTORY + FILE_NAME); // load a keystore from file
final KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); // initialize a keystore
keystore.load(is, KEYSTORE_PASSWORD.toCharArray()); // authorize in the keystore
extract(SECRET_KEY_ALIAS, KEYSTORE_PASSWORD, keystore); // extract stored password from the keystore
}
static void extract(final String alias, final String password, final KeyStore keyStore) throws Exception {
final SecretKey secretKey = (SecretKey) keyStore.getKey(alias, password.toCharArray());
System.out.println("[*] Encryption algorithm: " + secretKey.getAlgorithm());
System.out.println("[*] Converting stored key from HEX to string");
System.out.println("[+] Stored key: " + new String(secretKey.getEncoded(), StandardCharsets.UTF_8));
}
}

Correctly creating a new certificate with an intermediate certificate using bouny castle

So my problem is as follows,
Basically I want to create a certificate chain using bouncy castle (jdk16 version 1.46). I am rather new to bouncy castle and java.security in general so if my approach might be completely wrong, but anyway this is what I did:
So far I am able to create a self signed certificate which I use as the root certificate. This is done using the following code:
//-----create CA certificate with key
KeyPair caPair = Signing.generateKeyPair("DSA", 1024, null, null);
This basically creates the keypair, the two null options are for a provider and a secure random, if needed.
Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> caMap = new HashMap<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>>();
caMap.put(X509Extensions.BasicConstraints, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(true, new BasicConstraints(true)));
//------this creates the self signed certificate
X509Certificate caCert = X509CertificateGenerator.generateX509Certificate(serial, "CN=CA", "CN=CA", start, end, "SHA1withDSA", caPair.getPrivate(), caPair.getPublic(), null, caMap);
This will create the a certificate with the provided attributes.
serial = simply the current time in milliseconds
start = same as serial basically (may have 1 or 2 milliseconds difference)
end = start + 2 days
The map simply adds the basic contraint to set the certificate to be a CA. I use a map here since I want to be able to add additional X509Extensions if need be.
//-----save ca certificate in PEM format
X509CertificateGenerator.savePemX509Certificate(caCert, caPair.getPrivate(), caWriter);
This will store the certificate and private key in a pem file using the bouncy caste pem writer.
After that the file is generated and I can install the file as well (I use IE and then install it via the Internet Options as a trusted CA. The certificate is also shown to be valid).
After that I create the intermediate certificate, using the following code (note the above code is in the same scope so those variables are available as well)
KeyPair intermediatePair = Signing.generateKeyPair("DSA", 1024, null, null);
Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> intermediateMap = new HashMap<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>>();
intermediateMap.put(X509Extensions.AuthorityKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new AuthorityKeyIdentifierStructure(caCert)));
intermediateMap.put(X509Extensions.SubjectKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new SubjectKeyIdentifierStructure(intermediatePair.getPublic())));
X509Certificate intermediateCert = X509CertificateGenerator.generateX509Certificate(serial.add(BigInteger.valueOf(1l)), "CN=intermediate", caCert.getSubjectX500Principal().toString(), start, end, "SHA1withDSA", caPair.getPrivate(), intermediatePair.getPublic(), null, intermediateMap);
//-----save intermediate certificate in PEM format
X509CertificateGenerator.savePemX509Certificate(intermediateCert, intermediatePair.getPrivate(), intermediateWriter);
The procedure is bascially the same, however I add additional X509Extensions:
X509Extensions.AuthorityKeyIdentifier = sets the CA certificate as the intermediates parent
X509Extensions.SubjectKeyIdentifier = uses the generates public key for the certificate
furthermore the CA is used as the issuer and the CA private key is used to create the intermediate certificate.
This also works and I can install the intermediate certificate (using IE again), it is also shown that the parent certififcate is the generated CA certificate and that the certificate is valid.
Now comes the tricky part where I am making a mistake I guess. I now create a new certificate using the intermediate certificate, using the following code.
KeyPair endPair = Signing.generateKeyPair("DSA", 1024, null, null);
Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> endMap = new HashMap<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>>();
endMap.put(X509Extensions.AuthorityKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new AuthorityKeyIdentifierStructure(intermediateCert)));
endMap.put(X509Extensions.SubjectKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new SubjectKeyIdentifierStructure(endPair.getPublic())));
X509Certificate endCert = X509CertificateGenerator.generateX509Certificate(serial.add(BigInteger.valueOf(1l)), "CN=end", intermediateCert.getSubjectX500Principal().toString(), start, end, "SHA1withDSA", intermediatePair.getPrivate(), endPair.getPublic(), null, endMap);
X509CertificateGenerator.savePemX509Certificate(endCert, endPair.getPrivate(), endWriter);
Essentially it is the same as creating the intermediate certificate. However I now use the following X509Extension settings:
X509Extensions.AuthorityKeyIdentifier = sets the intermediate certificate as the certificates parent
X509Extensions.SubjectKeyIdentifier = uses the generates public key for the certificate
Also the intermediate certificate is used as the issuer and its private key is used to create the certificate.
I can also install the new certificate but when I examine if (again IE), it shows that the certificate is however invalid because "This CA is either not entitled to issue certificates or the certificate can not be used as an end-entity."
So I somehow need to enable the intermediate certificate to be able to create new certificates as well, by adding some KeyUsages/ExtendedKeyUsage I assume.
Does someone know how I enable the intermediate certificate to do what I need it to do or if I do something wrong in general ?
EDIT 1:
So okay I forgot to provide the code for the method which created the certificate and the one that saved it in PEM format (I renamed it to savePemX509Certificate since the old one was misguiding).
Code for the certificate generation:
public static X509Certificate generateX509Certificate(BigInteger serialnumber, String subject, String issuer, Date start , Date end, String signAlgorithm, PrivateKey privateKey, PublicKey publicKey, String provider, Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> map) throws CertificateEncodingException, InvalidKeyException, IllegalStateException, NoSuchProviderException, NoSuchAlgorithmException, SignatureException
{
if(serialnumber!=null && subject!=null && issuer!=null && start!=null && end!=null && signAlgorithm !=null && privateKey!=null && publicKey!=null)
{
//-----GENERATE THE X509 CERTIFICATE
X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
X509Principal dnSubject = new X509Principal(subject);
X509Principal dnIssuer = new X509Principal(issuer);
certGen.setSerialNumber(serialnumber);
certGen.setSubjectDN(dnSubject);
certGen.setIssuerDN(dnIssuer);
certGen.setNotBefore(start);
certGen.setNotAfter(end);
certGen.setPublicKey(publicKey);
certGen.setSignatureAlgorithm(signAlgorithm);
//-----insert extension if needed
if(map!=null)
for(ASN1ObjectIdentifier extension : map.keySet())
certGen.addExtension(extension, map.get(extension).getKey(), map.get(extension).getValue());
return certGen.generate(privateKey, provider);
}
return null;
}
Code for the saveing of the certificate and key:
public static boolean savePemX509Certificate(X509Certificate cert, PrivateKey key, Writer writer) throws NoSuchAlgorithmException, NoSuchProviderException, CertificateEncodingException, SignatureException, InvalidKeyException, IOException
{
if(cert!=null && key!=null && writer!=null)
{
PEMWriter pemWriter = new PEMWriter(writer);
pemWriter.writeObject(cert);
pemWriter.flush();
if(key!=null)
{
pemWriter.writeObject(key);
pemWriter.flush();
}
pemWriter.close();
return true;
}
return false;
}
As you can see I basically put the certificate and the key in the file, thats all. The result is the following and seems good to me.
-----BEGIN CERTIFICATE-----
MIICdjCCAjagAwIBAgIGAUDuXLRLMAkGByqGSM44BAMwDTELMAkGA1UEAwwCQ0Ew
HhcNMTMwOTA1MTM0MzA3WhcNMTMwOTA3MTM0MzA3WjANMQswCQYDVQQDDAJDQTCC
AbcwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADD
Hj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gE
exAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/Ii
Axmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4
V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozI
puE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4Vrl
nwaSi2ZegHtVJWQBTDv+z0kqA4GEAAKBgAeFoGATLbIr8+QNuxcbYJ7RhbefKWSC
Br67Pp4Ynikxx8FZN4kCjGX7pwT1KffN3gta7jxIXNM5G3IFbs4XnYljh5TbdnjP
9Ge3kxpwncsbMQfCqIwHh8T5gh55KaxH7yYV2mrtEEqj7NBL4thQhJe2WGwgkB9U
NxNmLoMq3m4poyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAJ
BgcqhkjOOAQDAy8AMCwCFFm5ybLY09y8y2uGsEnpceffy2KaAhQIyshgy3ohCLxQ
q3CmnvC+cfT2VQ==
-----END CERTIFICATE-----
-----BEGIN DSA PRIVATE KEY-----
MIIBuwIBAAKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR
+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb
+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdg
UI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlX
TAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCj
rh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQB
TDv+z0kqAoGAB4WgYBMtsivz5A27FxtgntGFt58pZIIGvrs+nhieKTHHwVk3iQKM
ZfunBPUp983eC1ruPEhc0zkbcgVuzhediWOHlNt2eM/0Z7eTGnCdyxsxB8KojAeH
xPmCHnkprEfvJhXaau0QSqPs0Evi2FCEl7ZYbCCQH1Q3E2YugyrebikCFDJCJHtt
NWB4LWYc4y4QvJ/l46ap
-----END DSA PRIVATE KEY-----
So after gtrig provided me with the correct way to create the certificate, I ended up using this method to create either a normal or self signed (if the private key is from the same keyPair as the public key that is) certificate
public static X509Certificate createX509V3Certificate(X500Principal name, BigInteger serial, Date start, Date end, PublicKey pubKey, String algorithm, PrivateKey privateKey, Map<ASN1ObjectIdentifier, Entry<Boolean, ASN1Object>> map, X509Certificate parentCert) throws IOException, OperatorCreationException, CertificateException
{
if(serial!=null && start!=null && end!=null && name!=null && pubKey!=null && algorithm!=null && privateKey!=null)
{
ContentSigner signer = new JcaContentSignerBuilder(algorithm).build(privateKey);
X509v3CertificateBuilder certBldr = null;
if(parentCert==null)
certBldr = new JcaX509v3CertificateBuilder(name, serial, start, end, name, pubKey);
else
certBldr = new JcaX509v3CertificateBuilder(parentCert, serial, start, end, name, pubKey);
if(map!=null)
for(ASN1ObjectIdentifier extension : map.keySet())
certBldr.addExtension(extension, map.get(extension).getKey(), map.get(extension).getValue());
return new JcaX509CertificateConverter().setProvider("BC").getCertificate(certBldr.build(signer));
}
return null;
}
Something looks wrong with the way you're creating the PEM files. You're using a method called, generateSelfSignedPemX509Certificate, but you don't really want a self-signed certificate, you want an end certificate signed by the intermediate private key, and you want an intermediate certificate signed by the CA private key.
Also, you need basic constraints and key usage extensions on your certificates.
For creating certificates signed by other entities (non-self-signed), I use these methods from Bouncy Castle to create an "end" certificate.
ASN1Sequence seq=
(ASN1Sequence) new ASN1InputStream(parentPubKey.getEncoded()).readObject();
SubjectPublicKeyInfo parentPubKeyInfo = new SubjectPublicKeyInfo(seq);
ContentSigner signer = new JcaContentSignerBuilder(algorithm).build(parentPrivKey);
X509v3CertificateBuilder certBldr =
new JcaX509v3CertificateBuilder(
parentCert,
serialNum,
startDate,
endDate,
distName,
pubKey)
.addExtension(
new ASN1ObjectIdentifier("2.5.29.35"),
false,
new AuthorityKeyIdentifier(parentPubKeyInfo))
.addExtension(
new ASN1ObjectIdentifier("2.5.29.19"),
false,
new BasicConstraints(false)) // true if it is allowed to sign other certs
.addExtension(
new ASN1ObjectIdentifier("2.5.29.15"),
true,
new X509KeyUsage(
X509KeyUsage.digitalSignature |
X509KeyUsage.nonRepudiation |
X509KeyUsage.keyEncipherment |
X509KeyUsage.dataEncipherment));
// Build/sign the certificate.
X509CertificateHolder certHolder = certBldr.build(signer);
X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC)
.getCertificate(certHolder);
For a CA or intermediate certificate, you'll need to add a SubjectKeyIdentifier extension. Also, BasicConstraints should be true, and KeyUsage should be:
new X509KeyUsage(
X509KeyUsage.keyCertSign|
X509KeyUsage.cRLSign));

Store PGP (public) keys in java keystore - Bouncycastle

I am using bouncycastle (JAVA) for signing, encryption, decryption and signatures' verification in implementation of SSO.
I have raw PGP public and private keys and I need to store them in Java keystore.
These PGP public keys have no certificate.
I understand that for public keys (according to javadoc of Keystore: http://docs.oracle.com/javase/6/docs/api/java/security/KeyStore.html) I have to create certificate. Once certificate is created I can import it to the keystore as KeyStore.TrustedCertificateEntry.
However, I am not able to create certificate entry for type org.bouncycastle.openpgp.PGPPublicKey.
I have searched through the web but could not find any valid example:
Bouncycastle documentation: http://www.bouncycastle.org/wiki/display/JA1/X.509+Public+Key+Certificate+and+Certification+Request+Generation
Generates certificate for X.509 keys -
Bouncycastle examples - org.bouncycastle.openpgp.examples.DirectKeySignature:
Add certificat (object of type PGPSignature) directly to the PGPPublicKey.
To conclude - I have signed (certified) PGPPublicKey but I am not able to store this type of Key into the java keystore.
OutputStream out = new ByteArrayOutputStream();
if (armor)
{
out = new ArmoredOutputStream(out);
}
PGPPrivateKey pgpPrivKey = secretKey.extractPrivateKey(secretKeyPass.toCharArray(), "BC");
PGPSignatureGenerator sGen = new PGPSignatureGenerator(secretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1, "BC");
sGen.initSign(PGPSignature.DIRECT_KEY, pgpPrivKey);
BCPGOutputStream bOut = new BCPGOutputStream(out);
sGen.generateOnePassVersion(false).encode(bOut);
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
boolean isHumanReadable = true;
spGen.setNotationData(true, isHumanReadable, notationName, notationValue);
PGPSignatureSubpacketVector packetVector = spGen.generate();
sGen.setHashedSubpackets(packetVector);
bOut.flush();
return PGPPublicKey.addCertification(keyToBeSigned, sGen.generate()).getEncoded();
I am mainly interested in programatic solution (java source code) but examples that use some tools will be helpful too.
Thanks!
I think you should extract a java.security.PublicKey from your PGPPublicKey and use that to construct an X509Certificate which can be stored in a keystore.
JcaPGPKeyConverter c = new JcaPGPKeyConverter();
PublicKey publicKey = c.getPublicKey(pgpPublicKey);
// ... Use Bouncy's X509V3CertificateGenerator or X509v3CertificateBuilder
// ... to construct a self-signed cert
X509Certificate x509Certificate = // ...
// ... add cert to KeyStore
To create an X509Certificate from a PublicKey see: Generate random certificates.
If you only want to save the public key, why cannot you just save the key content into Java keystore? Then retrieve the content and convert into a PGPPublicKey object when you need it.
Create a wrapper class first
public class PgpPublicKeyWrapper implements Key {
private final String keyContent;
public PgpPublicKeyWrapper(final String keyContent) {
this.keyContent = keyContent;
}
#Override
public String getAlgorithm() {
return "PGP-PublicKey"; // you can call whatever you want
}
#Override
public String getFormat() {
return "RAW"; // has to be raw format
}
#Override
public byte[] getEncoded() {
return keyContent.getBytes();
}
}
Then you can do this to save it
keyStore.setKeyEntry("think a name for alias", new PgpPublicKeyWrapper(key), PASSWORD, null);
When you want to retrieve it
Key key = this.keyStore.getKey(alias, PASSWORD);
InputStream is = new ByteArrayInputStream(key.getEncoded());
PGPPublicKey publicKey = readPublicKey(is);
For readPublicKey(), you can find a lot of examples online about how to read InputStream to a PGPPublicKey object.

Categories

Resources