I was running a code for rsa with digital signature on netbeans but got the following error :
Exception caught
java.io.FileNotFoundException: \org\owasp\crypto\testkeystore.ks (The system cannot find the path specified)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.(FileInputStream.java:120)
at java.io.FileInputStream.(FileInputStream.java:79)
at org.owasp.crypto.PublicKeyCryptography.main(PublicKeyCryptography.java:52)
BUILD SUCCESSFUL (total time: 0 seconds)
package org.owasp.crypto;
import java.security.*;
import java.security.cert.*;
import javax.crypto.*;
import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder;
public class PublicKeyCryptography {
public static void main(String[] args) {
SymmetricEncrypt encryptUtil = new SymmetricEncrypt();
String strDataToEncrypt = "Hello World";
byte[] byteDataToTransmit = strDataToEncrypt.getBytes();
// Generating a SecretKey for Symmetric Encryption
SecretKey senderSecretKey = SymmetricEncrypt.getSecret();
//1. Encrypt the data using a Symmetric Key
byte[] byteCipherText = encryptUtil.encryptData(byteDataToTransmit,senderSecretKey,"AES");
String strCipherText = new BASE64Encoder().encode(byteCipherText);
//2. Encrypt the Symmetric key using the Receivers public key
try{
// 2.1 Specify the Keystore where the Receivers certificate has been imported
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
char [] password = "testpwd".toCharArray();
java.io.FileInputStream fis = new java.io.FileInputStream("/org/owasp/crypto/testkeystore.ks");
ks.load(fis, password);
fis.close();
// 2.2 Creating an X509 Certificate of the Receiver
X509Certificate recvcert ;
MessageDigest md = MessageDigest.getInstance("MD5");
recvcert = (X509Certificate)ks.getCertificate("testrecv");
// 2.3 Getting the Receivers public Key from the Certificate
PublicKey pubKeyReceiver = recvcert.getPublicKey();
// 2.4 Encrypting the SecretKey with the Receivers public Key
byte[] byteEncryptWithPublicKey = encryptUtil.encryptData(senderSecretKey.getEncoded(),pubKeyReceiver,"RSA/ECB/PKCS1Padding");
String strSenbyteEncryptWithPublicKey = new BASE64Encoder().encode(byteEncryptWithPublicKey);
// 3. Create a Message Digest of the Data to be transmitted
md.update(byteDataToTransmit);
byte byteMDofDataToTransmit[] = md.digest();
String strMDofDataToTransmit = new String();
for (int i = 0; i < byteMDofDataToTransmit.length; i++){
strMDofDataToTransmit = strMDofDataToTransmit + Integer.toHexString((int)byteMDofDataToTransmit[i] & 0xFF) ;
}
// 3.1 Message to be Signed = Encrypted Secret Key + MAC of the data to be transmitted
String strMsgToSign = strSenbyteEncryptWithPublicKey + "|" + strMDofDataToTransmit;
// 4. Sign the message
// 4.1 Get the private key of the Sender from the keystore by providing the password set for the private key while creating the keys using keytool
char[] keypassword = "send123".toCharArray();
Key myKey = ks.getKey("testsender", keypassword);
PrivateKey myPrivateKey = (PrivateKey)myKey;
// 4.2 Sign the message
Signature mySign = Signature.getInstance("MD5withRSA");
mySign.initSign(myPrivateKey);
mySign.update(strMsgToSign.getBytes());
byte[] byteSignedData = mySign.sign();
// 5. The Values byteSignedData (the signature) and strMsgToSign (the data which was signed) can be sent across to the receiver
// 6.Validate the Signature
// 6.1 Extracting the Senders public Key from his certificate
X509Certificate sendercert ;
sendercert = (X509Certificate)ks.getCertificate("testsender");
PublicKey pubKeySender = sendercert.getPublicKey();
// 6.2 Verifying the Signature
Signature myVerifySign = Signature.getInstance("MD5withRSA");
myVerifySign.initVerify(pubKeySender);
myVerifySign.update(strMsgToSign.getBytes());
boolean verifySign = myVerifySign.verify(byteSignedData);
if (verifySign == false)
{
System.out.println(" Error in validating Signature ");
}
else
System.out.println(" Successfully validated Signature ");
// 7. Decrypt the message using Recv private Key to get the Symmetric Key
char[] recvpassword = "recv123".toCharArray();
Key recvKey = ks.getKey("testrecv", recvpassword);
PrivateKey recvPrivateKey = (PrivateKey)recvKey;
// Parsing the MessageDigest and the encrypted value
String strRecvSignedData = new String (byteSignedData);
String[] strRecvSignedDataArray = new String [10];
strRecvSignedDataArray = strMsgToSign.split("|");
int intindexofsep = strMsgToSign.indexOf("|");
String strEncryptWithPublicKey = strMsgToSign.substring(0,intindexofsep);
String strHashOfData = strMsgToSign.substring(intindexofsep+1);
// Decrypting to get the symmetric key
byte[] bytestrEncryptWithPublicKey = new BASE64Decoder().decodeBuffer(strEncryptWithPublicKey);
byte[] byteDecryptWithPrivateKey = encryptUtil.decryptData(byteEncryptWithPublicKey,recvPrivateKey,"RSA/ECB/PKCS1Padding");
// 8. Decrypt the data using the Symmetric Key
javax.crypto.spec.SecretKeySpec secretKeySpecDecrypted = new javax.crypto.spec.SecretKeySpec(byteDecryptWithPrivateKey,"AES");
byte[] byteDecryptText = encryptUtil.decryptData(byteCipherText,secretKeySpecDecrypted,"AES");
String strDecryptedText = new String(byteDecryptText);
System.out.println(" Decrypted data is " +strDecryptedText);
// 9. Compute MessageDigest of data + Signed message
MessageDigest recvmd = MessageDigest.getInstance("MD5");
recvmd.update(byteDecryptText);
byte byteHashOfRecvSignedData[] = recvmd.digest();
String strHashOfRecvSignedData = new String();
for (int i = 0; i < byteHashOfRecvSignedData.length; i++){
strHashOfRecvSignedData = strHashOfRecvSignedData + Integer.toHexString((int)byteHashOfRecvSignedData[i] & 0xFF) ;
}
// 10. Validate if the Message Digest of the Decrypted Text matches the Message Digest of the Original Message
if (!strHashOfRecvSignedData.equals(strHashOfData))
{
System.out.println(" Message has been tampered ");
}
}
catch(Exception exp)
{
System.out.println(" Exception caught " + exp);
exp.printStackTrace();
}
}
}
package org.owasp.crypto;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.Cipher;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
import java.security.InvalidAlgorithmParameterException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import sun.misc.BASE64Encoder;
/**
* #author Joe Prasanna Kumar
* This program provides the following cryptographic functionalities
* 1. Encryption using AES
* 2. Decryption using AES
* High Level Algorithm :
* 1. Generate a DES key (specify the Key size during this phase)
* 2. Create the Cipher
* 3. To Encrypt : Initialize the Cipher for Encryption
* 4. To Decrypt : Initialize the Cipher for Decryption
*/
public class SymmetricEncrypt {
String strDataToEncrypt = new String();
String strCipherText = new String();
String strDecryptedText = new String();
static KeyGenerator keyGen;
private static String strHexVal = "0123456789abcdef";
public static SecretKey getSecret(){
/**
* Step 1. Generate an AES key using KeyGenerator
* Initialize the keysize to 128
*
*/
try{
keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128);
}
catch(Exception exp)
{
System.out.println(" Exception inside constructor " +exp);
}
SecretKey secretKey = keyGen.generateKey();
return secretKey;
}
/**
* Step2. Create a Cipher by specifying the following parameters
* a. Algorithm name - here it is AES
*/
public byte[] encryptData(byte[] byteDataToEncrypt, Key secretKey, String Algorithm) {
byte[] byteCipherText = new byte[200];
try {
Cipher aesCipher = Cipher.getInstance(Algorithm);
/**
* Step 3. Initialize the Cipher for Encryption
*/
if(Algorithm.equals("AES")){
aesCipher.init(Cipher.ENCRYPT_MODE,secretKey,aesCipher.getParameters());
}
else if(Algorithm.equals("RSA/ECB/PKCS1Padding")){
aesCipher.init(Cipher.ENCRYPT_MODE,secretKey);
}
/**
* Step 4. Encrypt the Data
* 1. Declare / Initialize the Data. Here the data is of type String
* 2. Convert the Input Text to Bytes
* 3. Encrypt the bytes using doFinal method
*/
byteCipherText = aesCipher.doFinal(byteDataToEncrypt);
strCipherText = new BASE64Encoder().encode(byteCipherText);
}
catch (NoSuchAlgorithmException noSuchAlgo)
{
System.out.println(" No Such Algorithm exists " + noSuchAlgo);
}
catch (NoSuchPaddingException noSuchPad)
{
System.out.println(" No Such Padding exists " + noSuchPad);
}
catch (InvalidKeyException invalidKey)
{
System.out.println(" Invalid Key " + invalidKey);
}
catch (BadPaddingException badPadding)
{
System.out.println(" Bad Padding " + badPadding);
}
catch (IllegalBlockSizeException illegalBlockSize)
{
System.out.println(" Illegal Block Size " + illegalBlockSize);
illegalBlockSize.printStackTrace();
}
catch (Exception exp)
{
exp.printStackTrace();
}
return byteCipherText;
}
/**
* Step 5. Decrypt the Data
* 1. Initialize the Cipher for Decryption
* 2. Decrypt the cipher bytes using doFinal method
*/
public byte[] decryptData(byte[] byteCipherText, Key secretKey, String Algorithm) {
byte[] byteDecryptedText = new byte[200];
try{
Cipher aesCipher = Cipher.getInstance(Algorithm);
if(Algorithm.equals("AES")){
aesCipher.init(Cipher.DECRYPT_MODE,secretKey,aesCipher.getParameters());
}
else if(Algorithm.equals("RSA/ECB/PKCS1Padding")){
aesCipher.init(Cipher.DECRYPT_MODE,secretKey);
}
byteDecryptedText = aesCipher.doFinal(byteCipherText);
strDecryptedText = new String(byteDecryptedText);
}
catch (NoSuchAlgorithmException noSuchAlgo)
{
System.out.println(" No Such Algorithm exists " + noSuchAlgo);
}
catch (NoSuchPaddingException noSuchPad)
{
System.out.println(" No Such Padding exists " + noSuchPad);
}
catch (InvalidKeyException invalidKey)
{
System.out.println(" Invalid Key " + invalidKey);
invalidKey.printStackTrace();
}
catch (BadPaddingException badPadding)
{
System.out.println(" Bad Padding " + badPadding);
badPadding.printStackTrace();
}
catch (IllegalBlockSizeException illegalBlockSize)
{
System.out.println(" Illegal Block Size " + illegalBlockSize);
illegalBlockSize.printStackTrace();
}
catch (InvalidAlgorithmParameterException invalidParam)
{
System.out.println(" Invalid Parameter " + invalidParam);
}
return byteDecryptedText;
}
public static byte[] convertStringToByteArray(String strInput) {
strInput = strInput.toLowerCase();
byte[] byteConverted = new byte[(strInput.length() + 1) / 2];
int j = 0;
int interimVal;
int nibble = -1;
for (int i = 0; i < strInput.length(); ++i) {
interimVal = strHexVal.indexOf(strInput.charAt(i));
if (interimVal >= 0) {
if (nibble < 0) {
nibble = interimVal;
} else {
byteConverted[j++] = (byte) ((nibble << 4) + interimVal);
nibble = -1;
}
}
}
if (nibble >= 0) {
byteConverted[j++] = (byte) (nibble << 4);
}
if (j < byteConverted.length) {
byte[] byteTemp = new byte[j];
System.arraycopy(byteConverted, 0, byteTemp, 0, j);
byteConverted = byteTemp;
}
return byteConverted;
}
public static String convertByteArrayToString(byte[] block) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < block.length; ++i) {
buf.append(strHexVal.charAt((block[i] >>> 4) & 0xf));
buf.append(strHexVal.charAt(block[i] & 0xf));
}
return buf.toString();
}
}
Your exception is caused because you've passed a non-existent file path to the FileInputStream constructor:
... = FileInputStream("/org/owasp/crypto/testkeystore.ks")
Make sure the path you provide is a valid relative or absolute path.
Related
I am developing a Java application and I want to add a special encryption method - NTRU. Before that I used symmetric and asymmetric methods supplied by the bouncycastle provider however now I can't understand how works Post-Quantum algorithms in bouncycastle due to lack of any information on the network. Please help me implement NTRU text encryption code.
I tried using the light-weight API but it didn't work, my code refuses to do anything
This is a complete running example for NTRU, it is not a general encryption algorithm but a key exchange algorithm (KEM). To get this to work you need an actual Bouncy Castle lib available (I tested with BC verson 1.71 and 1.72).
It is taken from my Android app to test some Post Quantum Algorithms (PQC) with complete code here: https://github.com/MichaelsPlayground/PostQuantumCryptographyBc172
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.jcajce.SecretKeyWithEncapsulation;
import org.bouncycastle.jcajce.spec.KEMExtractSpec;
import org.bouncycastle.jcajce.spec.KEMGenerateSpec;
import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
import org.bouncycastle.pqc.legacy.crypto.ntru.NTRUEncryptionKeyGenerationParameters;
import org.bouncycastle.pqc.legacy.crypto.ntru.NTRUEncryptionKeyPairGenerator;
import org.bouncycastle.pqc.legacy.crypto.ntru.NTRUEncryptionParameters;
import org.bouncycastle.pqc.legacy.crypto.ntru.NTRUEncryptionPrivateKeyParameters;
import org.bouncycastle.pqc.legacy.crypto.ntru.NTRUEncryptionPublicKeyParameters;
import org.bouncycastle.pqc.legacy.crypto.ntru.NTRUEngine;
import org.bouncycastle.util.Arrays;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.KeyGenerator;
public class PqcNtruKem {
public static void main(String[] args) {
//Security.addProvider(new BouncyCastleProvider());
// we do need the regular Bouncy Castle file that includes the PQC provider
// get Bouncy Castle here: https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on
// tested with BC version 1.71
if (Security.getProvider("BCPQC") == null) {
Security.addProvider(new BouncyCastlePQCProvider());
}
System.out.println("PQC NTRU kem");
System.out.println("\n************************************\n" +
"* # # SERIOUS SECURITY WARNING # # *\n" +
"* This program is a CONCEPT STUDY *\n" +
"* for the algorithm *\n" +
"* NTRU [key exchange mechanism] *\n" +
"* The program is using an *\n" +
"* parameter set that I cannot *\n" +
"* check for the correctness of the *\n" +
"* output and other details *\n" +
"* *\n" +
"* DO NOT USE THE PROGRAM IN *\n" +
"* ANY PRODUCTION ENVIRONMENT *\n" +
"************************************");
// as there are 7 parameter sets available the program runs all of them
NTRUEncryptionKeyGenerationParameters[] ntruEncryptionKeyGenerationParameterSets = {
NTRUEncryptionKeyGenerationParameters.EES1087EP2,
NTRUEncryptionKeyGenerationParameters.EES1171EP1,
NTRUEncryptionKeyGenerationParameters.EES1499EP1,
NTRUEncryptionKeyGenerationParameters.APR2011_439,
NTRUEncryptionKeyGenerationParameters.APR2011_439_FAST,
NTRUEncryptionKeyGenerationParameters.APR2011_743,
NTRUEncryptionKeyGenerationParameters.APR2011_743_FAST,
};
// short name of the parameters for the summary print out
String[] ntruEncryptionKeyGenerationParameterNames = {
"EES1087EP2",
"EES1171EP1",
"EES1499EP1",
"APR2011_439",
"APR2011_439_FAST",
"APR2011_743",
"APR2011_743_FAST"
};
// statistics
int nrOfSpecs = ntruEncryptionKeyGenerationParameterSets.length;
String[] parameterSpecName = new String[nrOfSpecs];
int[] privateKeyLength = new int[nrOfSpecs];
int[] publicKeyLength = new int[nrOfSpecs];
int[] encryptedKeyLength = new int[nrOfSpecs];
boolean[] encryptionKeysEquals = new boolean[nrOfSpecs];
// data to encrypt is usually a 32 bytes long (randomly generated) AES key
String keyToEncryptString = "1234567890ABCDEF1122334455667788";
byte[] keyToEncrypt = keyToEncryptString.getBytes(StandardCharsets.UTF_8);
for (int i = 0; i < nrOfSpecs; i++) {
// generation of the NTRU key pair
NTRUEncryptionKeyGenerationParameters ntruEncryptionKeyGenerationParameters = ntruEncryptionKeyGenerationParameterSets[i];
String ntruParameterSpecName = ntruEncryptionKeyGenerationParameterNames[i];
parameterSpecName[i] = ntruParameterSpecName;
System.out.println("\nNTRU KEM with parameterset " + ntruParameterSpecName);
AsymmetricCipherKeyPair keyPair = generateNtruKeyPair(ntruEncryptionKeyGenerationParameters);
// get private and public key
AsymmetricKeyParameter privateKey = keyPair.getPrivate();
AsymmetricKeyParameter publicKey = keyPair.getPublic();
// storing the key as byte array
byte[] privateKeyByte = ((NTRUEncryptionPrivateKeyParameters) privateKey).getEncoded();
byte[] publicKeyByte = ((NTRUEncryptionPublicKeyParameters) publicKey).getEncoded();
System.out.println("\ngenerated private key length: " + privateKeyByte.length);
System.out.println("generated public key length: " + publicKeyByte.length);
privateKeyLength[i] = privateKeyByte.length;
publicKeyLength[i] = publicKeyByte.length;
// generate the keys from a byte array
NTRUEncryptionPrivateKeyParameters privateKeyLoad = getNtruPrivateKeyFromEncoded(privateKeyByte, ntruEncryptionKeyGenerationParameters);
NTRUEncryptionPublicKeyParameters publicKeyLoad = getNtruPublicKeyFromEncoded(publicKeyByte, ntruEncryptionKeyGenerationParameters);
// generate the encryption key and the encapsulated key
System.out.println("\nEncryption side: generate the encryption key");
byte[] encryptedKey = pqcNtruEncryptKey(publicKeyLoad, keyToEncrypt);
System.out.println("encrypted key length: " + encryptedKey.length
+ " key: " + bytesToHex(encryptedKey));
encryptedKeyLength[i] = encryptedKey.length;
System.out.println("\nDecryption side: receive the encrypted key and decrypt it to the decryption key");
byte[] decryptedKey = pqcNtruDecryptKey(privateKeyLoad, encryptedKey);
System.out.println("decryption key length: " + decryptedKey.length + " key: " + bytesToHex(decryptedKey));
boolean keysAreEqual = Arrays.areEqual(keyToEncrypt, decryptedKey);
System.out.println("decrypted key is equal to keyToEncrypt: " + keysAreEqual);
encryptionKeysEquals[i] = keysAreEqual;
}
System.out.println("\nTest results");
System.out.println("parameter spec name priKL pubKL encKL keyE");
for (int i = 0; i < nrOfSpecs; i++) {
System.out.format("%-20s%6d%8d%6d%6b%n", parameterSpecName[i], privateKeyLength[i], publicKeyLength[i], encryptedKeyLength[i], encryptionKeysEquals[i]);
}
System.out.println("Legend: priKL privateKey length, pubKL publicKey length, encKL encryption key length, keyE encryption keys are equal\n");
}
private static AsymmetricCipherKeyPair generateNtruKeyPair(NTRUEncryptionKeyGenerationParameters ntruEncryptionKeyGenerationParameters) {
NTRUEncryptionKeyPairGenerator ntruEncryptionKeyPairGenerator = new NTRUEncryptionKeyPairGenerator();
ntruEncryptionKeyPairGenerator.init(ntruEncryptionKeyGenerationParameters);
AsymmetricCipherKeyPair kp = ntruEncryptionKeyPairGenerator.generateKeyPair();
return kp;
}
private static byte[] pqcNtruEncryptKey(AsymmetricKeyParameter publicKey, byte[] keyToEncrypt) {
NTRUEngine ntru = new NTRUEngine();
ntru.init(true, publicKey);
try {
return ntru.processBlock(keyToEncrypt, 0, keyToEncrypt.length);
} catch (InvalidCipherTextException e) {
e.printStackTrace();
return null;
}
}
private static byte[] pqcNtruDecryptKey(AsymmetricKeyParameter privateKey, byte[] encryptedKeyToDecrypt) {
NTRUEngine ntru = new NTRUEngine();
ntru.init(false, privateKey);
try {
return ntru.processBlock(encryptedKeyToDecrypt, 0, encryptedKeyToDecrypt.length);
} catch (InvalidCipherTextException e) {
e.printStackTrace();
return null;
}
}
public static SecretKeyWithEncapsulation pqcGenerateNtruEncryptionKey(PublicKey publicKey) {
KeyGenerator keyGen = null;
try {
keyGen = KeyGenerator.getInstance("Frodo", "BCPQC");
keyGen.init(new KEMGenerateSpec((PublicKey) publicKey, "AES"), new SecureRandom());
SecretKeyWithEncapsulation secEnc1 = (SecretKeyWithEncapsulation) keyGen.generateKey();
return secEnc1;
} catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
return null;
}
}
public static byte[] pqcGenerateFrodoDecryptionKey(PrivateKey privateKey, byte[] encapsulatedKey) {
KeyGenerator keyGen = null;
try {
keyGen = KeyGenerator.getInstance("Frodo", "BCPQC");
keyGen.init(new KEMExtractSpec((PrivateKey) privateKey, encapsulatedKey, "AES"), new SecureRandom());
SecretKeyWithEncapsulation secEnc2 = (SecretKeyWithEncapsulation) keyGen.generateKey();
return secEnc2.getEncoded();
} catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
return null;
}
}
private static NTRUEncryptionPrivateKeyParameters getNtruPrivateKeyFromEncoded(byte[] encodedKey, NTRUEncryptionKeyGenerationParameters ntruEncryptionKeyGenerationParameters) {
NTRUEncryptionParameters ntruEncryptionParameters = ntruEncryptionKeyGenerationParameters.getEncryptionParameters();
try {
return new NTRUEncryptionPrivateKeyParameters(encodedKey, ntruEncryptionParameters);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private static NTRUEncryptionPublicKeyParameters getNtruPublicKeyFromEncoded(byte[] encodedKey, NTRUEncryptionKeyGenerationParameters ntruEncryptionKeyGenerationParameters) {
NTRUEncryptionParameters ntruEncryptionParameters = ntruEncryptionKeyGenerationParameters.getEncryptionParameters();
return new NTRUEncryptionPublicKeyParameters(encodedKey, ntruEncryptionParameters);
}
private static PublicKey getFrodoPublicKeyFromEncoded(byte[] encodedKey) {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(encodedKey);
KeyFactory keyFactory = null;
try {
keyFactory = KeyFactory.getInstance("Frodo", "BCPQC");
return keyFactory.generatePublic(x509EncodedKeySpec);
} catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidKeySpecException e) {
e.printStackTrace();
return null;
}
}
private static String bytesToHex(byte[] bytes) {
StringBuffer result = new StringBuffer();
for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
return result.toString();
}
}
When I encrypt plain text using Dart,and encrypted text is decrypted from Java code, I get this error:
javax.crypto.BadPaddingException: pad block corrupted
at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher$BufferedGenericBlockCipher.doFinal(Unknown Source)
at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(Unknown Source)
at javax.crypto.Cipher.doFinal(Cipher.java:2168)
at AesUtil.doFinal(AesUtil.java:75)
at AesUtil.decrypt(AesUtil.java:60)
at Main.main(Main.java:18)
Same IV, salt and passphase value using Java side for key generation, but the generated key is different and also cipher test is different. I am using same method for key generation. I don't know what is missing in Dart code.
dependencies:
encrypt: ^5.0.1
hex: ^0.2.0
password_hash_plus: ^4.0.0
Dart code is:
var random = Random.secure();
var values = List<int>.generate(16, (i) => random.nextInt(255));
// final salt = aes.IV.fromSecureRandom(16);
final salt = hex.encode(values);
final generator = PBKDF2(hashAlgorithm: sha1);
final key = aes.Key.fromBase64(generator.generateBase64Key("1234567891234567", salt, 1000, 16));
final iv = aes.IV.fromSecureRandom(16);
final encrypter =
aes.Encrypter(aes.AES(key, mode: aes.AESMode.cbc, padding: 'PKCS7'));
final encrypted = encrypter.encrypt(st.password!, iv: iv);
var str = '${iv.base16}::${salt}::${encrypted.base64}';
var bytes = utf8.encode(str);
var base64Str = base64.encode(bytes);
//final decrypt = encrypter.decrypt64("/vvAYMc3rgCvPvuSVU/qQw==", iv: iv);
print(
'------------------------------,\n encrypt ${(encrypted.base64)}-----------'
//'--\ndecrypted ${decrypt}-----------base64--------$base64Str-----'
'\nkey = ${key.base64} array--\niv = ${iv.base16}--salt= {${salt}');
And Java code is:
class Main {
public static void main(String[] args) {
AesUtil aesUtil = new AesUtil();
String encrypt = aesUtil.encrypt("b9266c74df614967d9acaa2878bff87c", "6ab7c799d6411f9d0c8e048ad526eeee", "1234567891234567", "Jitu#123456");
String a = aesUtil.decrypt("01e6a073a4255c92e704bd94d76d75c5", "98a21e07ed34afc523c5f5938c9202db", "1234567891234567", "MumTfpnzZh9bk94yiTuA+g==");
System.out.println("encrypt = " + encrypt + " \ndecrpty valaue----" + a);
}
}
Encryption code in Java:
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.springframework.stereotype.Component;
public class AesUtil {
private final int keySize;
private final int iterationCount;
private final Cipher cipher;
public AesUtil() {
this.keySize = 128;
this.iterationCount = 1000;
try {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
throw fail(e);
} catch (NoSuchProviderException e) {
throw new RuntimeException(e);
}
}
public String encrypt(String salt, String iv, String passphrase, String plaintext) {
try {
SecretKey key = generateKey(salt, passphrase);
System.out.println("encryption key-------= " + base64(key.getEncoded()));
byte[] encrypted = doFinal(Cipher.ENCRYPT_MODE, key, iv, plaintext.getBytes("ISO-8859-1"));
return base64(encrypted);
} catch (UnsupportedEncodingException e) {
throw fail(e);
}
}
public String decrypt(String salt, String iv, String passphrase, String ciphertext) {
try {
SecretKey key = generateKey(salt, passphrase);
System.out.println("decrypt key-------= " + base64(key.getEncoded()));
byte[] decrypted = doFinal(Cipher.DECRYPT_MODE, key, iv, base64(ciphertext));
return new String(decrypted, "ISO-8859-1");
} catch (UnsupportedEncodingException e) {
return null;
} catch (Exception e) {
return null;
}
}
private byte[] doFinal(int encryptMode, SecretKey key, String iv, byte[] bytes) {
try {
IvParameterSpec IivParameterSpec = new IvParameterSpec(hex(iv));
System.out.println("----iv--= " + hex(IivParameterSpec.getIV()));
cipher.init(encryptMode, key, IivParameterSpec);
return cipher.doFinal(bytes);
} catch (InvalidKeyException
| InvalidAlgorithmParameterException
| IllegalBlockSizeException
| BadPaddingException e) {
e.printStackTrace();
return null;
}
}
private SecretKey generateKey(String salt, String passphrase) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] s = hex(salt);
System.out.println("salt-= " + hex(s));
KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), s, iterationCount, keySize);
SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
return key;
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
return null;
}
}
public static String random(int length) {
byte[] salt = new byte[length];
new SecureRandom().nextBytes(salt);
return hex(salt);
}
public static String base64(byte[] bytes) {
return Base64.encodeBase64String(bytes);
}
public static byte[] base64(String str) {
return Base64.decodeBase64(str);
}
public static String hex(byte[] bytes) {
return Hex.encodeHexString(bytes);
}
public static byte[] hex(String str) {
try {
return Hex.decodeHex(str.toCharArray());
} catch (DecoderException e) {
throw new IllegalStateException(e);
}
}
private IllegalStateException fail(Exception e) {
return null;
}
public static byte[][] GenerateKeyAndIV(int keyLength, int ivLength, int iterations, byte[] salt, byte[] password, MessageDigest md) {
int digestLength = md.getDigestLength();
int requiredLength = (keyLength + ivLength + digestLength - 1) / digestLength * digestLength;
byte[] generatedData = new byte[requiredLength];
int generatedLength = 0;
try {
md.reset();
// Repeat process until sufficient data has been generated
while (generatedLength < keyLength + ivLength) {
// Digest data (last digest if available, password data, salt if available)
if (generatedLength > 0)
md.update(generatedData, generatedLength - digestLength, digestLength);
md.update(password);
if (salt != null)
md.update(salt, 0, 8);
md.digest(generatedData, generatedLength, digestLength);
// additional rounds
for (int i = 1; i < iterations; i++) {
md.update(generatedData, generatedLength, digestLength);
md.digest(generatedData, generatedLength, digestLength);
}
generatedLength += digestLength;
}
// Copy key and IV into separate byte arrays
byte[][] result = new byte[2][];
result[0] = Arrays.copyOfRange(generatedData, 0, keyLength);
if (ivLength > 0)
result[1] = Arrays.copyOfRange(generatedData, keyLength, keyLength + ivLength);
return result;
} catch (DigestException e) {
throw new RuntimeException(e);
} finally {
// Clean out temporary data
Arrays.fill(generatedData, (byte) 0);
}
}
}
The decryption fails because in both codes different salts are used and therefore different keys are generated. Ultimately, this is due to an inappropriate design of the PBKDF2 implementation of the password_hash_plus Dart library.
In the Java code a random salt is applied, in this case 0xb9266c74df614967d9acaa2878bff87c. In main(), the salt is passed hex encoded to encrypt(), hex decoded in generateKey(), and the resulting byte sequence is used for key derivation.
The generateBase64Key() method of the password_hash_plus library, on the other hand, expects the salt as string and does a UTF-8 encoding internally, see here. Therefore, only salts that are UTF-8 decodable can be processed. This is generally not true for random salts, since these are corrupted by a UTF-8 decoding.
The hex encoding of the salt applied in the Dart code does not work either, of course, because generateBase64Key() does not perform a hex decoding internally but a UTF-8 encoding.
Since salts are generally random byte sequences, the design of the PBKDF2 implementation of the password_hash_plus library is unsuitable. Instead, an implementation is required where the salt is passed as byte sequence (Uint8List or List<int>), e.g. the PBKDF2 implementation of PointyCastle:
import 'package:pointycastle/export.dart';
import 'dart:typed_data';
...
final key = aes.Key(deriveKey("1234567891234567", Uint8List.fromList(values))); // Raw salt for key derivation!
final salt = hex.encode(values); // Hex encoded salt for output!
...
Uint8List deriveKey(String passphrase, Uint8List salt){
Uint8List passphraseBytes = Uint8List.fromList(utf8.encode(passphrase));
KeyDerivator derivator = PBKDF2KeyDerivator(HMac(SHA1Digest(), 64)); // 64 byte block size
Pbkdf2Parameters params = Pbkdf2Parameters(salt, 1000, 16); // 16 byte key size
derivator.init(params);
return derivator.process(passphraseBytes);
}
...
The Dart code returns salt and IV hex encoded and the ciphertext Base64 encoded. If this data is passed with these encodings to the decrypt() method of the Java code, decryption is successful.
Be aware that an iteration count of 1000 is generally too small for PBKDF2.
I am trying to write a program that will encrypt a file based on a password the user enters when calling the program. The salt of the password should be included in the file. I believe the program is encrypting the file properly but when I go to decrypt the file the program throws
java.security.InvalidAlgorithmParameterException: Missing parameter type: IV expected.
I am not sure as to why this would be the case as I am not generating an IV as such. To call the program it is enc "password" fileToEncrypt.txt destinationFile.enc.
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.util.HexFormat;
import java.util.logging.Logger;
public class FileEncryptor {
private static final Logger LOG = Logger.getLogger(FileEncryptor.class.getSimpleName());
private static final String ALGORITHM = "AES";
private static final String CIPHER = "AES/CBC/PkCS5PADDING";
public static void main(String[] args) {
if (args.length == 4) {
if (args[0].equals("enc") || args[0].equals("dec")) {
try {
if (args[0].equals("enc")) {
SecureRandom sr = new SecureRandom();
PBEKeySpec pbeKeySpec;
PBEParameterSpec pbeParamSpec;
SecretKeyFactory keyFac;
byte[] salt = new byte[16];
sr.nextBytes(salt);
int count = 1000;
pbeParamSpec = new PBEParameterSpec(salt, count);
char[] password = args[1].toCharArray();
pbeKeySpec = new PBEKeySpec(password);
keyFac = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_256");
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
Cipher pbeCipher = Cipher.getInstance("PBEWithHmacSHA256AndAES_256");
pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
if (encrypt(pbeCipher, args[2], args[3],salt)) {
LOG.info("Encryption finished, saved at " + args[3]);
}
} else {
PBEKeySpec pbeKeySpec;
SecretKeyFactory keyFac;
char[] password = args[1].toCharArray();
pbeKeySpec = new PBEKeySpec(password);
keyFac = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_256");
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
Cipher pbeCipher = Cipher.getInstance("PBEWithHmacSHA256AndAES_256");
if (decrypt(pbeCipher, pbeKey, args[2], args[3])) {
LOG.info("Decryption complete, open " + args[3]);
}
}
} catch (Exception e) {
exceptionHandle(e);
}
} else {
errorHandle("Please call encrypt or decrypt");
}
} else {
errorHandle("Please call program with 4 arguments");
}
}
/**
* Method to encrypt a file with the Key and IV generated by the program and write the results to a
* new file.
*
* #param cipher cipher generated by the program early
* #param input name of the file to be encrypted
* #param output name of the new file after encryption
* #return boolean if encryption was successful or not.
*/
public static boolean encrypt(Cipher cipher, String input, String output,byte[] salt) {
LOG.info("File to be Encrypted: " + input);
try (FileInputStream in = new FileInputStream(input);
FileOutputStream out = new FileOutputStream(output);
CipherOutputStream encryptedOutputStream = new CipherOutputStream(out, cipher)) {
out.write(salt);
byte[] buffer = new byte[1024];
int nread;
while ((nread = in.read(buffer)) > 0) {
encryptedOutputStream.write(buffer, 0, nread);
}
encryptedOutputStream.flush();
} catch (IOException e) {
exceptionHandle(e);
}
return true;
}
/**
* Method to decrypt a file with the Key inputted by the user and IV stored in the file. Then write the results to a
* new file.
*
* #param cipher cipher generated by the program early based on key and IV provided.
* #param input name of the file to be decrypted
* #param output name of the new file after decryption
* #return boolean if decryption was successful or not
*/
public static boolean decrypt(Cipher cipher,SecretKey pbeKey, String input, String output) {
int count = 1000;
try (FileInputStream in = new FileInputStream(input);
CipherInputStream cipherInputStream = new CipherInputStream(in, cipher);
FileOutputStream out = new FileOutputStream(output)) {
byte[] salt = new byte[16];
in.read(salt);
byte[] buffer = new byte[1024];
cipher.init(Cipher.DECRYPT_MODE, pbeKey,new PBEParameterSpec(salt, count));
int nread;
while ((nread = cipherInputStream.read(buffer)) > 0) {
out.write(buffer, 0, nread);
}
out.flush();
} catch (IOException | InvalidAlgorithmParameterException | InvalidKeyException ex) {
ex.printStackTrace();
exceptionHandle(ex);
return false;
}
return true;
}
/**
* Methods prints a message to the console to let the user know what mistakes they made when running the program.
*
* #param message to be printed to the console.
*/
public static void errorHandle(String message) {
System.out.println(message);
}
/**
* Program prints a message to the user to let them know what went wrong in the program based on what exception
* was thrown early in the program and give them clear understanding of what went wrong.
*
* #param e Exception caught be the program
*/
public static void exceptionHandle(Exception e) {
if(e.getClass() == BadPaddingException.class){
System.out.println("hello");
LOG.info("Please input correct key. " + e.getMessage());
}
if (e.getClass() == FileNotFoundException.class) {
LOG.info("Please call enc or dec with an existing file. " + e.getMessage());
}
if (e.getClass() == InvalidKeyException.class) {
LOG.info("Please put correct Algorithm. " + e.getMessage());
}
if (e.getClass() == NoSuchAlgorithmException.class) {
LOG.info("Please put correct Cipher " + e.getMessage());
}
if (e.getClass() == InvalidAlgorithmParameterException.class) {
LOG.info("Please put correct IV length. " + e.getMessage());
}
if (e.getClass() == IllegalArgumentException.class) {
LOG.info("Please check length Key and IV. " + e.getMessage());
}
if (e.getClass() == NoSuchPaddingException.class) {
LOG.info("Please check Padding. " + e.getMessage());
}
}
public static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X ", b));
}
return sb.toString();
}
}
1) I have a private key in a txt file
2) I have written a decrypt method
while running the decrypt method I am getting the below mentioned exception.
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.EncodedKeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Calendar;
import java.util.Random;
import javax.crypto.Cipher;
import android.util.Base64;
public static String decrypt(String inputString, byte[] keyBytes) {
String resultStr = null;
Calendar cal = Calendar.getInstance();
int mDay = cal.get(Calendar.DAY_OF_MONTH);
Random generator = new Random(mDay);
int num = (generator.nextInt()) % 100;
String salt = "XXwerr" + num;
PrivateKey privateKey = null;
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(keyBytes);
privateKey = keyFactory.generatePrivate(privateKeySpec);
} catch (Exception e) {
System.out.println("Exception privateKey::::::::::::::::: "
+ e.getMessage());
}
byte[] decodedBytes = null;
try {
Cipher c = Cipher.getInstance("RSA");
//Also tried
// Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding");
c.init(Cipher.DECRYPT_MODE, privateKey);
decodedBytes = c.doFinal(Base64.decode(inputString, Base64.NO_CLOSE));
} catch (Exception e) {
System.out.println("Exception privateKey1::::::::::::::::: "
+ e.getMessage());
e.printStackTrace();
}
if (decodedBytes != null) {
resultStr = new String(decodedBytes);
System.out.println("resultStr:::" + resultStr + ":::::");
resultStr = resultStr.replace(salt, "");
}
return resultStr;
}
Following is the main method
public static void main(String[] args) {
FileInputStream fileInputStream = null;
File file = new File("/Users/buta1/Downloads/private.txt");
byte[] bFile = new byte[(int) file.length()];
try {
// convert file into array of bytes
fileInputStream = new FileInputStream(file);
fileInputStream.read(bFile);
fileInputStream.close();
// for (int i = 0; i < bFile.length; i++) {
// System.out.println((char)bFile[i]);
// }
decrypt("08F8CFE58F2E707C314F4D7894E0F1", bFile);
System.out.println("Done");
} catch (Exception e) {
e.printStackTrace();
}
}
THE ENCRYPTION METHOD USED IS AS follows USING ANDROID Base64 class
public static String encrypt(String inputString, byte [] keyBytes)
{
Calendar cal = Calendar.getInstance();
int mDay = cal.get(Calendar.DAY_OF_MONTH);
//System.out.println("Day of month :::" + mDay);
String encryptedString = "";
Key publicKey = null;
try {
Random generator = new Random(mDay);
int num = (generator.nextInt() ) % 100;
String salt = "xx"+num;
inputString += salt;
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
publicKey = keyFactory.generatePublic(publicKeySpec);
} catch (Exception e) {
System.out.println("Exception rsaEncrypt::::::::::::::::: "+
e.getMessage());
}
// Encode the original data with RSA public key
byte[] encodedBytes = null;
try {
Cipher c = Cipher.getInstance("RSA");
c.init(Cipher.ENCRYPT_MODE, publicKey);
encodedBytes = c.doFinal(inputString.getBytes());
encryptedString =
Base64.encodeToString(encodedBytes, Base64.NO_CLOSE);
} catch (Exception e) {
System.out.println("Exception rsaEncrypt::::::::::::::::: "+
e.getMessage());
}
return encryptedString;
}
After using commons codec Base 64
Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding ");
getting following error
javax.crypto.BadPaddingException: Decryption error
at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:380)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:291)
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:365)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:391)
at javax.crypto.Cipher.doFinal(Cipher.java:2087)
at RSAEncryption.decrypt(RSAEncryption.java:41)
at RSAEncryption.main(RSAEncryption.java:108)
I am assuming that you are trying to run Android code from inside your IDE with the android.jar on your classpath (e.g. by using the ADT Plugin in eclipse). Then this looks like "working as intended" (cf. https://code.google.com/p/android/issues/detail?id=33188).
The android.jar only contains stub implementations for all classes, because the intended way would be to run that code inside an emulator.
If you are trying to write code for Android and you find running the code inside an emulator to be too cumbersome, you could try Roboelectric, which basically replaces all those stubs with real implementations and allows you to run the code from inside your IDE.
On the other hand, if you are not trying to write code for Android you can simply replace android.util.Base64 with org.apache.commons.codec.binary.Base64 or java.util.Base64.Decoder (since Java 8).
I am new to encryption and decryption. I was given a PSKC file and asked for decryption. I was given the password for decryption. The PSKC file doenot have initialization vector value.
I wrote the code trying to decrypt it. But i am unsuccessful in achieving the outcome.
below is the PSKC file example
<?xml version="1.0"?>
<pskc:KeyContainer xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:pkcs5="http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" xmlns:xenc11="http://www.w3.org/2009/xmlenc11#" xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc">
<pskc:EncryptionKey>
<xenc11:DerivedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" xmlns:pkcs5="http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#" xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xenc11="http://www.w3.org/2009/xmlenc11#">
<xenc11:KeyDerivationMethod Algorithm="http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#pbkdf2">
<pkcs5:PBKDF2-params xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:xenc11="http://www.w3.org/2009/xmlenc11#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:pkcs5="http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#">
<Salt>
<Specified>EW0h0yUcDX72WU9UiKiCwDpXsJg=</Specified>
</Salt>
<IterationCount>128</IterationCount>
<KeyLength>16</KeyLength>
<PRF />
</pkcs5:PBKDF2-params>
</xenc11:KeyDerivationMethod>
<xenc:ReferenceList>
<xenc:DataReference URI="#ED" />
</xenc:ReferenceList>
<xenc11:MasterKeyName>Passphrase1</xenc11:MasterKeyName>
</xenc11:DerivedKey>
</pskc:EncryptionKey>
<pskc:MACMethod Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1">
<pskc:MACKey>
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
<xenc:CipherData>
<xenc:CipherValue>jq/NdikC7AZf0Z+HEL5NrCICV8XW+ttzl/8687hVGHceoyJAaFws+111plQH6Mlg</xenc:CipherValue>
</xenc:CipherData>
</pskc:MACKey>
</pskc:MACMethod>
<pskc:KeyPackage>
<pskc:DeviceInfo>
<pskc:Manufacturer>Gemalto</pskc:Manufacturer>
<pskc:SerialNo>GAKT000047A5</pskc:SerialNo>
</pskc:DeviceInfo>
<pskc:CryptoModuleInfo>
<pskc:Id>CM_ID_007</pskc:Id>
</pskc:CryptoModuleInfo>
<pskc:Key Id="GAKT000047A5" Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:totp">
<pskc:Issuer>Issuer0</pskc:Issuer>
<pskc:AlgorithmParameters>
<pskc:ResponseFormat Encoding="DECIMAL" Length="6" />
</pskc:AlgorithmParameters>
<pskc:Data>
<pskc:Secret>
<pskc:EncryptedValue>
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
<xenc:CipherData>
<xenc:CipherValue>pM7VB/KomPjq2cKaxPr5cKT1tUZN5tGMI+u1XKJTG1la+ThraPpLKlL2plKk6vQE</xenc:CipherValue>
</xenc:CipherData>
</pskc:EncryptedValue>
<pskc:ValueMAC>lbu+9OcLArnj6mS7KYOKDa4zRU0=</pskc:ValueMAC>
</pskc:Secret>
<pskc:Time>
<pskc:PlainValue>0</pskc:PlainValue>
</pskc:Time>
<pskc:TimeInterval>
<pskc:PlainValue>30</pskc:PlainValue>
</pskc:TimeInterval>
</pskc:Data>
</pskc:Key>
</pskc:KeyPackage>
</pskc:KeyContainer>
below is the java code which i have written for decryption.
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import com.sun.org.apache.xerces.internal.impl.dv.util.HexBin;
public class test {
/**
* #param args
*/
public static void main(String[] args) {
test te = new test();
try {
te.decryptSeedValue();
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println(e.getMessage());
e.printStackTrace();
}
// TODO Auto-generated method stub
}
public static HashMap decryptSeedValue()throws Exception{
String password = "G?20R+I+3-/UcWIN";
String pbesalt ="EW0h0yUcDX72WU9UiKiCwDpXsJg=";
String iv = "aaaaaaaaaaaaaaaaaaaaaaaa";
int iteration = 128;
String value = "pM7VB/KomPjq2cKaxPr5cKT1tUZN5tGMI+u1XKJTG1la+ThraPpLKlL2plKk6vQE";
String valueDigest = "lbu+9OcLArnj6mS7KYOKDa4zRU0=";
byte[] cipherText =null;
//some parameters need to decode from Base64 to byte[]
byte[] data = base64Decode(value.getBytes());
//System.out.println("data(hex string) = " + HexBin.encode(data));//debug
byte[] salt = base64Decode(pbesalt.getBytes());
//System.out.println("salt(hex string) = " + HexBin.encode(salt));//debug
byte[] initVec = base64Decode(iv.getBytes());
//System.out.println("iv(hex string) = " + HexBin.encode(initVec));//debug
//perform PBE key generation and AES/CBC/PKCS5Padding decrpyption
HashMap hs = myFunction(data, password, initVec, salt, iteration);
String seedValue = (String)hs.get("DECRYPTED_SEED_VALUE");
byte[] temp = (byte[])hs.get("HASH_OUTPUT");
//System.out.println("hashed output(hex string) = " + HexBin.encode(temp));//debug
//perform Base64 Encode
byte[] out = base64Encode(temp);
String output = new String((out));
System.out.println("output = "+output);
System.out.println("valueD = "+valueDigest);
//System.out.println("hashed output(base64) = " + output);
//compare the result
if(output.equals(valueDigest)){
System.out.println("Hash verification successful for:-->" );
System.out.println("\n");
//hs.put("SEED_VALUE", HexBin.encode(temp));
hs.put("SEED_VALUE", seedValue);
return hs;
}
else{
System.out.println("Hash verification failed for :-->");
return null;
}
}
public static HashMap myFunction(byte[] data, String password, byte[] initVec,
byte[] salt, int iteration) throws Exception{
PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator();
byte[] pBytes = password.getBytes();
generator.init(pBytes, salt, iteration);
int keysize = 128;//fixed at AES key of 16 bytes
int ivsize = initVec.length;
ParametersWithIV params = (ParametersWithIV) generator.generateDerivedParameters(keysize, ivsize);
KeyParameter keyParam = (KeyParameter) params.getParameters();
//System.out.println("derived key = " + HexBin.encode(keyParam.getKey()));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec paramSpec = new IvParameterSpec(initVec);
SecretKeySpec key = new SecretKeySpec(keyParam.getKey(), "AES");
cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
//perform decryption
byte[] secret = cipher.doFinal(data);
//display the 20 bytes secret of the token
//System.out.println("token secret(hex string) = " + HexBin.encode(secret));
//perform HMAC-SHA-1
byte[] output = hmac_sha1(secret, keyParam.getKey());
HashMap hs = new HashMap();
hs.put("ENCRYPTION_KEY", HexBin.encode(keyParam.getKey()));
hs.put("HASH_OUTPUT", output);
hs.put("DECRYPTED_SEED_VALUE", HexBin.encode(secret));
return hs;
}
public static byte[] base64Encode(byte[] passwordBytes) throws NoSuchAlgorithmException {
Base64 base64 = new Base64();
byte[] hashBytes2 = base64.encode(passwordBytes);
return hashBytes2;
}
public static byte[] base64Decode(byte[] passwordBytes) throws NoSuchAlgorithmException {
Base64 base64 = new Base64();
byte[] hashBytes2 = base64.decode(passwordBytes);
return hashBytes2;
}
public static byte[] hmac_sha1(byte[] dataByte, byte[] keyByte) throws Exception{
Mac hmacSha1;
hmacSha1 = Mac.getInstance("HmacSHA1");
SecretKeySpec macKey = new SecretKeySpec(keyByte, "HmacSHA1");
hmacSha1.init(macKey);
byte[] result = hmacSha1.doFinal(dataByte);
return result;
}
/**
* Convert a byte array of 8 bit characters into a String.
*
* #param bytes the array containing the characters
* #param length the number of bytes to process
* #return a String representation of bytes
*/
private static String toString(
byte[] bytes,
int length)
{
char[] chars = new char[length];
for (int i = 0; i != chars.length; i++)
{
chars[i] = (char)(bytes[i] & 0xff);
}
return new String(chars);
}
}
it doesn't throw any exception, but it prints "Hash verification failed for" which is defined in my code when decryption fails.
Can some one please help me out.
As per the pskc standard http://www.rfc-editor.org/rfc/rfc6030.txt the IV is prepended to the ciphervalue. This is aes128, so it'll be the first 16 bytes once it's been base64 decoded.
Adding onto what bcharlton is describing; what you are not doing is check the hmac_sha1 for the encrypted data (which has the iv prepended in encrypted form), using the MACKey described in the xml document.
With AES-128 CBC the initialization vector is explicitly defined, and since there is no verification built into it, it uses HMAC for it.
So given your example the following will work:
public static HashMap decryptSeedValue() throws Exception
{
String password = "G?20R+I+3-/UcWIN";
String pbesalt = "EW0h0yUcDX72WU9UiKiCwDpXsJg=";
String iv = "aaaaaaaaaaaaaaaaaaaaaaaa";
int iteration = 128;
String value = "pM7VB/KomPjq2cKaxPr5cKT1tUZN5tGMI+u1XKJTG1la+ThraPpLKlL2plKk6vQE";
String valueDigest = "lbu+9OcLArnj6mS7KYOKDa4zRU0=";
//YOU NEED THIS GUY BELOW TO VERIFY
String macKey = "jq/NdikC7AZf0Z+HEL5NrCICV8XW+ttzl/8687hVGHceoyJAaFws+111plQH6Mlg";
byte[] cipherText = null;
//some parameters need to decode from Base64 to byte[]
byte[] data = base64Decode(value.getBytes());
//System.out.println("data(hex string) = " + HexBin.encode(data));//debug
byte[] salt = base64Decode(pbesalt.getBytes());
//System.out.println("salt(hex string) = " + HexBin.encode(salt));//debug
byte[] initVec = base64Decode(iv.getBytes());
//System.out.println("iv(hex string) = " + HexBin.encode(initVec));//debug
//perform PBE key generation and AES/CBC/PKCS5Padding decrpyption
HashMap hs = myFunction(data, password, base64Decode(macKey.getBytes()), salt, iteration);
String seedValue = (String) hs.get("DECRYPTED_SEED_VALUE");
byte[] temp = (byte[]) hs.get("HASH_OUTPUT");
//System.out.println("hashed output(hex string) = " + HexBin.encode(temp));//debug
//perform Base64 Encode
byte[] out = base64Encode(temp);
String output = new String((out));
System.out.println("output = " + output);
System.out.println("valueD = " + valueDigest);
//System.out.println("hashed output(base64) = " + output);
//compare the result
if (output.equals(valueDigest)) {
System.out.println("Hash verification successful for:-->");
System.out.println("\n");
//hs.put("SEED_VALUE", HexBin.encode(temp));
hs.put("SEED_VALUE", seedValue);
return hs;
} else {
System.out.println("Hash verification failed for :-->");
return null;
}
}
public static HashMap myFunction(byte[] data, String password, byte[] macData,
byte[] salt, int iteration) throws Exception
{
PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator();
byte[] pBytes = password.getBytes();
generator.init(pBytes, salt, iteration);
byte[] iv = new byte[16];
int ivsize = iv.length;
byte[] encryptedData = new byte[data.length - ivsize];
System.arraycopy(data, 0, iv, 0, iv.length);
System.arraycopy(data, ivsize, encryptedData, 0, encryptedData.length);
byte[] maciv = new byte[16];
byte[] encryptedMac = new byte[macData.length - maciv.length];
System.arraycopy(macData, 0, maciv, 0, maciv.length);
System.arraycopy(macData, maciv.length, encryptedMac, 0, encryptedMac.length);
int keysize = 128;//fixed at AES key of 16 bytes
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iteration, keysize);
SecretKey tmp = factory.generateSecret(spec);
SecretKey key = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
dcipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
byte[] decryptedData = dcipher.doFinal(encryptedData);
// decryptedData is your token value!
dcipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(maciv));
byte[] decryptedMac = dcipher.doFinal(encryptedMac);
//display the 20 bytes secret of the token
//System.out.println("token secret(hex string) = " + HexBin.encode(secret));
//perform HMAC-SHA-1
//Use the decrypted MAC key here for hashing!
byte[] output = hmac_sha1(data, decryptedMac);
HashMap hs = new HashMap();
hs.put("ENCRYPTION_KEY", password);
hs.put("HASH_OUTPUT", output);
hs.put("DECRYPTED_SEED_VALUE", HexBin.encode(decryptedData));
return hs;
}
Keep in mind that as https://www.rfc-editor.org/rfc/rfc6030#section-6.2 describes, a different iv can be used for the MAC and the token key.