I have created a certificate basically straight from the keytool example page:
keytool -genkey -dname "cn=Anything, ou=Anything, o=Anything, c=US" -alias business -keypass kpi135 -keystore C:\mykeystore -storepass ab987c -validity 1095
I am trying to access this certificate and use the private key portion to digitally sign a portion of text to authenticate with a third party. Below is the code I'm attempting:
//Add bouncyCastle as a provider
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(new FileInputStream("C:\\mykeystore"), "ab987c".toCharArray());
Signature sig = Signature.getInstance("MD5withRSA", "BC");
PrivateKey privateKey = (PrivateKey)keystore.getKey("business", "kpi135".toCharArray()); //Exception here
sig.initSign(privateKey);
sig.update("myUID__myNonce".getBytes());
byte[] digitalSignature = sig.sign();
System.out.println("Signature Formulated: " + digitalSignature);
I get the following exception:
java.security.InvalidKeyException: Supplied key (sun.security.provider.DSAPrivat
eKey) is not a RSAPrivateKey instance
at org.bouncycastle.jce.provider.JDKDigestSignature.engineInitSign(Unkno
wn Source)
at java.security.Signature$Delegate.engineInitSign(Signature.java:1095)
at java.security.Signature.initSign(Signature.java:480)
at MainClass.<init>(MainClass.java:15)
at MainClass.main(MainClass.java:28)
I assume it's because I've somehow created the certificate with the wrong type of key, but I'm not sure I'm finding what I need by digging through the keytool page. It does mention that you can apparently generate a key using -keysig RSA and -sigalg RSA however when I try those flags when creating a certificate I get:
keytool error: java.security.NoSuchAlgorithmException: RSA Signature not availab
le
enter code here
Actually it seems you can add "-keyalg RSA" to the keygen command which alleviates the mismatch issue. I was incorrectly trying both -keyalg and -sigalg in the same keygen command. The code above now executes without exceptions.
Related
I am looking to store sensitive passwords in a java keystore using java keytool's importpass. I am using Oracle java version 1.8.0_212, and cannot upgrade from java 8 at the moment.
I have created a PKCS12 keystore with the following command:
keytool -keystore test-keystore.p12 -genkey -storetype PKCS12 -alias test
I then imported a password into the keystore using:
keytool -importpass -storetype pkcs12 -alias protectedPass -keystore test-keystore.p12
This worked, but this uses the encryption algorithm "PBEWithMD5AndDES" by default, which isn't particularly secure. I am now trying to use "PBEWithHmacSHA256AndAES_128" from the Java Security Standard Algorithm Names doc but having issues getting this to work.
I've tried specifying the keyalg like this:
keytool -importpass -keyalg PBEWithHmacSHA256AndAES_128 -storetype pkcs12 -alias protectedPass -keystore test-keystore.p12 -v
and while this doesn't cause an error, it doesn't seem to actually affect the output. The secret is key still generated with PBEWithMD5AndDES:
D:\temp>keytool -importpass -keyalg PBEWithHmacSHA256AndAES_128 -storetype pkcs12 -alias protectedPass -keystore test-keystore.p12 -v
Enter keystore password:
Enter the password to be stored:
Re-enter password:
Generated PBEWithMD5AndDES secret key
[Storing test-keystore.p12]
I can see examples, such as in Java Keystores the Gory Details, of people using KeyStore.PasswordProtection to use algorithms like this, but I wanted to use the keytool if possible.
Am I missing something key here or trying to do something silly?
EDIT:
Tried to do something similar programmatically to figure out what I'm doing wrong and had no luck using PKCS12 keystores with this algorithm. However, jceks seems to work. I think this is to do with it using the SunJCE provider instead. Is there some parameter I am missing to get this algorithm working with pcks12? Or is there some other approach I could take?
I have provided some simple demo code below. If you swap "JCEKS" for "PKCS12" it will throw java.security.NoSuchAlgorithmException: unrecognized algorithm name: PBEWithHmacSHA256AndAES_128.
Demo Code:
static void encrypt() throws Exception {
KeyStore keyStore = KeyStore.getInstance("JCEKS");
keyStore.load(null, "changeit".toCharArray());
KeyStore.PasswordProtection keyStorePP = new KeyStore.PasswordProtection("changeit".toCharArray());
SecretKeyFactory pbeKeyFactory = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_128");
SecretKey pbeKey = pbeKeyFactory.generateSecret(new PBEKeySpec("testpassword".toCharArray()));
keyStore.setEntry(SECRET_KEY_ALIAS, new KeyStore.SecretKeyEntry(
pbeKey), keyStorePP);
FileOutputStream outputStream = new FileOutputStream(FILE_PATH);
keyStore.store(outputStream, "changeit".toCharArray());
}
static void decrypt() throws Exception {
KeyStore keyStore = KeyStore.getInstance("JCEKS");
FileInputStream fileInputStream = new FileInputStream(FILE_PATH);
keyStore.load(fileInputStream, "changeit".toCharArray());
KeyStore.PasswordProtection keyStorePP = new KeyStore.PasswordProtection("changeit".toCharArray());
KeyStore.SecretKeyEntry ske =
(KeyStore.SecretKeyEntry)keyStore.getEntry(SECRET_KEY_ALIAS, keyStorePP);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_128");
PBEKeySpec keySpec = (PBEKeySpec)factory.getKeySpec(
ske.getSecretKey(),
PBEKeySpec.class);
System.out.println(new String(keySpec.getPassword()));
}
You need to keep separate the algorithm a key is for and the way it is protected.
A Java keystore (depending on type, including JCEKS and PKCS12 but not JKS) can contain three kinds of entries. A SecretKeyEntry in general contains a secret key which is typically valid for a particular cipher algorithm, identified in the SecretKey object within the entry. But for password-based algorithms, the 'key' object is actually a generic 'PBEKey' which contains a password and AFAICT can be used for any PBE algorithm. It appears the 'algorithm' for such keys is actually stored as PBEwithMD5andDES, I suspect because that's the only scheme from original PKCS5 (i.e. PBES1) Java implements. However, my Oracle 8u212 keytool says more generically Generated PBE secret key in this case, not PBEwithMD5andDES as you show.
The stored secret key (and stored private key also) can be and normally is 'protected' in the keystore by being encrypted with a password-based encryption algorithm; this 'protection' algorithm is different from and in general not related to the algorithm(s) that the key or password will be used with after retrieval.
keytool -importpass -keyalg PBEWithHmacSHA256AndAES_128 attempts to specify the algorithm the key-really-password is nominally to be used with when retrieved, but not how it is protected, which for PKCS12 in most older versions including yours is actually PBE-SHA1-TripleDES (the same as for privatekeys) -- you can see this, with a little work, using OpenSSL. And in my 8u212 keytool specifying -keyalg fails for PKCS12 with an exception claiming it can't protect because there are no parameters for that algorithm (although I think the problem is serializing, not actually encrypting) much as you say your code does for PKCS12, but Java does have these parameters -- although in a different provider (SunJCE vs SunJSSE) which might be messing it up somehow; as you say JCEKS is in SunJCE.
Similarly your code generates a key to be used with that algorithm, but protected the default way. To change the protection you need to specify it in PasswordProtection, as stated in the page you link; see https://stackoverflow.com/a/47389384/ (by the same author) for an example.
Recent versions of Java -- 8u301 and 11.0.12 up -- have a new feature to configure the protection algorithms for PKCS12 with security properties in the java.security file which is in JRE/lib/security for 8 down or JRE/conf/security for 9 up. This applies to both code you write if not overridden as above, and to keytool (which can't override). However, those versions (except for 12 through 16, which are now EOL) also now default to PBEwithHmacSHA256andAES_256 which is already better than you asked for, so you probably don't need to change anything -- just use 8u301 up.
Note JCEKS uses a Sun-defined PBE-MD5-TripleDES similar to, but not, PBES1, while JKS doesn't support secretkey entries but for privatekey entries uses a deliberately weak (ITAR-friendly back in the 1990s) Sun-custom algorithm; these cannot be changed.
I have created two different DSA keys using java keytool (defaults to 2048 and SHA256, so the 2nd one is explicitly created with length 1024):
keytool -genkey -keystore c:\test\dsa2048.p12 -storetype pkcs12 -storepass password -keypass password -alias dsa2048
keytool -genkey -keystore c:\test\dsa1024.p12 -storetype pkcs12 -storepass password -keypass password -alias dsa1024 -keysize 1024
I then imported these keys into the windows keystore.
When I read the windows keystore, Keystore.aliases() does not return these ids. It works fine when I use RSA as the keyalg, but DSA doesn't work. I am also able to load these ids if I create the keystore from the file system, but I need to load them from the windows cert store.
Is this expected or is there something else I need to do to see them? Is it possible that keytool is creating invalid ids? When I try to sign with these ids in Acrobat I'm getting an error there as well...
import java.io.FileInputStream;
import java.security.KeyStore;
import java.util.Enumeration;
public class WindowsKeyStoreTest
{
public static void main(String [] args) throws Exception
{
windowsKeystore();
}
public static void windowsKeystore() throws Exception
{
KeyStore keyStore = KeyStore.getInstance("Windows-MY");
keyStore.load(null, null);
Enumeration<String> aliases = keyStore.aliases();
while(aliases.hasMoreElements())
{
String alias = aliases.nextElement();
if(keyStore.isKeyEntry(alias))
{
System.out.println(alias);
}
}
}
}
I want to store private key in android KeyStore, but I have problem with KeyStore.getInstance("JKS") is error like this:
java.security.KeyStoreException: JKS not found.
Please help me why it's not know for this instance?
I have file load my keystore: mykeystore.jks
My code as below:
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.genKeyPair();
X509Certificate certificate = generateCertificate(keyPair);
KeyStore ks = KeyStore.getInstance("JKS");
char[] password = "xxxxxx".toCharArray();
try (FileInputStream fis = new FileInputStream("path\\mykeystore.jks")) {
ks.load(fis, password);
}
Certificate[] certChain = new Certificate[1];
certChain[0] = certificate;
ks.setKeyEntry("key1", keyPair.getPrivate(), password, certChain);
} catch(Exception e) {
e.printStackTrace();
}
Result error:
java.security.KeyStoreException: JKS not found
A Java KeyStore (JKS) is not available on Android.
The recommended keystore on android is AndroidKeyStore. For your case, get keystore instance using AndroidKeyStore instead of JKS and set password as null because AndroidKeyStore does not accepts any password:
//...
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
//...
ks.setKeyEntry("key1", keyPair.getPrivate(), null, certChain);
In an AndroidKeyStore, you cannot set password for keys or keystore because keystore file(s) managed by Android system itself. Also, generating keys inside to the AndroidKeyStore instead of importing (eg. calling setKeyEntry) is more secure and recommended way.(In supported devices, Android generates keys in separated secure hardware. Which means, nobody, including app itself cannot export keys from that device)
So, generate keys using KeyGenParameterSpec.Builder instead of KeyPairGenerator.
You can check documentation here: https://developer.android.com/training/articles/keystore
But if you think AndroidKeyStore is still not suitable for you, you can list available keystore types in Android via:
// Iterate in security providers
for(Provider provider: Security.getProviders()) {
for(Object item: provider.keySet()) {
if(item.toString().startsWith("KeyStore.")) { // grep KeyStores
Log.d(TAG, "Keystore: " + item.toString() + " available in provider: " + provider.getName());
}
}
}
In an Android Emulator Pixel 2 API 28 this code outputs:
Keystore: KeyStore.BouncyCastle available in provider: BC
Keystore: KeyStore.PKCS12 available in provider: BC
Keystore: KeyStore.BKS available in provider: BC
Keystore: KeyStore.AndroidCAStore available in provider: HarmonyJSSE
Keystore: KeyStore.AndroidKeyStore available in provider: AndroidKeyStore
Which means, available KeyStore's are:
BouncyCastle
PKCS12
BKS
AndroidCAStore
AndroidKeyStore
I want to get privatekey from BKS store by program but i found it is difference between my parse and the pem file.
Any body know why is that?
Here is my code:
KeyStore testkeyStore = KeyStore.getInstance("bks");
testkeyStore.load(inputstream, keystorepass);
PrivateKey key = (PrivateKey)testkeyStore .getKey("xxxxx",keypassword);
byte[] bas = key.getEncoded();
bas = Base64.encode(bas, Base64.DEFAULT);
String keys = new String(bas);
Log.d("XXX","keys " + keys);
Here is how i transfer bks to pem:
keytool -importkeystore -srckeystore xxx.bks -srcstoretype BKS -destkeystore xx.p12 -deststoretype PKCS12 -provider org.bouncycastle.jce.provider.BouncyCastleProvider
openssl pkcs12 -in xxx.p12 -out xxx.pem
I found answer after i traced code of openssl project.
The private key was encrypted, here is the code:
https://github.com/openssl/openssl/blob/master/apps/pkcs12.c
I created Java keystore programmatically of type jks (i.e. default type).
It is initially empty so I created a DSA certificate.
keytool -genkey -alias myCert -v -keystore trivial.keystore
How can I see the public and private keys?
I.e. is there a command that prints the private key of my certificate?
I could only find keytool -certreq which in my understanding prints the certificate as a whole:
-----BEGIN NEW CERTIFICATE REQUEST-----
MIICaTCCAicCAQAwZTELMAkGA1UEBhMCR1IxDzANBgNVBAgTBkdyZWVjZTEPMA0GA1UEBxMGQXRo
BQADLwAwLAIUQZbY/3Qq0G26fsBbWiHMbuVd3VICFE+gwtUauYiRbHh0caAtRj3qRTwl
-----END NEW CERTIFICATE REQUEST-----
I assume this is the whole certificate. How can I see private (or public key) via keytool?
You created a private (and associated public) key in your keystore. For it to be really usable, you can get it signed by a certification agency (CA) - for this is the -certreq command (you send the output to this certification agency, along with some other information and a bit of money, and they send back a certificate, which you can then import in your keystore.)
Viewing the private key is not intended ... you usually don't need this, since you use the keystore in your Java program, and this knows how to use it.
Edit: Since you want to look at your keystore, here a quick Java program which does this:
import java.io.*;
import java.security.*;
import java.security.cert.Certificate;
public class KeyPrinter {
/**
* to be invoked with these parameters:
*
* [0]: keystore-password
* [1]: filename
* [2]: alias
* [3]: entry-Password (if necessary)
*/
public static void main(String[] params)
throws IOException, GeneralSecurityException
{
char[] storePass = params[0].toCharArray();
String fileName = params[1];
String alias = params[2];
KeyStore.ProtectionParameter entryPass;
if(params.length > 3) {
entryPass=new KeyStore.PasswordProtection(params[3].toCharArray());
} else {
entryPass = null;
}
KeyStore store = KeyStore.getInstance("JKS");
InputStream input = new FileInputStream(fileName);
store.load(input, storePass);
KeyStore.Entry entry = store.getEntry(alias, entryPass);
System.out.println(entry);
}
}
First call keytool -list -keystore myStore to know which alias to look for, then call this program with the passwords and parameters. In case of a private key entry, it shows the key itself and additionally a self-signed certificate which contains the public key, in a readable form. In case of a "trusted certificate", it shows only the public key.
No, you cannot.
You can access the private key from code, but you cannot export it using the keytool.
Use OpenSSL if you need to export private key.
Another option: you can generate keystore in PKCS12 format. Then you can import it to a browser and then to export the private key.
(Portecle) is a very convenient GUI tool for managing keystores. And among other things it have an option to export private key and its associated certificate.
The common way to share your public key is to share a certificate for your keypair (it contains your public key inside)
keytool -list -v -alias myCert -storepass 123456 -keystore file.jks
or
keytool -list -rfc -alias myCert -storepass 123456 -keystore file.jks
as noted in
keytool -help
You can use keystore-explorer.