How to use java to generate csr from exist keystore - java

How to use java code to generate csr from exist keystore?
The function affect would be as same as(but not genearate the file)
keytool -certreq -alias certificate_alias -keystore jssecacerts -storepass changeit -file client.csr
I just found out "Generating a Certificate Signing Request using Java API"
But I already have X.509 certificate, how can I use this certificate to generate csr in java?
KeyStore ts = KeyStore.getInstance("JKS");
FileInputStream is = new FileInputStream(trustStoreFileName);
ts.load(is, trustStorePassword.toCharArray());
is.close();
X509Certificate x509Cert = (X509Certificate)ts.getCertificate("certificate_alias");
How can I use above info to generate CSR?
I Just solve it~
To share all my code to generate csr from exist certificate.
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream is = new FileInputStream(trustStoreFileName);
ks.load(is, trustStorePassword.toCharArray());
is.close();
X509Certificate x509Cert = (X509Certificate)ks.getCertificate("certificate_alias");
X500Principal principal = x509Cert.getSubjectX500Principal();
X500Name x500Name = new X500Name( principal.getName() );
PublicKey publicKey = x509Cert.getPublicKey();
PrivateKey privateKey = (PrivateKey) ks.getKey("certificate_alias", trustStorePassword.toCharArray());
String sigAlg = x509Cert.getSigAlgName();
PKCS10 pkcs10 = new PKCS10(publicKey);
Signature signature = Signature.getInstance(sigAlg);
signature.initSign(privateKey);
pkcs10.encodeAndSign(new X500Signer(signature, x500Name));
ByteArrayOutputStream bs = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(bs);
pkcs10.print(ps);
byte[] c = bs.toByteArray();
try {
if (ps != null)
ps.close();
if (bs != null)
bs.close();
} catch (Throwable th) {
}

You need the public key from certificate and the private key to sign the CSR. A JKS can contain x509 certificates and key pairs. So, ensure you have it
PrivateKey privateKey = ts.getPrivateKey("certificate_alias");
Once the CSR is signed, the CA will issue a new X509Certificate. But is not usual to reuse existing keys ( that could have been compromised) to issue a new certificate. It is recommended to generate a new key pair

Related

java - How to generate PrivateKey and PublicKey starting from a keystore (.p12)

