How to programmatically generate and store HMacSHA256 key in Java? - java

I would like to generate and store a HMacSHA256 key for testing purposes in the Java keystore.
I would normally do this via the keytool:
keytool -genseckey -keystore keystore.jceks -storetype jceks -storepass secret -keyalg HMacSHA256 -keysize 2048 -alias HS256 -keypass secret
So far I found that I can generate the key using:
SecretKey key = new SecretKeySpec("secret".getBytes(), "HmacSHA256");
That key is unfortunately not an instance of PrivateKey and thus storing the key fails:
KeyStore ks = ...
ks.setEntry("HS256", new SecretKeyEntry(key), new PasswordProtection("secret".toCharArray()));
Exception:
java.security.KeyStoreException: Cannot store non-PrivateKeys
at sun.security.provider.JavaKeyStore.engineSetKeyEntry(JavaKeyStore.java:258)
at sun.security.provider.JavaKeyStore$JKS.engineSetKeyEntry(JavaKeyStore.java:56)
at java.security.KeyStoreSpi.engineSetEntry(KeyStoreSpi.java:550)
at sun.security.provider.KeyStoreDelegator.engineSetEntry(KeyStoreDelegator.java:179)
at sun.security.provider.JavaKeyStore$DualFormatJKS.engineSetEntry(JavaKeyStore.java:70)
at java.security.KeyStore.setEntry(KeyStore.java:1557)
at com.gentics.mesh.SecretKeyTest.testSHA(SecretKeyTest.java:31)
I believe that SecretKey represents a symmetric key. And PrivateKey is part of a PublicKey and Private-Key Pair. Is there a way to store a single symmetric key?

Yes, you can. But until Java 9 comes out, PKCS#12 key stores will be limited in functionality. JCEKS key stores as you are using in the command line keytool do however support symmetric (HMAC) keys:
public class HMACKeyStore {
public static void gen( String thePath, String thePassword ) throws Exception {
KeyGenerator keygen = KeyGenerator.getInstance("HmacSHA256");
SecretKey key = keygen.generateKey();
KeyStore keystore = KeyStore.getInstance("jceks");
keystore.load(null, null);
// This call throws an exception
keystore.setKeyEntry("theKey", key, thePassword.toCharArray(), null);
keystore.store( new FileOutputStream(thePath), thePassword.toCharArray() );
SecretKey keyRetrieved = (SecretKey) keystore.getKey("theKey", thePassword.toCharArray());
System.out.println(keyRetrieved.getAlgorithm());
}
public static void main(String[] args) throws Exception {
gen("hmac_store.jceks", "password");
}
}
should work fine on Java 8.

Related

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));
}
}

Issue in generating private key

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();

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 ...]

Generate GOST 34.10-2001 keypair and save it to some keystore

Currently I need to generate a keypair for GOST 34.10-2001 signature algorithm. It was pleasant to discover that bouncy castle provider has supported this algorithm, but I can not generate a keypair and save it to any keystore of any type. Currently I tried this command (this command works great if keyalg is DSA and sigalg is SHA1withDSA):
keytool -genkey -alias test1 -keyalg ECGOST3410 -keysize 512 -sigalg GOST3411withECGOST3410 \
-keypass test_1 -validity 1000 -storetype JKS -keystore test1.jks -storepass test_1 -v \
-provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "bcprov-jdk16-1.46.jar"
But I've got an error:
keytool error: java.lang.IllegalArgumentException: unknown key size.
java.lang.IllegalArgumentException: unknown key size.
at sun.security.x509.CertAndKeyGen.generate(CertAndKeyGen.java:134)
at sun.security.tools.KeyTool.doGenKeyPair(KeyTool.java:1156)
at sun.security.tools.KeyTool.doCommands(KeyTool.java:786)
at sun.security.tools.KeyTool.run(KeyTool.java:172)
at sun.security.tools.KeyTool.main(KeyTool.java:166)
Exactly the same error I can see when I try to manipulate keysize or remove keysize option from the command. But there is some special case. When I set keysize to 256 I've got another error:
keytool error: java.lang.IllegalArgumentException: key size not configurable.
java.lang.IllegalArgumentException: key size not configurable.
at sun.security.x509.CertAndKeyGen.generate(CertAndKeyGen.java:134)
at sun.security.tools.KeyTool.doGenKeyPair(KeyTool.java:1156)
at sun.security.tools.KeyTool.doCommands(KeyTool.java:786)
at sun.security.tools.KeyTool.run(KeyTool.java:172)
at sun.security.tools.KeyTool.main(KeyTool.java:166)
Currently I have no idea how to generate a keypair and how to save it to a keystore. Also I've got some java code that can generate a key pair for GOST 34.10-2001 algorithm:
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
KeyPairGenerator kpg = KeyPairGenerator.getInstance("ECGOST3410", "BC");
kpg.initialize(new ECGenParameterSpec("GostR3410-2001-CryptoPro-A"));
KeyPair kp = kpg.generateKeyPair();
This code sample uses ECGenParameterSpec class to initialize a key pair generator, so may be I should provide it somehow to the keytool (-providerArg provider_arg or -Jjavaoption)?
P.S. I think that I should provide curve name as some parameter but I can not determine what parameter I should use.
You will not be able to use keytool and BC to create a keystore with GOST3410 keys.
sun.security.x509.CertAndKeyGen class used by the keytool does not provide an option to initialize the key generator with parameters, while BC GOST3410 key generator requires the initialization with ECParameterSpec.
You can create the keypair+certificate and place them into the keystore programmatically:
Security.addProvider( new org.bouncycastle.jce.provider.BouncyCastleProvider() );
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance( "ECGOST3410", "BC" );
keyPairGenerator.initialize( new ECGenParameterSpec( "GostR3410-2001-CryptoPro-A" ) );
KeyPair keyPair = keyPairGenerator.generateKeyPair();
org.bouncycastle.asn1.x500.X500Name subject = new org.bouncycastle.asn1.x500.X500Name( "CN=Me" );
org.bouncycastle.asn1.x500.X500Name issuer = subject; // self-signed
BigInteger serial = BigInteger.ONE; // serial number for self-signed does not matter a lot
Date notBefore = new Date();
Date notAfter = new Date( notBefore.getTime() + TimeUnit.DAYS.toMillis( 365 ) );
org.bouncycastle.cert.X509v3CertificateBuilder certificateBuilder = new org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder(
issuer, serial,
notBefore, notAfter,
subject, keyPair.getPublic()
);
org.bouncycastle.cert.X509CertificateHolder certificateHolder = certificateBuilder.build(
new org.bouncycastle.operator.jcajce.JcaContentSignerBuilder( "GOST3411withECGOST3410" )
.build( keyPair.getPrivate() )
);
org.bouncycastle.cert.jcajce.JcaX509CertificateConverter certificateConverter = new org.bouncycastle.cert.jcajce.JcaX509CertificateConverter();
X509Certificate certificate = certificateConverter.getCertificate( certificateHolder );
KeyStore keyStore = KeyStore.getInstance( "JKS" );
keyStore.load( null, null ); // initialize new keystore
keyStore.setEntry(
"alias",
new KeyStore.PrivateKeyEntry(
keyPair.getPrivate(),
new Certificate[] { certificate }
),
new KeyStore.PasswordProtection( "entryPassword".toCharArray() )
);
keyStore.store( new FileOutputStream( "test.jks" ), "keystorePassword".toCharArray() );

