java.security.InvalidKeyException when implementing DH protocol - java

In my android application I would like to implement the Diffie-Hellman protocol in order to generate a common secret between two users.
What I do is the following: when the first user sends a friendship request to the second one, the app generates a key pair, storing the public one in a remote database and the private in a local database.
Here is the code for this first part:
generateKeys();
localDB.insertPrivateKey(userId, entityId, privateKey);
remoteDB.insertFirstPublicKey(userId, entityId, publicKey);
The generateKeys method is the following:
private void generateKeys() {
try {
final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DH");
keyPairGenerator.initialize(1024);
final KeyPair keyPair = keyPairGenerator.generateKeyPair();
privateKey = keyPair.getPrivate();
publicKey = keyPair.getPublic();
} catch (Exception e) {
e.printStackTrace();
}
}
When the second user accepts the request, another key pair is generated, once again storing the private key in the local db and the public in the remote. Then it fetches the public key of the first user from the remote db, converts it back to PublicKey and generate the common secret.
Here is the code for the second part:
generateKeys();
localDB.insertPrivateKey(userId, entityId, privateKey);
remoteDB.insertSecondPublicKey(entityId, userId, publicKey);
String stringFirstPubKey = remoteDB.fetchFirstKey(entityId, userId);
PublicKey firstPubKey = stringToPublicKey(stringFirstPubKey);
byte[] commonSecret = generateCommonSecret(firstPubKey);
The stringToPublicKey is the following:
private PublicKey stringToPublicKey(String stringPubK) throws NoSuchAlgorithmException, InvalidKeySpecException {
byte[] pubK = Base64.decodeBase64(stringPubK);
KeyFactory keyFactory = KeyFactory.getInstance("DH");
EncodedKeySpec keySpec = new X509EncodedKeySpec(pubK);
return keyFactory.generatePublic(keySpec);
}
And the generataCommonSecret:
private byte[] generateCommonSecret(PublicKey firstPubKey) {
try {
final KeyAgreement keyAgreement = KeyAgreement.getInstance("DH");
keyAgreement.init(privateKey);
keyAgreement.doPhase(firstPubKey, true);
byte[] secretKey = adjustKeyLenght(keyAgreement.generateSecret());
return secretKey;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Also when storing the keys I convert them into String with the following:
String stringPubK = Base64.encodeBase64String(publicKey.getEncoded());
When executing the following line in generateCommonSecret
keyAgreement.doPhase(firstPubKey, true);
I get this exception
java.security.InvalidKeyException: DHPublicKey not for this KeyAgreement!
at com.android.org.bouncycastle.jcajce.provider.asymmetric.dh.KeyAgreementSpi.engineDoPhase(KeyAgreementSpi.java:101)
at javax.crypto.KeyAgreement.doPhase(KeyAgreement.java:383)
Can someone help me finding the problem? What is weird is that if I try to do this in a single java class, without storing and retrieving keys, it works fine.

Related

How can I create a RSA Encrypter with a String Public Key

I have a System that generates a PublicKey, then encrypts a password, then they are both stored in a database.
PK Generator
public PublicKey GetPK() {
try {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048);
KeyPair pair = generator.generateKeyPair();
return pair.getPublic();
} catch(Exception e) {
return null;
}
}
Password Encryption
public static String EncryptPassword(PublicKey publicKey, String password) {
try {
Cipher encryptCipher = Cipher.getInstance("RSA");
encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] secretMessageBytes = password.getBytes(StandardCharsets.UTF_8);
byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessageBytes);
return Base64.getEncoder().encodeToString(encryptedMessageBytes);
} catch(Exception e) {
return null;
}
}
Then, what I try to make Login , I retrieve this Public key, encrypt my login form's password using this Public Key, and it Just don't give me the same encrypted password.
PuclicKey return
public PublicKey ReturnPK(String keyFromDatabase) throws InvalidKeySpecException, NoSuchAlgorithmException {
KeyFactory kf = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(keyFromDatabase));
return kf.generatePublic(keySpec);
}
Compare Passwords
PublicKey pk = encryption.ReturnHash(GetUsers().getHashCode());
String encryptedPw = encryption.EncryptPassword(pk, pwd);
boolean loginState = encryptedPw.equals(dbPwd);
The var loginState always returns false. Can someone help me?

How to test RSA keys for flaws in encryption

