Private key generation
public PrivateKey getStoredPrivateKey(String filePath) {
PrivateKey privateKey = null;
byte[] keydata = getKeyData(filePath);
PKCS8EncodedKeySpec encodedPrivateKey = new PKCS8EncodedKeySpec(keydata);
KeyFactory keyFactory = null;
try {
keyFactory = KeyFactory.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
try {
System.out.println("hello");
privateKey = keyFactory.generatePrivate(encodedPrivateKey);
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
return privateKey;
}
I am using it here
PrivateKey privateKey = new KryptoUtil().getStoredPrivateKey(privateKeyFilePath);
but its showing error
hello
java.security.spec.InvalidKeySpecException:
java.security.InvalidKeyException: IOException : version mismatch: (supported: 00, parsed: 03
at sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(Unknown Source)
I am passing a (.p12) file in getStoredPrivateKey(String filePath) function.
why its giving error?
P12 is is keystore type where multiple keys and certificates can be stored and a password can be used to protect them. You can search about P12 (PKCS12) on Internet. Your file is P12 file, so most likely it is PKCS12 format file.
To get private key from P12 file use below code. You need below things before calling this code.
filePath. String path (absolute) of P12 file.
filePassword. It is a char[]. Represents password of p12 file.
keyPassword. It is a char[]. Represents password for private key. Most
likely it is same as filePassword.
alias. A String. Represents by which alias a private key stored in P12
archive/keystore.
To check what is the alias of your private key you can use below command
keytool -list -v -keystore <yourfile>.p12 -storetype pkcs12
It will ask for password then print multiple lines. Look for
Entry Type: PrivatKeyEntry
There you will find the alias.
Initialize these variables and then use below code to get private key. You can also get Certificates/Public key associate with this key. Look for API of PrivateKeyEntry
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(new FileInputStream(filePath), filePassword);
PrivateKeyEntry keyEntry = (PrivateKeyEntry) ks.getEntry(alias, new KeyStore.PasswordProtection(keyPassword));
PrivateKey key = privateKeyEntry.getPrivateKey();
Related
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));
}
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
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));
}
}
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)
}
}
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 ...]