I have a PBE-generated secret key and I ciphered a string using the algorithm "PBEWithHmacSHA256AndAES_128" however I'm unable to decipher the said string.
Secret key generate:
private final static byte[] SALT = { (byte) 0xc9, (byte) 0x36, (byte) 0x78, (byte) 0x99, (byte) 0x52, (byte) 0x3e, (byte) 0xea,
(byte) 0xf2 };
PBEKeySpec keySpec = new PBEKeySpec(pwd.toCharArray(), SALT, 20 , 128);
try {
SecretKeyFactory kf = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_128");
PRIVATE_KEY = kf.generateSecret(keySpec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
e.printStackTrace();
}
Encypt String:
private static String cipherString(String string) {
PBEParameterSpec pbeParameterSpec = new PBEParameterSpec(SALT, 100);
Cipher cipher;
try {
cipher = Cipher.getInstance("PBEWithHmacSHA256AndAES_128");
cipher.init(Cipher.ENCRYPT_MODE, PRIVATE_KEY, pbeParameterSpec);
byte[] input = string.getBytes();
byte[] encryptedp = cipher.doFinal(input);
return encryptedp.toString();
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Decrypt String:
private static String decipherString(String string) {
Cipher c;
try {
c = Cipher.getInstance("PBEWithHmacSHA256AndAES_128");
c.init(Cipher.DECRYPT_MODE, PRIVATE_KEY);
byte[] input = string.getBytes();
byte[] encryptedp = c.doFinal(input);
return encryptedp.toString();
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
Just use the following code that works on J2SE. As the algorithm PBEWithHmacSHA256AndAES_128 uses AES in CBC mode internally we also have to provide an IV, which is my example generated randomly. You have to use the same IV for encryption as well as decryption. For security reasons for each encryption you should use a new random IV and save it together with the encrypted text.
SecureRandom rnd = new SecureRandom();
byte[] iv = new byte[16];
rnd.nextBytes(iv);
String password = "password";
byte[] plaintext = "plaintext".getBytes(StandardCharsets.UTF_8);
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(SALT, 10000, ivParamSpec);
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
try {
SecretKeyFactory kf = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_128");
SecretKey secretKey = kf.generateSecret(keySpec);
// On J2SE the SecretKeyfactory does not actually generate a key, it just wraps the password.
// The real encryption key is generated later on-the-fly when initializing the cipher
System.out.println(new String(secretKey.getEncoded()));
// Encrypt
Cipher enc = Cipher.getInstance("PBEWithHmacSHA256AndAES_128");
enc.init(Cipher.ENCRYPT_MODE, secretKey, pbeParamSpec);
byte[] encrypted = enc.doFinal(plaintext);
String encryptedBase64 = new BASE64Encoder().encode(encrypted);
System.out.println("Encrypted text: " + encryptedBase64);
// Decrypt
Cipher dec = Cipher.getInstance("PBEWithHmacSHA256AndAES_128");
dec.init(Cipher.DECRYPT_MODE, secretKey, pbeParamSpec);
byte[] decrypted = dec.doFinal(new BASE64Decoder().decode(encryptedBase64));
String message = new String(decrypted, StandardCharsets.UTF_8);
System.out.println(message);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
e.printStackTrace();
}
Related
I have encrypted file in C# with this method:
public byte[] Crypt(byte[] filearray, string sKey)
{
AesManaged DES = new AesManaged();
DES.Key = Encoding.UTF8.GetBytes(sKey);
DES.Mode = CipherMode.ECB;
DES.Padding = PaddingMode.PKCS7;
ICryptoTransform crypt = DES.CreateEncryptor();
byte[] cipher = crypt.TransformFinalBlock(niz, 0, filearray.Length);
String encryptedText = Convert.ToBase64String(cipher);
return cipher.ToArray();
}
Now I try to decrypt file with Java:
private static byte[] transform(string base64Key, final byte[] fileBytes) throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchProviderException
{
final byte[] keyBytes = base64Key.getBytes(StandardCharsets.UTF_8);
final byte[] ivBytes = base64Iv.getBytes(StandardCharsets.UTF_8);
final SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
final IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
byte[] transformedBytes = null;
try
{
Security.addProvider(new BouncyCastleProvider());
final Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");;
cipher.init(Cipher.DECRYPT_MODE, keySpec);
transformedBytes = cipher.doFinal(fileBytes);
}
catch (NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException |
BadPaddingException e)
{
e.printStackTrace();
}
return transformedBytes;
}
When I try to execute this function I get exception
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:2165)
Try using a different padding, for example:
Cipher.getInstance("AES/ECB/NoPadding");
or
Cipher.getInstance("AES/ECB/PKCS5Padding");
instead of
Cipher.getInstance("AES/ECB/PKCS7Padding");
I have encrypted the string in php. Decrypted successfully from php and node.js. In addition, it must be decrypted by java.
Help me an example of decrypting from java?
PHP Encrypt code
/* encrypt */
$encryption_method = 'aes-256-cbc';
$secretHash = "d95acd54c6a821ff32c52825b931c194";
$iv_size = openssl_cipher_iv_length($encryption_method);
$iv = openssl_random_pseudo_bytes($iv_size);
//encrypt
$encryptedMessage = openssl_encrypt($new_token, $encryption_method, $secretHash, 0, $iv);
//Concatenate iv with data
$ciphertext = bin2hex($iv).$encryptedMessage;
/* decrypt the cipher */
$iv_size = openssl_cipher_iv_length($encryptionMethod);
$iv = hex2bin(substr($encryptedMessageWithIv, 0, $iv_size * 2));
$decryptedMessage = openssl_decrypt(substr($encryptedMessageWithIv, $iv_size * 2), $encryptionMethod, $secretHash, 0, $iv);
Below is the encryption and decryption process for a string using AES algorithm.
private static final String key = "aesEncryptionKey";
private static final String initVector = "encryptionIntVec";
public static String encrypt(String value) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
return Base64.encodeBase64String(encrypted);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static String decrypt(String encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));
return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
In case init vector is not known, try using below code segment.
public byte[] decrypt(String encryptedString) throws DataLengthException, InvalidCipherTextException {
byte[] input = encryptedString.getBytes("UTF-8");
CBCBlockCipher cbcBlockCipher = new CBCBlockCipher(new AESEngine());
SecureRandom random = new SecureRandom();;
KeyParameter key = new KeyParameter("$secretHash".getBytes());// your key string
BlockCipherPadding blockCipherPadding = new PKCS7Padding();;
PaddedBufferedBlockCipher pbbc = new PaddedBufferedBlockCipher(cbcBlockCipher, blockCipherPadding);
int blockSize = cbcBlockCipher.getBlockSize(); // Make sure this block size is same as that used while encrypting the string.
int inputOffset = 0;
int inputLength = input.length;
int outputOffset = 0;
byte[] initializationVector = new byte[blockSize];
System.arraycopy(input, 0, initializationVector, 0, blockSize);
inputOffset += blockSize;
inputLength -= blockSize;
pbbc.init(encrypt, new ParametersWithIV(key, initializationVector));
byte[] output = new byte[pbbc.getOutputSize(inputLength) + outputOffset];
int outputLength = outputOffset + pbbc.processBytes(input, inputOffset, inputLength, output, outputOffset);
outputLength += pbbc.doFinal(output, outputLength);
return Arrays.copyOf(output, outputLength);
}
Just in case it helps someone in the future: encryption with AES/CBC/PKCS5PADDING along with the generation of a dynamic IV that is appended to the final ciphertext in Java can be done through the following code:
Encryption (JAVA)
public String encryptPlainText(String plainText) {
String cipherText = "";
try {
String keyString = "examplesecretkeyexamplesecretkey";
//Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy
Security.setProperty("crypto.policy", "unlimited");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec keyspec = new SecretKeySpec(keyString.getBytes(), "AES");
byte[] v = new byte[16];
new SecureRandom().nextBytes(v);
IvParameterSpec iv = new IvParameterSpec(v);
cipher.init(Cipher.ENCRYPT_MODE, keyspec, iv);
byte[] cipherTextByteArray = cipher.doFinal(plainText.getBytes());
//appending iv to ciphertext without any additional libraries to handle the concatenation of the two byte arrays
byte[] ivWithCipherTextByteArray = new byte[v.length + cipherTextByteArray.length];
System.arraycopy(v, 0, ivWithCipherTextByteArray, 0, v.length);
System.arraycopy(cipherTextByteArray, 0, ivWithCipherTextByteArray, v.length, cipherTextByteArray.length);
cipherText = new String(Base64.getEncoder().encode(ivWithCipherTextByteArray));
} catch (Exception e) {
LOG.info("Exception", e);
}
return cipherText;
}
Decryption of the cipherText obtained with the code above can be implemented in the following way:
Decryption (JAVA)
public static String decryptCipherText(String cipherText) {
String plainText="";
try {
String keyString = "examplesecretkeyexamplesecretkey";
Security.setProperty("crypto.policy", "unlimited");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec keyspec = new SecretKeySpec(keyString.getBytes(), "AES");
byte[] cipherTextByteArray = Base64.getDecoder().decode(cipherText);
//initialize the IvParameterSpec with the first 16 bytes of the cipherText
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(cipherTextByteArray, 0, 16));
//cipherText to decrypt is now the original one with the first 16 bytes removed (the IV used above)
cipherTextByteArray = Arrays.copyOfRange(cipherTextByteArray, 16, cipherTextByteArray.length);
cipher.init(Cipher.DECRYPT_MODE, keyspec, iv);
plainText = new String(cipher.doFinal(cipherTextByteArray));
} catch (Exception e) {
LOG.info("Exception", e);
}
return plainText;
}
I am using AES with salt and IV to encrypt and decrypt a unique ID but Its giving javax.crypto.BadPaddingException while decrypting .
Full error stack trace giving each time while decrypting the data
javax.crypto.BadPaddingException: Given final block not properly padded null
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:991)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:847)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at com.data.commons.security.impl.DataAESCrypt.decode(DataAESCrypt.java:84)
at com.data.CryptoTest.main(CryptoTest.java:13)
Encryption Method -
private static final int PASSWORD_ITERATIONS = 65536;
private static final int KEY_LENGTH = 256;
private static byte[] salt = new byte[16];
private static byte[] iv= new byte[16];
private static final String ALGORITHM = "AES/CBC/PKCS5Padding" ;
#Override
public String encode(String plainText) throws Exception {
// TODO Auto-generated method stub
try {
SecureRandom random = new SecureRandom();
random.nextBytes(salt);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(plainText.toCharArray(), salt, PASSWORD_ITERATIONS, KEY_LENGTH);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance(ALGORITHM);
AlgorithmParameters params = cipher.getParameters();
iv = params.getParameterSpec(IvParameterSpec.class).getIV();
cipher.init(Cipher.ENCRYPT_MODE, secret);
byte[] encryptedText = cipher.doFinal(plainText.getBytes("UTF-8"));
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write(salt);
outputStream.write(iv);
outputStream.write(encryptedText);
System.out.println("Salt " + DatatypeConverter.printBase64Binary(salt) + " IV " + DatatypeConverter.printBase64Binary(iv) );
return DatatypeConverter.printBase64Binary(outputStream.toByteArray());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Decryption Method
public String decode(String encodedText) throws Exception {
// TODO Auto-generated method stub
try {
byte[] ciphertext = DatatypeConverter.parseBase64Binary(encodedText);
if (ciphertext.length < 48) {
return null;
}
byte[] salt = Arrays.copyOfRange(ciphertext, 0, 16);
byte[] iv = Arrays.copyOfRange(ciphertext, 16, 32);
byte[] ct = Arrays.copyOfRange(ciphertext, 32, ciphertext.length);
System.out.println("Salt " + DatatypeConverter.printBase64Binary(salt) + " IV " + DatatypeConverter.printBase64Binary(iv) );
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(encodedText.toCharArray(), salt, PASSWORD_ITERATIONS, KEY_LENGTH);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
byte[] plaintext = cipher.doFinal(ct);
return new String(plaintext, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
I am new to JCA.
You are using plainText.toCharArray() and encodedText.toCharArray() as password in PBEKeySpec. Instead, use an actual passphrase. Currently you can only get back the plaintext message when you know the plaintext message, which isn't all that useful. Decrypting it with the encoded ciphertext as input will certainly not work.
I am trying to write methods to encrypt or decrypt String (which mostly will be numeric). It works fine for some texts(e.g.- '1010000011','1010000012', '1010000013') but gives following error for others(e.g.- '1010000014', '1010000018'):
javax.crypto.BadPaddingException: Given final block not properly
padded
Here goes my code:
public static SecretKey secKey;
private static IvParameterSpec ivspec;
static {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec("i15646dont6321wanna".toCharArray(),
"ahhalkdjfslk3205jlk3m4ljdfa85l".getBytes("UTF-8"), 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
secKey = new SecretKeySpec(tmp.getEncoded(), "AES");
byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
ivspec = new IvParameterSpec(iv);
} catch (NoSuchAlgorithmException | InvalidKeySpecException | UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public static String encryptData(String textToEncrypt) {
byte[] encryptedBytes = null;
String encryptedText = "";
try {
byte[] byteToEncrypt = textToEncrypt.getBytes(Charset.defaultCharset());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secKey, ivspec);
encryptedBytes = cipher.doFinal(byteToEncrypt);
encryptedText = new String(encryptedBytes);
} catch (NoSuchAlgorithmException | IllegalBlockSizeException | BadPaddingException
| InvalidKeyException | NoSuchPaddingException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
return encryptedText;
}
public static String decryptData(String textToDecrypt) {
byte[] decryptedBytes = null;
String decryptedText = "";
try {
byte[] byteToDecrypt = textToDecrypt.getBytes(Charset.defaultCharset());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secKey, ivspec);
decryptedBytes = cipher.doFinal(byteToDecrypt);
decryptedText = new String(decryptedBytes);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| IllegalBlockSizeException | BadPaddingException
| InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
return decryptedText;
}
The String to be encrypted is read from file and be written to some other file after encryption. This encrypted text will have to be decrypted back later. I am calling these methods in following manner:
String[] lineArray = line.split(" | "); //line is read from a file.
String encryptedText = AESEncryption.encryptData(lineArray[0]);
String decryptedText = AESEncryption.decryptData(encryptedText);
System.out.println("Original Text: " + lineArray[0] + " | Encrypted text: "
+ encryptedText + " | Decrypted again: " + decryptedText);
You are trying to pass the encrypted data as a string. This is fatal. Encrypted data is bytes, not a string. Use the Base64 conversion utilities to convert the encrypted bytes to a Base64 string.
Encryption: plaintext -> encrypted bytes -> Base64 text.
Decryption: Base64 text -> encrypted bytes -> decrypted text.
I'd like to be able to generate proper AES keys from an ECDH key agreement. However, when I generate the secret key, I get a key with an invalid length, usually 66 bits. Here's the error:
java.security.InvalidKeyException: Key length not 128/192/256 bits.
Encrypted cipher text: null
at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineInit(Unknown Source)
at javax.crypto.Cipher.init(Cipher.java:1346)
at javax.crypto.Cipher.init(Cipher.java:1282)
at Test.encryptString(Test.java:99)
at Test.main(Test.java:44)
And here is the relevant code that generates this exception:
public static byte[] iv = new SecureRandom().generateSeed(16);
public static void main(String[] args) {
String plainText = "Look mah, I'm a message!";
System.out.println("Original plaintext message: " + plainText);
// Initialize two key pairs
KeyPair keyPairA = generateECKeys();
KeyPair keyPairB = generateECKeys();
// Create two AES secret keys to encrypt/decrypt the message
SecretKey secretKeyA = generateSharedSecret(keyPairA.getPrivate(),
keyPairB.getPublic());
System.out.println(bytesToHex(secretKeyA.getEncoded()));
SecretKey secretKeyB = generateSharedSecret(keyPairB.getPrivate(),
keyPairA.getPublic());
System.out.println(bytesToHex(secretKeyB.getEncoded()));
// Encrypt the message using 'secretKeyA'
String cipherText = encryptString(secretKeyA, plainText);
System.out.println("Encrypted cipher text: " + cipherText);
// Decrypt the message using 'secretKeyB'
String decryptedPlainText = decryptString(secretKeyB, cipherText);
System.out.println("Decrypted cipher text: " + decryptedPlainText);
}
public static KeyPair generateECKeys() {
try {
ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec("secp521r1");
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
"ECDH", "BC");
keyPairGenerator.initialize(parameterSpec);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
System.out.println("Private key length: "
+ keyPair.getPrivate().getEncoded().length);
System.out.println("Public key length: "
+ keyPair.getPublic().getEncoded().length);
return keyPair;
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException
| NoSuchProviderException e) {
e.printStackTrace();
return null;
}
}
public static SecretKey generateSharedSecret(PrivateKey privateKey,
PublicKey publicKey) {
try {
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH", "BC");
keyAgreement.init(privateKey);
keyAgreement.doPhase(publicKey, true);
SecretKey key = keyAgreement.generateSecret("AES");
System.out.println("Shared key length: " + key.getEncoded().length);
return key;
} catch (InvalidKeyException | NoSuchAlgorithmException
| NoSuchProviderException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
public static String encryptString(SecretKey key, String plainText) {
try {
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
byte[] plainTextBytes = plainText.getBytes("UTF-8");
byte[] cipherText;
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
cipherText = new byte[cipher.getOutputSize(plainTextBytes.length)];
int encryptLength = cipher.update(plainTextBytes, 0,
plainTextBytes.length, cipherText, 0);
encryptLength += cipher.doFinal(cipherText, encryptLength);
return bytesToHex(cipherText);
} catch (NoSuchAlgorithmException | NoSuchProviderException
| NoSuchPaddingException | InvalidKeyException
| InvalidAlgorithmParameterException
| UnsupportedEncodingException | ShortBufferException
| IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
return null;
}
}
public static String decryptString(SecretKey key, String cipherText) {
try {
Key decryptionKey = new SecretKeySpec(key.getEncoded(),
key.getAlgorithm());
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
byte[] cipherTextBytes = hexToBytes(cipherText);
byte[] plainText;
cipher.init(Cipher.DECRYPT_MODE, decryptionKey, ivSpec);
plainText = new byte[cipher.getOutputSize(cipherTextBytes.length)];
int decryptLength = cipher.update(cipherTextBytes, 0,
cipherTextBytes.length, plainText, 0);
decryptLength += cipher.doFinal(plainText, decryptLength);
return new String(plainText, "UTF-8");
} catch (NoSuchAlgorithmException | NoSuchProviderException
| NoSuchPaddingException | InvalidKeyException
| InvalidAlgorithmParameterException
| IllegalBlockSizeException | BadPaddingException
| ShortBufferException | UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}
public static String bytesToHex(byte[] data, int length) {
String digits = "0123456789ABCDEF";
StringBuffer buffer = new StringBuffer();
for (int i = 0; i != length; i++) {
int v = data[i] & 0xff;
buffer.append(digits.charAt(v >> 4));
buffer.append(digits.charAt(v & 0xf));
}
return buffer.toString();
}
public static String bytesToHex(byte[] data) {
return bytesToHex(data, data.length);
}
public static byte[] hexToBytes(String string) {
int length = string.length();
byte[] data = new byte[length / 2];
for (int i = 0; i < length; i += 2) {
data[i / 2] = (byte) ((Character.digit(string.charAt(i), 16) << 4) + Character
.digit(string.charAt(i + 1), 16));
}
return data;
}
I have a feeling that I'm not generating the ECDH keys properly (possibly due to the named curve that I chose), but otherwise, I'm stumped. Can anyone see where I may be doing something wrong?
EDIT: Almost forgot the rest of the output:
Original plaintext message: Look mah, I'm a message!
Private key length: 1106
Public key length: 158
Private key length: 1107
Public key length: 158
Shared key length: 66
0147A5780737C5C0D7457C503D4036AC7BBED53D5536A32D6BE8713E6DB4FE0A549AF20514C223D630426292A8EDB512EBD50726A130FFA4AEE96A0EC2A6F9D4C3A0
Shared key length: 66
0147A5780737C5C0D7457C503D4036AC7BBED53D5536A32D6BE8713E6DB4FE0A549AF20514C223D630426292A8EDB512EBD50726A130FFA4AEE96A0EC2A6F9D4C3A0
I've investigated this and I think it's a bug in the BC provider, or maybe two.
You can get your example to work if you choose a KDF-based algorithm and use an OID to identify the AES algorithm for the generated key:
...
KeyAgreement keyAgreement = KeyAgreement.getInstance(X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme.getId(), "BC");
...
SecretKey key = keyAgreement.generateSecret(NISTObjectIdentifiers.id_aes128_CBC.getId());
...
Alternatively you could work around this by just truncating the original 66-byte SecretKey down to 16 (take the leading bytes, i.e. 0 thru 15), as this is what it should be doing but isn't.
We'll see what we can do about sorting this out in BC.
replace "BC" with new org.bouncycastle.jce.provider.BouncyCastleProvider() and remove NoSuchProviderException