Recently I have read an article Seriously, stop using RSA
and I was wondering what is the best way to test my implementation of the RSA keyset generation. I am not a security expert, but I have been working in Java and Software Dev. for quite some time now. However, I am not sure how I would test the strength of a key (flaws in the implementation process).
The Implementation I did for my RSA key gen is the following:
private KeyPairGenerator keyGen;
private PrivateKey privateKey;
private PublicKey publicKey;
private SecureRandom srandom;
public SecreteKeyGenerator() throws NoSuchAlgorithmException {
this.keyGen = KeyPairGenerator.getInstance("RSA");
this.keyGen.initialize(Encryptable.RSA_LENGTH);
this.srandom = new SecureRandom();
}
public void createRSAKeys() {
KeyPair pair = this.keyGen.generateKeyPair();
this.privateKey = pair.getPrivate();
this.publicKey = pair.getPublic();
}
The way I am using this implementation is in combination with AES key. I use AES key to encrypt the data and then I encrypt the key using RSA.
This is a suggested method in Java when dealing with large files.
The variable Encryptable.RSA_LENGTH has a value 2048, which is the length of a key.
After the keys are generated they are written to a files stored on clients machine.
The encryption works properly, as well as the digital signature process. However, I want to be extra sure that I've implemented everything correctly.
This is the code that is handling the encryption process of the file:
public void execute() {
OperationsLogger.getLogger().log(Level.INFO, "Encryption of file started");
AESEncryption aesEncryption = new AESEncryption();
aesEncryption.createAESKey();
aesEncryption.encryptFile(this.targetFilePath, this.publicKeyPath);
OperationsLogger.getLogger().log(Level.INFO, "File encrypted successfully.");
}
//AES Encryption
public void encryptFile(String inputFile, String publicKeyLocation) throws IOException, GeneralSecurityException {
try (FileOutputStream out = new FileOutputStream(inputFile + ".enc")) {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, new RSAEncryption().getPublic(publicKeyLocation));
byte[] b = cipher.doFinal(secretKey.getEncoded());
out.write(b);
out.write(gen.getIv());
Cipher ci = Cipher.getInstance("AES/CBC/PKCS5Padding");
ci.init(Cipher.ENCRYPT_MODE, secretKey, gen.getIvspec());
try (FileInputStream in = new FileInputStream(inputFile)) {
processFile(ci, in, out);
}
}
}
public PublicKey getPublic(String filename) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
byte[] keyBytes = Files.readAllBytes(new File(filename).toPath());
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(spec);
}

RSA Bad Padding Exception

I am trying to RSA encrypt data on Android and send it to server(spring).
getting BadPaddingException :
Approach:
server send public key in a string, which i convert to PublicKey Object and send data from App after encryption as a string.
server has a private key string , which is converted to PublicKey object and then data is decrypted.
Any Help would be much appreciated.
generation of key :
public static KeyPair generateKeyPairRSA() {
try {
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024, random);
KeyPair keyPair = keyGen.generateKeyPair();
return keyPair;
} catch (Exception e) {
Log.d(TAG,e.getLocalizedMessage());
}
return null;
}
public byte[] RSAEncrypt(final String plain, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance(ALGO_RSA);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = cipher.doFinal(plain.getBytes());
return encryptedBytes;
}
public static PublicKey loadPublicKey1(String stored) throws Exception{
byte[] data = Base64.decode(stored.getBytes());
X509EncodedKeySpec spec = new X509EncodedKeySpec(data);
KeyFactory fact = KeyFactory.getInstance(ALGO_RSA);
return fact.generatePublic(spec);
}
Server Methods :
public byte[] decryptRSA(String inputData) throws Exception {
byte[] inputBytes = Base64.decodeBase64(inputData);
PrivateKey key = loadPrivateKey(getPrivateKey());
Cipher cipher1 = Cipher.getInstance("RSA");
cipher1.init(Cipher.DECRYPT_MODE, key);
return cipher1.doFinal(inputBytes);
}
private PrivateKey loadPrivateKey(String key64) throws Exception {
byte[] pkcs8EncodedBytes = Base64.decodeBase64(key64);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8EncodedBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(keySpec);
}
Got it Working.
So diffrent libs have differenet implementation of Cipher.
so while calling
Cipher.getInstance("RSA/ECB/PKCS1Padding");
Explicitly mention encryption mode and padding.
Hope it helps somebody.

McEliece (Bouncy Castle) Getting the public key back