RSA decryption using modulus and exponent

My task: I have encrypted (RSA) data and public key as modulus and exponent. I have to write decryption code.
My problem with it: My implementation doesn't work ;) As far as I know philosophy is simple "open text" == rsa(public_key, rsa(private_key, "open text")) Edit: Exactly my assumption was wrong (Assumption is mother of all fu..ups ;) ). It should be "open text" == rsa(private_key, rsa(public_key, "open text")) because in RSA, public key is used for encryption and private for decryption.
I assumed that I can have public key which doesn't correspond to private key using during encryption so for tests I created own keys in such way:
openssl genrsa -des3 -out server.key 1024
openssl req -new -key server.key -out server.csr
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
I got public key modulus and exponent using command:
openssl x509 -in server.crt -text
For encryption testing I'm using code
//Reads private key from file
//StringPasswordFinder is my tmp implementation of PasswordFinder
PEMReader pemReader = new PEMReader(new FileReader("/path/to/server.key"), new StringPasswordFinder());
KeyPair keyPair = (KeyPair) pemReader.readObject();
PrivateKey pk = keyPair.getPrivate();
//text for encryption
String openText = "openText";
//encryption
Cipher rsaCipher = Cipher.getInstance("RSA", "BC");
rsaCipher.init(Cipher.ENCRYPT_MODE, pk);
byte[] encrypted = rsaCipher.doFinal(openText.getBytes("utf-8"));
And for decryption of encrypted text I use code
//modulus hex got using openssl
byte[] modulus = Hex.decodeHex("very long hex".toCharArray());
//exponent hex got using openssl
byte[] exponent = Hex.decodeHex("010001".toCharArray());
//initialization of rsa decryption engine
RSAEngine rsaEngine = new RSAEngine();
rsaEngine.init(false, new RSAKeyParameters(false, new BigInteger(modulus), new BigInteger(exponent)));
//input - encrypted stream
ByteArrayInputStream bais = new ByteArrayInputStream(encrypted);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//reading blocks from the input stream and decrypting them
int bytesRead = 0;
byte[] block = new byte[rsaEngine.getInputBlockSize()];
while ((bytesRead = bais.read(block)) > -1) {
baos.write(rsaEngine.processBlock(block, 0, bytesRead));
}
//dispalying decrypted text
System.out.println(new String(baos.toByteArray(), "utf-8"));
And after all displayed text is not. Can anybody show me where I'm wrong?
Edit: Summing up this problem has no solution. Because it's not possible encrypt message using private key and later decrypt it using public one. At general I mixed up encryption with signing message and decryption with verification. Because during making signature private key is used and public is used during verification. Btw, MByD thx for important clue.
I am not so familiar with java libraries for RSA, the times I tried to implement RSA in java was to build all calculations by myself, but if I understood you correct, I see 2 problems:
the data should be encrypted with the public key and decrypted with private key, not the other way around (since everyone with public key will be able to decrypt it...)
the public key should match the private key, otherwise, anyone with any private key will be able to decrypt data encrypted with any public key...
Also, for very long data, you should not use public key encryption. Instead, encrypt the data in some other algorithm (RC4, AES, etc.) and encrypt the key in RSA (similar to PGP approach)

Categories

Resources