Generate some keys with OpenSSL, then encode them in Base64 and obtain them and try to generate them to validate the authentication with JWT. Here is the code and description of what happens to me
Generate with the following commands:
openssl req -x509 -newkey rsa:4096 -keyout private_key.pem -out public_key.der
openssl pkcs12 -export -out keyStore.p12 -inkey private_key.pem -in public_key.der
base64 –w 0 private_key.pem > private_key_base64_enc.txt
base64 –w 0 public_key.der > public_key_base64_enc.txt
I saved in my vault.keystore from wildfly: private_key_base64_enc.txt and public_key_base64_enc.txt
Then in my java class I write the following:
private void jwtSignedAuthentication(String token, PropName vaultBlockName) throws Exception
{
String rsa512Alias = vaultBlockName.getDefaultValue();
String rsa512pvt = VaultReader.getValue(rsa512Alias, "privateKey");
String rsa512pbc = VaultReader.getValue(rsa512Alias, "publicKey");
KeyFactory keyfatc = null;
PrivateKey privateKey = null;
PublicKey publicKey = null;
try {
keyfatc = KeyFactory.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
logger.error(e);
}
StringBuilder pkcs8Lines = new StringBuilder();
BufferedReader rdr = new BufferedReader(new StringReader(new String(Base64.getDecoder().decode(rsa512pvt.getBytes()))));
String line;
while ((line = rdr.readLine()) != null) {
pkcs8Lines.append(line);
}
// Remove the "BEGIN" and "END" lines, as well as any whitespace
String pkcs8Pem = pkcs8Lines.toString();
pkcs8Pem = pkcs8Pem.replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "");
pkcs8Pem = pkcs8Pem.replace("-----END ENCRYPTED PRIVATE KEY-----", "");
pkcs8Pem = pkcs8Pem.replaceAll("\\s+","");
byte[] dataPvt = Base64.getDecoder().decode(pkcs8Pem.getBytes());
PKCS8EncodedKeySpec specPvt = new PKCS8EncodedKeySpec(dataPvt);
byte[] dataPbc = Base64.getDecoder().decode(rsa512pbc.getBytes());
StringBuilder publicLinesBuilder = new StringBuilder();
BufferedReader readerPlubKey = new BufferedReader(new StringReader(new String(dataPbc)));
String lineP;
while ((lineP = readerPlubKey.readLine()) != null) {
publicLinesBuilder.append(lineP);
}
String pubK = publicLinesBuilder.toString();
pubK = pubK.replace("-----BEGIN CERTIFICATE-----", "");
pubK = pubK.replace("-----END CERTIFICATE-----", "");
pubK = pubK.replaceAll("\\s+","");
X509EncodedKeySpec specPbc = new X509EncodedKeySpec(Base64.getDecoder().decode(pubK.getBytes()));
try {
privateKey = keyfatc.generatePrivate(specPvt);
publicKey = keyfatc.generatePublic(specPbc);
} catch (InvalidKeySpecException e) {
logger.error(e);
}
Algorithm algorithm = Algorithm.RSA512((RSAPublicKey) publicKey, (RSAPrivateKey) privateKey);
// Creación de un verificador JWT
JWTVerifier verifier = JWT.require(algorithm).withIssuer(JWT_CLAIM_ISSUER).acceptLeeway(2).build();
UserContext userContext = new UserContext();
userContext.setUserName(JWT_CLAIM_ISSUER);
try {
// Decode JWT, verificación del token.
#SuppressWarnings("unused")
DecodedJWT decodeJwt = verifier.verify(token);
} catch (JWTDecodeException e) {
logger.error(e);
}
}
When I try to generate the keys I return null:
privateKey = keyfatc.generatePrivate(specPvt);
publicKey = keyfatc.generatePublic(specPbc);
Anyone have any idea what happens with this. Thanks in advance
For generate my JWT:
public ResteasyWebTarget getClientWebAgent(String host, String blockName) throws KeyStoreException
{
ResteasyClient clientBuilder = new ResteasyClientBuilder().establishConnectionTimeout(10, TimeUnit.SECONDS).socketTimeout(5, TimeUnit.SECONDS).build();
ResteasyWebTarget target = clientBuilder.target(host);
KeyPair keys = null;
try {
keys = keyStore.getKeys();
/*logger.infov(new String(Base64.getEncoder().encode(keys.getPrivate().getEncoded())));
logger.infov("****PUBLIC KEY ******");
logger.infov(new String(keys.getPublic().getEncoded()));*/
} catch (IOException e) {
logger.error(e);
}
Algorithm algorithm = Algorithm.RSA512((RSAPublicKey) keys.getPublic(), (RSAPrivateKey) keys.getPrivate());
Map<String, Object> headerClaims = new HashMap<>();
headerClaims.put("alg", "RS512");
headerClaims.put("typ", "JWT");
JWTCreator.Builder jwtCreator = JWT.create();
jwtCreator.withHeader(headerClaims);
jwtCreator.withIssuer(JWT_CLAIM_ISSUER);
jwtCreator.withIssuedAt(LocalDate.now().toDate());
jwtCreator.withExpiresAt(LocalDate.now().toDateTimeAtCurrentTime().plusSeconds(30).toDate());
String jwtToken = jwtCreator.sign(algorithm);
target.register(new BearerAuthenticator(jwtToken));
target.register(new LanguageHeaderToken(Locale.getDefault()));
return target;
}
Your 'public key' is actually a certificate (specifically an X.509 v1 or v3 certificate, depending on your openssl config), which contains a publickey but is different from a publickey -- and is in PEM format even though you have misleadingly named it .der -- and your privatekey is encrypted.
In addition to the approach of using a PKCS12, as Roberto validly proposes and is usually the simplest because it's only one file to manage and is still encrypted and thus more secure:
Java can handle an X.509 certificate, but you use a CertificateFactory.getInstance("X.509") and give it an InputStream instead of a KeyFactory and an X509EncodedKeySpec. CertificateFactory can handle either PEM or DER, unlike KeyFactory which can handle only DER, so you don't need the de-PEM (strip BEGIN/END/EOL and decode base64) parts.
standard Java cannot handle encrypted PKCS8 keys directly. If you can add a thirdparty library, BouncyCastle's bcpkix can do so; search the dozen or so existing Qs that use PEMParser (not PEMReader, that's the older version) and JceOpenSSLPKCS8DecryptorBuilder. Otherwise, you can add -nodes to your req -newkey -x509 command to generate an unencrypted privatekey file, which after you de-PEM it does work in KeyFactory with PKCS8EncodedKeySpec. (It's still spelled -nodes even though the encryption used without it hasn't been plain aka single DES for decades.) Using an unencrypted privatekey file of course means that any intruder or malware on your system that can read that file can get your privatekey, which in many situations is a risk.
finally, if you really want only the keypair and not a certificate, don't bother with req -newkey -x509. Instead use openssl genpkey to generate the privatekey, or the older but simpler openssl genrsa -nodes followed by (or piped to) openssl pkcs8 -topk8 -nocrypt to convert it to PKCS8-unencrypted format. Then use openssl pkey -pubout or the older openssl rsa -pubout to make a separate file with the publickey. Those commands can write (and read back where applicable) DER format instead of PEM; if you do that, your code doesn't need the de-PEM steps, you can just pass the binary file contents to KeyFactory. The risks for an unencrypted file are the same as above.
Maybe you are generating the keystore without assigning a valid alias, looking at your command you are not using the -name option.
The command should be like this:
openssl pkcs12 -export -out keyStore.p12 -inkey private_key.pem -in public_key.der -name "alias"
A smarter way to use the keys in java is by creating a KeyPair:
KeyPair loadKeyPair() throws Exception {
// Read keystore from resource folder
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL resource = classLoader.getResource("keyStore.p12");
File file = new File(Objects.requireNonNull(resource).toURI());
char[] keyPass = "1234".toCharArray();
String alias = "alias";
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
try (FileInputStream is = new FileInputStream(file)) {
keystore.load(is, keyPass);
}
Key key = keystore.getKey(alias, keyPass);
if (key instanceof PrivateKey) {
// Get certificate of public key
Certificate cert = keystore.getCertificate(alias);
// Get public key
PublicKey publicKey = cert.getPublicKey();
// Return a key pair
return new KeyPair(publicKey, (PrivateKey) key);
}
return null;
}
Then extract RSAPublicKey and RSAPrivateKey keys from the KeyPair:
void loadKeys() throws Exception{
KeyPair keyPair = loadKeyPair();
if (null != keyPair) {
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
}
}
Hope it can be helpful and good luck with your Json Web Tokens! :-p