I am currently trying to implement McEliece encryption using BC but running into some trouble. I currently have the capabilities to create the keys and place them into a file, i can read them back into the program but cannot get it to go from bytes back to Public Key.
Below is what i currently have:
public static String EncryptText(Component tmp, String Plaintext) throws InvalidKeyException, InvalidCipherTextException {
String CipherText = "Didnt Work";
try {
// The message to encrypt.
byte[] messageBytes = Plaintext.getBytes();
//read in the Public Key to use to Encrypt.
File f = new File(tmp.getPublicKey());
FileInputStream fis = new FileInputStream(f);
byte[] PubKeybytes = new byte[fis.available()];
fis.read(PubKeybytes);
fis.close();
//turn the bytes into the Key.
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(PubKeybytes);
SubjectPublicKeyInfo PKI ;
KeyFactory KF = null;
try {
KF = KeyFactory.getInstance("McEliece");
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(McEliecePKCS.class.getName()).log(Level.SEVERE, null, ex);
}
PublicKey PK = null;
try {
PK = KF.generatePublic(pubKeySpec);
} catch (InvalidKeySpecException ex) {
Logger.getLogger(McEliecePKCS.class.getName()).log(Level.SEVERE, null, ex);
}
//Public Key
PublicKey aPublic = PK;
McEliecePublicKeyParameters GPKP = (McEliecePublicKeyParameters) McElieceKeysToParams.generatePublicKeyParameter(aPublic);
//set the public key to use.
McElieceCipher EnCipheredText = new McElieceCipher();
EnCipheredText.init(true, GPKP);
EnCipheredText.initCipherEncrypt(GPKP);
byte[] ciphertextBytes;
//sign the message with the public key.
ciphertextBytes = EnCipheredText.messageEncrypt(messageBytes);
CipherText = new String(ciphertextBytes);
return CipherText;
} catch (IOException ex) {
Logger.getLogger(McEliecePKCS.class.getName()).log(Level.SEVERE, null, ex);
}
return CipherText;
}\
The current error im having with this code is with the KeyFactory and that "McEliece" is not considered an algorithm as im getting NoSuchAlgorithmException but im not really sure what else to try at the moment. i have also tried to use the KeyFactory that is included with BouncyCastle for McEliece but had no success as the methods were either protected or did not allow for KeySpec and wanted SubjectPublicKeyInfo which i could not figure out how to change the KeySpec into or the Byte array into.
Sorry if this is a simple question im fairly new to coding Cryptography.
Thanks for the replies in advance.
Managed to figure out the issue. i needed to add:
Security.addProvider(new BouncyCastleProvider());
Security.addProvider(new BouncyCastlePQCProvider());

In Java, how do I decrypt using the private key from an X509 certificate (public/private key pair) inside a JKS keystore?

I created a KeyStore using KeyStore Explorer with a public/private key pair inside it of type RSA, 4096 bytes, and PKCS#8 formatting.
I get an error when my code runs and hits the cipher.init() method :
"Key for algorithm RSA not suitable for symmetric encryption."
This doesn't really make sense to me because I'm using asymmetric key encryption/decryption. I'm not sure where to go from here or what I'm doing wrong.
Here is what I have:
public TransactionData processData(TransactionData data) throws BTHException {
String keystoreFilePath = manager.getStringValue(KeyStoreFilePath);
String keystorePassword = manager.getStringValue(KeyStoreFilePassword);
String privateKeyPassword = manager.getStringValue(KeyStorePrivateKeyPassword);
String certificateAlias = manager.getStringValue(CertificateAlias);
org.apache.xml.security.Init.init();
try {
InputStream in = data.getDataStream();
byte[] dataBytes = DataUtil.readBytes(in);
String encryptedDataStr = new String(dataBytes);
PrivateKey privateKey = getPrivateKeyFromKeyStore(keystoreFilePath, keystorePassword, certificateAlias, privateKeyPassword);
decrypt(
encryptedDataStr,
privateKey
);
}catch(Exception e){
throw new BTHException(e.getMessage());
}
return data;
}
private PrivateKey getPrivateKeyFromKeyStore(String keyStoreFilePath, String keyStorePassword, String privateKeyCertAlias, String privateKeyPassword) throws BTHException {
PrivateKey privateKey = null;
try {
KeyStore keystore = KeyStore.getInstance("JKS");
BASE64Encoder encoder = new BASE64Encoder();
keystore.load(new FileInputStream(keyStoreFilePath), keyStorePassword.toCharArray());
Key key=keystore.getKey(privateKeyCertAlias,keyStorePassword.toCharArray());
if(key instanceof PrivateKey) {
Certificate cert=keystore.getCertificate(privateKeyCertAlias);
PublicKey publicKey=cert.getPublicKey();
KeyPair keyPair = new KeyPair(publicKey,(PrivateKey)key);
privateKey = keyPair.getPrivate();
}
//privateKeyEncoded = encoder.encode(privateKey.getEncoded());
} catch (Exception e) {
throw new BTHException(e.getMessage());
}
return privateKey;
}
private String decrypt(String cipherText, PrivateKey privateKey) throws IOException, GeneralSecurityException, BTHException {
String decryptedValue = null;
try {
// 1. Get the cipher ready to start doing the AES transformation
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// 2. Start the decryption process
// THIS IS WHERE IT FAILS
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// 3. Finish the decryption process
decryptedValue = new String(cipher.doFinal(Base64.decodeBase64(cipherText)), "UTF-8");
} catch (Exception e) {
throw new BTHException(e.getMessage());
}
return decryptedValue;
}
Any help would be great. Thanks in advance!
You are trying to initialize your cipher as AES/CBC/PKCS5Padding which is an symmetric encryption and that is where the exception originates.
You should use the Cipher like that:
// 1. Get the cipher ready to start doing the RSA transformation
Cipher cipher = Cipher.getInstance("RSA");
// 2. Start the decryption process
cipher.init(Cipher.DECRYPT_MODE, privateKey);
Here you can find a good example for RSA-Encryption and Decryption.

Categories

Resources