I have created a pkcs12 file having a private key entry without any password.
I am able to access the p12 file using keytool utility without providing any password.
The same I am not able to to programmatically.
When I am trying like below
if( keyStore.isKeyEntry(KEYSTORE_ALIAS)) {
key = (PrivateKey) keyStore.getKey(KEYSTORE_ALIAS,new char[0]);
}
I am getting the below exception
Caused by: javax.crypto.BadPaddingException: Given final block not
properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
at com.sun.crypto.provider.PKCS12PBECipherCore.implDoFinal(PKCS12PBECipherCore.java:355)
at com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndDESede.engineDoFinal(PKCS12PBECipherCore.java:387)
at javax.crypto.Cipher.doFinal(Cipher.java:2087)
at sun.security.pkcs12.PKCS12KeyStore.engineGetKey(PKCS12KeyStore.java:266)
... 2 more
key = (PrivateKey) keyStore.getKey(KEYSTORE_ALIAS,new char[0]);
That's not the correct way to specify no key. Here you are specifying a zero-length key. It isn't the same thing. Try this:
key = (PrivateKey) keyStore.getKey(KEYSTORE_ALIAS, null);
Related
I am trying to store my own secret key in PKCS12 keystore. I tried by using below code:
char[] passArray = "password".toCharArray();// this is key store pass
String key = "test123"; // this is my own secret key
// Loading a Keystore
KeyStore p12KeyStore = KeyStore.getInstance("PKCS12");
p12KeyStore.load(new FileInputStream("testKeyStore.p12"), passArray);
Storing my own secret key like as shown below:
byte [] byteKey = key.getBytes();
SecretKeySpec keySpec = new SecretKeySpec(byteKey, "DSA");
KeyStore.SecretKeyEntry secret = new KeyStore.SecretKeyEntry(keySpec);
KeyStore.ProtectionParameter password = new KeyStore.PasswordProtection(passArray);
p12KeyStore.setEntry("secret-key", secret, password);
but i am not sure whether storing the my own secret key as per best practice or not. and also when trying to get my own key from keystore as it is not getting like as my original secret key.
Key eKey = p12KeyStore.getKey("secret-key", passArray);
Can anyone please help me on this to store & get my own secret in PKCS12 keystore?
Are you sure the code you have above worked? Because DSA is a asymmetric algorithm, not a symmetric algorithm. If you are sure it is a secret key, it has to be AES or DES or DESede (Triple DES). Your above code should have failed.
And answers to your questions:
Just like databases stores data, keys (secret keys and key pairs) and certificates are meant to be stored in a keystore, that's where they should residing in your application. You should be referring/retrieving them from the keystore.
And from what you said in one of the statements, that the key is different from what you had originally when you stored and then retrieved, judging by this, I think the key you have is a Triple DES or a DES key. DES/TripleDES uses parity bits, and the parity bits are corrected by the Java API SecretKeySpec. This causes the key to look different from the original, but it is actually the same. You can read more about the parity bits here.
And as a best practice you shouldn't be storing the secret keys in a PKCS12 Keystore. The internet standard defines that the PKCS12 keystore should contain only one KeyPair entry associated with its certificate chain. Even though it can contain more than one entry, it is ideal to have only one entry, with the key password same as the keystore password. They can store secret keys too, but it is best if you store them in a JCEKS keystore format.
I extracted a Key and its certification chain from a JKS, and now I'm trying to add this key to the Windows Keystore using Java.
To load my JKS I did the following:
String jksPath = "D:\\mykeystore.jks";
KeyStore keystore = KeyStore.getInstance("JKS");
FileInputStream fIn = new FileInputStream(jksPath);
keystore.load(fIn, "12345678".toCharArray());
Then I get the key and the certification chain:
Key key = keystore.getKey("res1", "12345678".toCharArray());
Certificate[] cchain = keystore.getCertificateChain("res1");
So far so good, then I try to add this key to my Windows Keystore:
KeyStore ks = KeyStore.getInstance("Windows-MY");
ks.load(null, null);
ks.setKeyEntry("myKey", key, "12345678".toCharArray(), cchain);
And BOOM:
Exception in thread "main" java.lang.ClassCastException: [Ljava.security.cert.Certificate; cannot be cast to [Ljava.security.cert.X509Certificate;
at sun.security.mscapi.KeyStore.engineSetKeyEntry(KeyStore.java:402)
at sun.security.mscapi.KeyStore$MY.engineSetKeyEntry(KeyStore.java:62)
at java.security.KeyStore.setKeyEntry(KeyStore.java:909)
Exception thrown due to the setKeyEntry call.
P.S: when I use the same syntaxe on a JKS type of Keystore no exception is thrown.
It seems there's a clumsy bit of java code in the implementation of sun.security.mscapi.KeyStore.engineSetKeyEntry(). It tries to convert an array of Certificates ("[Ljava.security.cert.Certificate", notice the prefix in the class name) to an array of X509Certificates (" [Ljava.security.cert.X509Certificate"), which is not something java ever allows you to do with a simple cast expression (e.g. see discussion of a similar mistake).
All I did in a similar situation is to pass the certificate array to the keyStore.setKeyEntry() method call as a X509Certificate array, rather than a simple Certificate array.
I am working with digital certificate and digital signature. We got pfx file from the vendor. We convert this pfx file to java key store and used it to create the digital signature using java program. Now the vendor has etoken hardware. They give me cer file in place pf pfx. I converted cer to jks java key store and used it in my program... My program told me that private key is not there. I have found that there is no private key with cer file. I have talked to vendor about this he told me private key can not be extracted from the etoken.. you must directly access the etoken through program to get the private key. Can anybody tell me how do i access etoken programetically. Is there any java api which is used to access etoken directly. Help me....
Private key can be extracted using PKCS11.
To extract Private key from eToken in java, you need to pass config file to sun.security.pkcs11.SunPKCS11 instance.
Config file must have following properties:
name=<Name of Etoken>
slot=<slot number for etoken>
library=<path of the pckcs11 library(dll) for that etoken>
Following is sample code to extract private key using eToken
PrivateKey privateKey = null;
char password[] = "1234".toCharArray();
Provider userProvider = new sun.security.pkcs11.SunPKCS11("D:\\config.cfg");
ks = KeyStore.getInstance("PKCS11", userProvider);
ks.load(null, password);
Enumeration e = ks.aliases();
String alias=null;
while (e.hasMoreElements())
{
alias = (String) e.nextElement();
privateKey = (PrivateKey) ks.getKey(alias, password);
}
I have a keystore.jks file with multiple certificate including public and private key.
now i want to know how to export all public key certificate into new file all-public-cert.crt file.
This "all-public-cert.crt" file contain only certificate (public key only) . should not contain any private key in this file.
after this i would like to ready this "all-public-cert.crt" file via java code and validate public and private key using challenge response.
kindly guide me or suggest me some reference document or url.
Note : i am able to use any tool like openssl or keytool.
Thanks & Regards,
Gaurav Paliwal
Normally a keystore i.e., your keystore.jks contains private keys and the corresponding X.509 certificates. But the X.509 certificates do NOT include a private key, as you falsely assumed:
I have a keystore.jks file with multiple certificate including public and private key
This "all-public-cert.crt" file contain only certificate (public key only) . should not contain any private key in this file.
This means that your X.509 certificates do only contain public key information, no private key information.
In order to fetch a certificate form your keystore (leaving the error handling aside):
String keystorePath = ....
InputStream is = new FileInputStream(new File(keystorePath));
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(is, "keystorePassword".toCharArray());
Enumeration e = ks.aliases();
Set<Certificate> certs = new HashSet<Certificate>();
while (e.hasMoreElements()) {
Certificate cert = ks.getCertificate((String) enumeration.nextElement());
certs.add(cert);
}
Now you have all certificates in your certs Set. But why do you need to put them all into the all-public-cert.crt.
1.) First you cannot put multiple certificates in one file, and hope that the one file can be used the "normal" way (e.g. double click to open it, import it into other applications, ..). The file will be garbage and can only be read from e.g., your application
2.) Therefore the file extension should not be .crt, it should be .myExportedCertificates or something like that.
I think you just want to store the certificates on the file system in order to use them afterwards. In this case simply use this code (error handling is your job):
String pathToStoreTheCerts = ...
File path = new File(pathToStoreTheCerts);
OutputStream os = null;
for (X509Certificate cert : certs) {
File certFile = new File(path, cert.getSubjectX500Principal().getName() + ".crt");
os = new FileOutputStream(certFile);
os.write(cert.getEncoded());
os.flush();
}
os.close();
I obtain a randomly generated RSA certificate and private key as strings from another service, and I would like to use those strings to create a Java KeyStore. All of the examples I see involve saving these strings to files, using the openssl and keytool command line tools to create the keystore on disk, and then loading the resulting KeyStore into memory (like here). However, it makes more sense for my purposes to create the KeyStore entirely in memory.
To that end, I am trying to use the Java Security API. I am able to convert the certificate string into an instance of the java.security.cert.Certificate class, but I am unable to convert the private key into an instance of java.security.PrivateKey. Here's method I am trying to create:
private PrivateKey generatePrivateKey (String newKey)
throws NoSuchAlgorithmException,
InvalidKeySpecException, IOException {
//Configuring the KeyFactory to use RSA
KeyFactory kf = KeyFactory.getInstance("RSA");
//Convert the key string to a byte array
byte[] keyBytes = newKey.getBytes();
KeySpec ks = new PKCS8EncodedKeySpec(keyBytes);
PrivateKey key = kf.generatePrivate(ks);
return key;
}
Where the value of newKey is something like "-----BEGIN RSA PRIVATE KEY-----\nMIIEow...gNK3x\n-----END RSA PRIVATE KEY-----".
When I run the code, I receive the following exception:
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format
at sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:217)
at java.security.KeyFactory.generatePrivate(KeyFactory.java:372)
... 30 more
Caused by: java.security.InvalidKeyException: invalid key format
at sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:341)
at sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:367)
at sun.security.rsa.RSAPrivateCrtKeyImpl.<init>(RSAPrivateCrtKeyImpl.java:91)
at sun.security.rsa.RSAPrivateCrtKeyImpl.newKey(RSAPrivateCrtKeyImpl.java:75)
at sun.security.rsa.RSAKeyFactory.generatePrivate(RSAKeyFactory.java:316)
at sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:213)
... 32 more
This particular error is very similar to this stackoverflow question, and I would be grateful if that question were resolved, but I also want to know if my higher level goal (creating a JKS programmatically and solely in memory using Java) is feasible, and if so, whether I am on the right path.
you need to decode base64 if your key is in the base64 representation:
KeySpec ks = new PKCS8EncodedKeySpec(Base64.decodeBase64(newKey));
you can use org.apache.commons.codec.binary.Base64 to do this.
if you want to generate keyPair you can you this code:
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
// extract the encoded private key, this is an unencrypted PKCS#8 private key
byte[] encodedprivkey = keyPair.getPrivate().getEncoded();
System.out.println(Base64.encodeBase64String(encodedprivkey));