How to convert x509 Cert and Key to a pkcs12 file

To convert a pem file containing a x509 certificate + private key into a pkcs12 (.p12) file, the following command is being used:
openssl pkcs12 -export -inkey cert_pkey.pem -in cert_pkey.pem -out cert.p12
I am trying to accomplish the same programatically using Java with BouncyCastle library. I am able to extract the X509Cert from the PEMObject but the Private key has been confusing.
Any help in piecing together the steps is appreciated:
Open cert_pkey.pem file stream using PEMParser
Get the X509 Certificate from PemObject (done)
Get the private key from the PemObject (how?)
Create KeyStore of instance type PKCS12 with password
Finally got around how to get the cert and key separately - not sure why it worked out the way it worked out:
PEMParser pemParser = new PEMParser(new BufferedReader(new InputStreamReader(certStream)));
Object pemCertObj = pemParser.readObject();
PemObject pemKeyObj = pemParser.readPemObject();
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(pemKeyObj.getContent());
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privKey = kf.generatePrivate(privKeySpec);
Security.addProvider(new BouncyCastleProvider());
X509CertificateHolder certHolder = (X509CertificateHolder)pemCertObj;
X509Certificate x509cert = (new JcaX509CertificateConverter()).setProvider("BC").getCertificate(certHolder);
I got the hint when I looked up the .getType() on permCertObj and permKeyObj and got RSA CERT and RSA PRIVATE KEY respectively returned.
Couldn't figure out the difference between readObject() and readPemObject()
The PEMParser class will parse just about anything from PEM format. You can read the object from the file using that parser - if you'll print the class of that object you'l;l see it's a PEMKeyPair. That can be converted to a regular KeyPair using JcaPEMKeyConverter.
public KeyPair importKeyFromPemFile(String filePath)
{
try (FileReader reader = new FileReader(filePath))
{
PEMParser pemParser = new PEMParser(reader);
PEMKeyPair pemKeyPair = (PEMKeyPair)pemParser.readObject()
return new JcaPEMKeyConverter().getKeyPair(pemKeyPair);
}
catch (IOException | PEMException e)
{
throw new RuntimeException(e)
}
}

Signing a X509Certificate with another Self Signed x509Certificate [acting as CA]

I have created a self-signed certificate and encoded it successfully. But I want to sign this certificate with another self signed certificate, which will act as a Certification Authority.
The code is below:
X509Certificate caCert;
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(null, null);
CertAndKeyGen keypair = new CertAndKeyGen("RSA", "SHA1WithRSA", null);
X500Name x500Name = new X500Name(commonName, organizationalUnit, organization, city, state, country);
keypair.generate(keysize);
PrivateKey privKey = keypair.getPrivateKey();
X509Certificate[] chain = new X509Certificate[1];
chain[0] = keypair.getSelfCertificate(x500Name, new Date(), (long) validity * 24 * 60 * 60);
keypair.getCertRequest(x500Name);
keyStore.setKeyEntry(alias, privKey, keyPass, chain);
keyStore.store(new FileOutputStream("test.keystore"), keyPass);
caCert = (X509Certificate) keyStore.getCertificate(alias);
File crtFile = new File("saif.der");
writeCertificate(new FileOutputStream(crtFile), caCert);
Create the user certificate using X509V3CertificateGenerator class of bouncycastle. Then finally use the X509V3CertificateGenerator.generateX509Certificate(privateKey) method to generate the X509Certificate. Here the privateKey will be the self signed certificate's private key from PKCS12. Save the user certificate in PKCS12 format.

