I have a Diffie–Hellman security class like this:
public class AESSecurityCap {
private PublicKey publicKey;
KeyAgreement keyAgreement;
byte[] sharedsecret;
AESSecurityCap() {
makeKeyExchangeParams();
}
private void makeKeyExchangeParams() {
KeyPairGenerator kpg = null;
try {
kpg = KeyPairGenerator.getInstance("EC");
kpg.initialize(128);
KeyPair kp = kpg.generateKeyPair();
publicKey = kp.getPublic();
keyAgreement = KeyAgreement.getInstance("ECDH");
keyAgreement.init(kp.getPrivate());
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
e.printStackTrace();
}
}
public void setReceiverPublicKey(PublicKey publickey) {
try {
keyAgreement.doPhase(publickey, false); // <--- Error on this line
sharedsecret = keyAgreement.generateSecret();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
}
}
and implemented this class:
public class Node extends AESSecurityCap {
}
Sometimes I need to reinitialize DH keyAgreement:
public class TestMainClass {
public static void main(String[] args) {
Node server = new Node();
Node client = new Node();
server.setReceiverPublicKey(client.getPublicKey());
client.setReceiverPublicKey(server.getPublicKey());
// My problem is this line ,
// Second time result exception
server.setReceiverPublicKey(client.getPublicKey());
}
}
but receive this exception:
Exception in thread "main" java.lang.IllegalStateException: Phase already executed
at jdk.crypto.ec/sun.security.ec.ECDHKeyAgreement.engineDoPhase(ECDHKeyAgreement.java:91)
at java.base/javax.crypto.KeyAgreement.doPhase(KeyAgreement.java:579)
at ir.moke.AESSecurityCap.setReceiverPublicKey(AESSecurityCap.java:37)
at ir.moke.TestMainClass.main(TestMainClass.java:13)
Is there any way to reinitialize ECDH KeyAgreement multiple time?
This is my test case:
Client initialize DH and generate public key.
Client sent public key to server.
Server initialize DH with client key and generate own public key and generate shared secret key.
Server send public key to client.
Client generate shared secret key with server public key.
In this step client and server has public keys and shared secret.
My problem is client disconnected() and KeyAgreement initialized by singleton object and don't reinitialized second time.
Sometimes I need to do this subject.
Please guide me to fix this problem.
The IllegalStateException (Phase already executed) seems to be especially caused by the ECDH-implementation of the SunEC-provider. The exception doesn't occur if an (additional) init is executed immediately before the doPhase. However, this init-call shouldn't be necessary, since after the doPhase-call generateSecret is executed, which should reset the KeyAgreement-instance to the state after the init-call, at least according to the generateSecret-documentation:
This method resets this KeyAgreement object to the state that it was in after the most recent call to one of the init methods...
Possibly it's a bug in the SunEC-provider. If DH is used instead of ECDH (and the SunJCE-provider instead of the SunEC-provider) the behavior is as expected, i.e. repeated doPhase-calls are possible (without additional init-calls). The same applies to ECDH using the BouncyCastle-provider. Therefore, you could take the BouncyCastle-provider instead of the SunEC-provider to run ECDH with your code.
Note: The second parameter (lastPhase) in doPhase should be set to true, otherwise an IllegalStateException (Only two party agreement supported, lastPhase must be true) is generated (at least for ECDH).
EDIT:
The bug is already known and fixed in JDK 12, see JDK-8205476: KeyAgreement#generateSecret is not reset for ECDH based algorithmm.
Related
When I concurrently attempt to sign with the same key inside the HSM, some signatures fail with the following error :
com.safenetinc.luna.exception.LunaCryptokiException: function 'C_Sign' returns 0x20 (CKR_DATA_INVALID)
With only one thread and one instance of my jar, I have no error.
More threads I have more often the signatures fail.
An important point, if I have 2 instances of only one thread each the issue also occurs.
I'm using Luna JSP provider.
The algo is SHA256withECDSA.
I tried also with token connection, but same result.
I check my timeout in crystoki.ini, failing occurs before the timeout.
Even with a Singleton for the keyStore, or for the privateKey I have the issue.
Thread generator:
public static void main(String[] args) {
SpringApplication.run(DccHsmTest.class, args);
for (int i=0; i<4; i++)
{
Multithreading object = new Multithreading();
object.start();
}
}
private byte[] CHALLENGE = new byte[10000];
public void run()
{
try
{
final HsmService hsmService = new HsmService();
hsmKeyStore = hsmService.hsmKeyStore(hsmPartition, hsmPassword);
PrivateKey privateKey = (PrivateKey) hsmKeyStore.getKey(aliasToRetrieve, password);
while(sign(privateKey)){
...
}
throw new RuntimeException();
}
catch (Exception e)
{
...
}
}
private static boolean sign(PrivateKey privateKey) {
try {
Signature signature = Signature.getInstance("SHA256withECDSA", "LunaProvider");
signature.initSign(privateKey);
signature.update(CHALLENGE);
signature.sign();
return true;
}
catch(Exception e){
...
return false;
}
}
HsmService
#Service
#Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class HsmService {
public KeyStore hsmKeyStore(String hsmPartition, String hsmPassword) throws
KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
LunaProvider lunaProvider = new LunaProvider();
Security.addProvider(lunaProvider);
LunaSlotManager lunaSlotManager = LunaSlotManager.getInstance();
lunaSlotManager.login(hsmPartition, hsmPassword);
KeyStore keyStore = KeyStore.getInstance("Luna");
keyStore.load(null, null);
return keyStore;
}
}
The error definition of CKR_DATA_INVALID is:
The plaintext input data to a cryptographic operation is invalid.
But I always pass the same input, sometimes it works.
The CKlog doesn't provide much details :
FINISign CKR_DATA_INVALID(5811ms) {"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" }
Just seems to return a bad signature
Finally a simple reboot sold the problem.
Now I have to find if this issue is due to a bad usage of the HSM on my code, or if it's due to tests from other developer(the HSM is used by other project)
I'm trying to implement ECDH in Android using a private generated by Android KeyStore Provider.
public byte[] ecdh(PublicKey otherPubKey) throws Exception {
try {
ECPublicKey ecPubKey = (ECPublicKey) otherPubKey;
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
PrivateKey pk = (PrivateKey) LoadPrivateKey("Backend");
keyAgreement.init(pk);
keyAgreement.doPhase(ecPubKey, true);
return (keyAgreement.generateSecret());
}
catch (Exception e)
{
Log.e("failure", e.toString());
return null;
}
}
However, this exception is catched in keyAgreement.init(pk) :
E/failure: java.security.InvalidKeyException: cannot identify EC private key: java.security.InvalidKeyException: no encoding for EC private key
I generated before successfully the "Backend" Public/Private key pair using:
public void GenerateNewKeyPair(String alias)
throws Exception {
if (!keyStore.containsAlias(alias)) {
// use the Android keystore
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, ANDROID_KEYSTORE);
keyGen.initialize(
new KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY | KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
.setDigests(KeyProperties.DIGEST_SHA256,
KeyProperties.DIGEST_SHA384,
KeyProperties.DIGEST_SHA512)
.setRandomizedEncryptionRequired(true)
.build());
// generates the keypair
KeyPair keyPair = keyGen.generateKeyPair();
}
}
And I load the private key using:
public PrivateKey LoadPrivateKey(String alias) throws Exception {
PrivateKey key = (PrivateKey) keyStore.getKey(alias, null);
return key;
}
Anyone has an idea what is happening and can help me to understand how to fix it? Thanks!
As far as I know through research and trial and error, this is not currently supported.
I believe the best you can do is sign the public key of an EC key pair you generate outside of the AndroidKeyStore with an EC key pair that is stored in AndroidKeyStore. You can then send this signed public key over to the other party with your signing key certificate, generate a shared secret (outside of AndroidKeyStore), then store the SecretKey that is derived using a KDF on the generated secret. I recommend using this non-AndroidKeyStore generated key pair once (so only for the purpose of deriving the secret) and repeating this process to re-key when deemed necessary.
EDIT: When I said 'store the SecretKey', I meant in AndroidKeyStore. That key will initially be in what is called 'normal world' in this context, but its the best you can do for now.
ECDH is supported in API level 23. Please refer android documentation on Android Keystore System
Sample code is also available in this link..
I am writing and sns http end point. I have to verify the SNS message using verifyMessageSignature method of SignatureChecker. How can I get the publicKey attribute from message. Is there util method for it as well. Please help.
The SNS message from Amazon includes a field SigningCertURL. Fetch the bytes at this location into a string cert, then make a public key from it thusly:
/**
* Build a PublicKey object from a cert
*
* #param cert the cert body
* #return a public key
*/
private PublicKey makePublicKey(String cert) {
try {
CertificateFactory fact = CertificateFactory.getInstance("X.509");
InputStream stream = new ByteArrayInputStream(cert.getBytes(StandardCharsets.UTF_8));
X509Certificate cer = (X509Certificate) fact.generateCertificate(stream);
return cer.getPublicKey();
} catch (Exception e) {
LOGGER.error("Failed to make a public key from Amazon cert", e);
return null;
}
}
Then you can call signatureChecker.verifySignature with it as the second argument.
I'm using JUnit 4 [C:\eclipse\plugins\org.junit_4.11.0.v201303080030\junit.jar] in combination with Eclipse (MARS, Version: Mars Milestone 3 (4.5.0M3) Build id: 20141113-0320.
I have some tests that test a simple class and which work well. But now arrived at the point where I wanted to test my encryption class, which implements the following encrypt function:
public String encrypt(String data) {
try {
SecretKeySpec KS = new SecretKeySpec(mKeyData, "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish/CBC/ZeroBytePadding"); // PKCS5Padding
cipher.init(Cipher.ENCRYPT_MODE, KS, new IvParameterSpec(mIv));
return bytesToHex(cipher.doFinal(data.getBytes()));
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
return null;
}
The Crypto class is not sub classed...
public class Crypto {
So to test this Class and more the encrypt function I have designed the following unit test:
package my.junit4.example;
import static org.junit.Assert.*;
import org.junit.Test;
public class CryptoTest {
#Test
public void testEncryption() {
Crypto myCrypto = new Crypto();
String encodedString = myCrypto.encrypt("Secret");
assertTrue("The decrypted encrypted word should deliver the original string", encodedString.equals(myCrypto.decrypt(encodedString)));
}
}
This test is failing with a stack trace:
java.security.NoSuchAlgorithmException: Cannot find any provider supporting Blowfish/CBC/ZeroBytePaddingnull
at javax.crypto.Cipher.getInstance(Cipher.java:535) at
my.junit.example.encrypt(Crypto.java:35) at
my.junit.example.CryptoTest.testEncrypt(CryptoTest.java:14) at
This didn't make much sense to me. But being relatively new to JUnit I suspect the issue is with me not understanding how to formulate these tests. The code works well encryption - decryption in my debugger is giving me the desired outcome. But how can I get this to work with JUnit. What obvious mistake I have made?
The problem is with this line:
Cipher cipher = Cipher.getInstance("Blowfish/CBC/ZeroBytePadding");
The algorithm you're requesting is not supported on your system. Any particular reason you want that specific one?
The docs specify the following default implementations:
AES/CBC/NoPadding (128)
AES/CBC/PKCS5Padding (128)
AES/ECB/NoPadding (128)
AES/ECB/PKCS5Padding (128)
DES/CBC/NoPadding (56)
DES/CBC/PKCS5Padding (56)
DES/ECB/NoPadding (56)
DES/ECB/PKCS5Padding (56)
DESede/CBC/NoPadding (168)
DESede/CBC/PKCS5Padding (168)
DESede/ECB/NoPadding (168)
DESede/ECB/PKCS5Padding (168)
RSA/ECB/PKCS1Padding (1024, 2048)
RSA/ECB/OAEPWithSHA-1AndMGF1Padding (1024, 2048)
RSA/ECB/OAEPWithSHA-256AndMGF1Padding (1024, 2048)
You need to add the Bouncy Castle provider to the Java runtime. You can see how to install the provider by looking at the Bouncy Castle wiki page. Neither the Blowfish algorithm nor zero padding is supported out of the box by Java installations.
The following runs fine on my box:
Security.addProvider(new BouncyCastleProvider());
byte[] mKeyData = new byte[16];
byte[] mIv = new byte[8];
SecretKeySpec KS = new SecretKeySpec(mKeyData, "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish/CBC/ZeroBytePadding");
cipher.init(Cipher.ENCRYPT_MODE, KS, new IvParameterSpec(mIv));
Make sure that the provider is also available to the test framework when it is run. You'll need to put the bcprov-jdk15on-[version].jar in the class path of the runtime before you can install the provider.
I have created a chat application and to finish it I have to implement some Cryptography algorithm to secure the messages between server - client.
My implementation is:
1.Client creates kaypair (public and private key) and sends public key to server.
2.Server gets public key and creates symmetric key encrypted with the public key.
3.Server sends the encrypted key to Client.
4.Client unlocks symmetric key with private key.
5.Client and Server communicate with Symmetric key.
This part of the code is where the server gets the public key and sends the symmetric key encrypted
else if(msg.type.equals("pubKey")){
pubKey = msg.content; //get public key
String key = Arrays.toString(crypt.geteKey());
clients[findClient(ID)].send(new Message("symmKey", "SERVER", key, msg.sender));//! //send symmetric key encrypted with public key
}
Key encryption method:
public void keyEncryption() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
eKey = cipher.doFinal(key.getBlowfishKeyBytes()); //symmetric key encrypted with public key
//System.out.println("2. cipherText= " + bytesToHex(symmKey));
}
How I get encrypted symmetric key from server:
else if(msg.type.equals("symmKey")){
symmKey = (String) msg.content; //get encrypted symmetric key (must unlock with private key)
}
The Server Message class: (Client Message class has "object content" instead of String)
package com.socket;
import java.io.Serializable;
public class Message implements Serializable{
private static final long serialVersionUID = 1L;
public String type, sender,content, recipient;
public Message(String type, String sender, String content, String recipient){
this.type = type; this.sender = sender; this.content = content; this.recipient = recipient;
}
#Override
public String toString(){
return "{type='"+type+"', sender='"+sender+"', content='"+content+"', recipient='"+recipient+"'}";
}
}
Client GUI where I send the key to the Server:
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
//KeyPair
try {
keyPair = new Keypair();
} catch (NoSuchAlgorithmException ex) {
jTextArea1.append("Security Error! You are not safe!");
}
Object pubKey = keyPair.getKeyPair().getPublic();
username = jTextField3.getText();
password = jPasswordField1.getText();
if (!username.isEmpty() && !password.isEmpty()) {
client.send(new Message("login", username, password, "SERVER"));
client.send(new Message("pubKey",username, pubKey, "SERVER")); //send Public key to Server
}
}
Error I Get on Server:
Database exception : userExists()
53846 ERROR reading: cannot assign instance of sun.security.rsa.RSAPublicKeyImpl to field com.socket.Message.content of type java.lang.String in instance of com.socket.Message
I have implemented steps 1-3 but I get this exception... If anyone has any idea how to deal with this issue, please help me.
(I will provide any additional code if required.)
Thank you.
msg.content is instance of String and you trying assign it to sun.security.rsa.RSAPublicKeyImpl here:
pubKey = msg.content;
Just on a note, your implementation looks susceptible to a man-in-the-middle attack. If we call your client and server Alice and Bob. I'm Mallory - a malicious eavesdropper.
Alice creates public-private key pair and sends public key to Bob.
Mallory intercepts this, keeps Alice's public key for later and sends his own public key on to Bob.
Bob receives Mallory's public key, thinking it belongs to Alice, generates a session key, encrypts it and sends back to Mallory.
Mallory decrypts the session key, reencrypts using Alice's public key and returns it to her.
Alice decrypts using her private key and happily goes about sending encrypted messages to Bob, not realising that Mallory has intercepted the session key.
Mallory now listens in on their conversation and sells Alice's mums recipe for chocolate cake to the highest bidder. Alice blames Bob, Bob blames Alice etc.
You will need to introduce signing to your protocol to help to ensure authenticity of your protocol: Alice and Bob need to be certain that that the communications are original and have not been tampered with. I'm on my phone but will see if I can find a decent link later for you.