Creating a Key Pair Certificate and Signing It with External CA using BouncyCastle

Here what I have so far generating a Certificate for a User
try {
Security.addProvider(new BouncyCastleProvider()); // adding provider
// to
String pathtoSave = "D://sureshtest.cer";
KeyPair keyPair = generateKeypair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
X509Certificate trustCert = createCertificate(null, "CN=CompanyName",
"CN=Owner", publicKey, privateKey);
java.security.cert.Certificate[] outChain = { trustCert, };
trustCert.checkValidity();
KeyStore outStore = KeyStore.getInstance("PKCS12");
outStore.load(null, null);
outStore.setKeyEntry("my own certificate", privateKey,
"admin123".toCharArray(), outChain);
OutputStream outputStream = new FileOutputStream(pathtoSave);
outStore.store(outputStream, "admin123".toCharArray());
outputStream.flush();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
The above code generate a certificate with a private and public key.
Now I want to sign that certificate with a signing certificate I've been issued by a certificate authority (CA). After that I'll grant that certificate to user.
I got some input from here and it seems that is not the required answer with my case.
No need for a full implementation, just a valid procedure or some hints will greatly help.
You need to generate a CSR so you can invoke the code from Sign CSR using Bouncy Castle which is using the BC API. Add this to your code above:
final PKCS10 request = new PKCS10(publicKey);
final String sigAlgName = "SHA1WithRSA"; // change this to SHA1WithDSA if it's a DSA key
final Signature signature = Signature.getInstance(sigAlgName);
signature.initSign(privateKey);
final X500Name subject = new X500Name(trustCert.getSubjectDN().toString());
final X500Signer signer = new X500Signer(signature, subject);
// Sign the request and base-64 encode it
request.encodeAndSign(signer);
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final PrintStream writer = new PrintStream(baos);
request.print(writer);
// Remove -----BEGIN NEW CERTIFICATE REQUEST----- and -----END NEW CERTIFICATE REQUEST-----
final String requestBase64 = new String(baos.toByteArray());
String withoutTags = requestBase64.substring(41);
withoutTags = withoutTags.substring(0, withoutTags.length() - 39);
// org.bouncycastle.pkcs.PKCS10CertificationRequestHolder
final PKCS10CertificationRequest holder = new PKCS10CertificationRequest(Base64.decode(withoutTags));
// Feed this into https://stackoverflow.com/questions/7230330/sign-csr-using-bouncy-castle

Extracting Private key from pkcs12 and text encryption

I have .p12 file, I am extracting the private key using openssl, I have a password for extracting it.
openssl pkcs12 -in my.p12 -nocerts -out privateKey.pem
And after I get my private key, I'm trying to use that key for encryption:
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
KeyPair keyPair = readKeyPair(privateKey, "testpassword".toCharArray());
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] textEncrypted = cipher.doFinal("hello world".getBytes());
System.out.println("encrypted: "+new String(textEncrypted));
cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
byte[] textDecrypted = cipher.doFinal(textEncrypted);
System.out.println("decrypted: "+new String(textDecrypted));
}
private static KeyPair readKeyPair(File privateKey, char[] keyPassword) throws IOException {
FileReader fileReader = new FileReader(privateKey);
PEMReader r = new PEMReader(fileReader, new DefaultPasswordFinder(keyPassword));
try {
return (KeyPair) r.readObject(); // this returns null
} catch (IOException ex) {
throw new IOException("The private key could not be decrypted", ex);
} finally {
r.close();
fileReader.close();
}
}
r.readObject(); returns null. But when I create a private key by myself by this command:
openssl genrsa -out privkey.pem 2048
The above code works fine.
How can I extract private key from p12 file properly?
Or is there any way to use p12 file for encrypt/decrypt the text
without extracting through command line?
I know it is just PKCS#12 is just archaive file which stores keys.
I don't know what is wrong with your code, but I have code that reads stuff from a key store. I read the file into a KeyStore instance and then access the key or entry as appropriate. Here are some of the relevant calls:
char[] password;
String alias;
java.security.KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");
keyStore.load(inputStream, password);
java.security.PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, password);
java.security.keystore.PrivateKeyEntry privateKeyEntry = (PrivateKeyEntry) keyStore.getEntry(alias, new KeyStore.PasswordProtection(password));
To find the alias of the entry you are interested in, I suggest using keytool (comes with JDK):
keytool -list -v -keystore keystore.pkcs12 -storetype pkcs12
You will be prompted for the keystore password and then get information like this:
Keystore type: PKCS12
Keystore provider: SunJSSE
Your keystore contains 1 entry
Alias name: thealias
Creation date: Aug 30, 2013
Entry type: PrivateKeyEntry
Certificate chain length: 2
[... lots of info about the certificates deleted ...]

Categories

Resources