public static void main(String[] args) throws SignatureException {
String data = "GET"+"\n"+"webservices.amazon.com"+"\n"+"/onca/xml"+"\n"+"AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&ItemId=0679722769&Operation=ItemLookup&ResponeGroup=ItemAttributes%2COffers%2CImages%2CReviews&Service=AWSECommerceService&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-06";
String key = "1234567890";
String result = calculateRFC2104HMAC(data, key);
System.out.println(result);
}
private static final String HMAC_SHA_ALGORITHM = "HmacSHA256";
public static String calculateRFC2104HMAC(String data, String key)throws java.security.SignatureException{
String result;
try {
// get an hmac_sha256 key from the raw key bytes
SecretKeySpec signingKey = new SecretKeySpec(key.getBytes("UTF-8"), HMAC_SHA_ALGORITHM);
// get an hmac_sha256 Mac instance and initialize with the signing key
Mac mac = Mac.getInstance(HMAC_SHA_ALGORITHM);
mac.init(signingKey);
// compute the hmac256 on input data bytes
byte[] rawHmac = mac.doFinal(data.getBytes("UTF-8"));
// base64-encode the hmac256
result = Base64.encodeBase64String(rawHmac);
} catch (Exception e) {
throw new SignatureException("Failed to generate HMAC : " + e.getMessage());
}
return result;
}
So I am trying to calculate this hmac with sha256 for AWS, but I do not get the excpected result, even though this example is taken from official AWS docs: http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/AuthJavaSampleHMACSignature.html And the only thing changed is the algorithm, which did not break the program, so it should work, but it does not.
The result I get: k1T/qvVoXgEvmdFhTEh71vLDznqEVCyKcslA5RRSB6s=
The result I expect: M/y0+EAFFGaUAp4bWv/WEuXYah99pVsxvqtAuC8YN7I=
Does anyone have any idea what is wrong?
It may have to do with how the newline character is interpreted. \n can be a cr, lf, or cr-lf depending on your OS.
AWS uses to two different HMAC functions, the first returns the string representation, the other returns the binary representation. This is from my C++ implementation using OpenSSL, hope it helps:
string hmacHex(string key, string msg)
{
unsigned char hash[32];
HMAC_CTX hmac;
HMAC_CTX_init(&hmac);
HMAC_Init_ex(&hmac, &key[0], key.length(), EVP_sha256(), NULL);
HMAC_Update(&hmac, (unsigned char*)&msg[0], msg.length());
unsigned int len = 32;
HMAC_Final(&hmac, hash, &len);
HMAC_CTX_cleanup(&hmac);
std::stringstream ss;
ss << std::hex << std::setfill('0');
for (int i = 0; i < len; i++)
{
ss << std::hex << std::setw(2) << (unsigned int)hash[i];
}
return (ss.str());
}
the string implementation
string hmac(string key, string msg)
{
unsigned char hash[32];
HMAC_CTX hmac;
HMAC_CTX_init(&hmac);
HMAC_Init_ex(&hmac, &key[0], key.length(), EVP_sha256(), NULL);
HMAC_Update(&hmac, ( unsigned char* )&msg[0], msg.length());
unsigned int len = 32;
HMAC_Final(&hmac, hash, &len);
HMAC_CTX_cleanup(&hmac);
std::stringstream ss;
ss << std::setfill('0');
for (int i = 0; i < len; i++)
{
ss << hash[i];
}
return (ss.str());
}
If you are using Java, I'd recommend using the corresponding SDK. I my experience the API's tend to change rather quickly.
Related
I am trying to encrypt and decrypt a string of data using 3DES and it is working fine. However I want that the size of the data after encryption to be limited to length of 16 bits.
This is the code I am referring from https://gist.github.com/riversun/6e15306cd6e3b1b37687a0e5cec1cef1 :
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class DesedeCrypter {
private static final String CRYPT_ALGORITHM = "DESede";
private static final String PADDING = "DESede/CBC/NoPadding";
private static final String CHAR_ENCODING = "UTF-8";
private static final byte[] MY_KEY = "5oquil2oo2vb63e8ionujny6".getBytes();//24-byte
private static final byte[] MY_IV = "3oco1v52".getBytes();//8-byte
public static void main(String[] args) {
String srcText = "M3A1B2C3D4HHG393";
final DesedeCrypter crypter = new DesedeCrypter();
String encryptedText = crypter.encrypt(srcText);
System.out.println("sourceText=" + srcText + " -> encryptedText=" + encryptedText + "\n");
System.out.println("encrypted-text=" + encryptedText + " -> decrypted-text(source text)="
+ crypter.decrypt(encryptedText));
}
public String encrypt(String text) {
String retVal = null;
try {
final SecretKeySpec secretKeySpec = new SecretKeySpec(MY_KEY, CRYPT_ALGORITHM);
final IvParameterSpec iv = new IvParameterSpec(MY_IV);
final Cipher cipher = Cipher.getInstance(PADDING);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, iv);
final byte[] encrypted = cipher.doFinal(text.getBytes(CHAR_ENCODING));
retVal = new String(encodeHex(encrypted));
} catch (Exception e) {
e.printStackTrace();
}
return retVal;
}
public String decrypt(String text) {
String retVal = null;
try {
final SecretKeySpec secretKeySpec = new SecretKeySpec(MY_KEY, CRYPT_ALGORITHM);
final IvParameterSpec iv = new IvParameterSpec(MY_IV);
final Cipher cipher = Cipher.getInstance(PADDING);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, iv);
final byte[] decrypted = cipher.doFinal(decodeHex(text.toCharArray()));
retVal = new String(decrypted, CHAR_ENCODING);
} catch (Exception e) {
e.printStackTrace();
}
return retVal;
}
private byte[] decodeHex(char[] data) throws Exception {
int len = data.length;
if ((len & 0x01) != 0) {
throw new Exception("Odd number of characters.");
}
byte[] out = new byte[len >> 1];
// two characters form the hex value.
for (int i = 0, j = 0; j < len; i++) {
int f = toDigit(data[j], j) << 4;
j++;
f = f | toDigit(data[j], j);
j++;
out[i] = (byte) (f & 0xFF);
}
return out;
}
private int toDigit(char ch, int index) throws Exception {
int digit = Character.digit(ch, 16);
if (digit == -1) {
throw new Exception("Illegal hexadecimal character " + ch + " at index " + index);
}
return digit;
}
private char[] encodeHex(byte[] data) {
final char[] DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
int l = data.length;
char[] out = new char[l << 1];
// two characters form the hex value.
for (int i = 0, j = 0; i < l; i++) {
out[j++] = DIGITS[(0xF0 & data[i]) >>> 4];
out[j++] = DIGITS[0x0F & data[i]];
}
return out;
}
}
Currently this is the output I am getting:
sourceText=M3A1B2C3D4HHG393 -> encryptedText=afc1d48ea5cc703253cbc1a88a198103
encrypted-text=afc1d48ea5cc703253cbc1a88a198103 -> decrypted-text(source text)=M3A1B2C3D4HHG393
Is there any way that the size of the encryptedText be limited to 16 as I want to add the encrypted text back into a message which has 16 digits space for encrypted text.
Please suggest some way or any other change that is required to achieve this. Thanks !
For one, I highly recommend not supporting (3)DES anymore, as it's officially unsecure in favour of AES/ChaCha, which I must say before answering this question.
3DES has a block size of 64 bits (or 8 bytes). With that also comes that encryption ≠ compression. So if you want to encrypt 16 bytes, provide 16 bytes of input, unless:
You apply a padding scheme (which it appears you're not doing)(taken from the DESede/CBC/NoPadding
You apply a (random) initialisation vector (which it appears you're doing)
The latter one should, but I'm not to sure of Android's implementation, create a 64 bits (8 byte) iv, as the iv should be as big as the block size of the cipher (again, 64 bits for 3DES).
So if you want 16 bytes of output, you can, and should, only provide 8 bytes to encrypt. Now, if 8 bytes is not enough, you might choose to drop the iv from being in the ciphertext, which you could do if you use a fixed iv (such as an all zero iv) with random keys, as reusing the same key with iv is not secure.
Now if you do take security in consideration, keep in mind that AES has a block size of 16 bytes. AES and ChaCha come with the same constraints regarding the iv.
Then you might also might want to consider changing the message (protocol) instead, so it can take more than 16 bytes of data or use those 16 bytes in such a way that it indicates that there is more data to handle, as like an attachment to an e-mail.
I wish to convert following python code into Java. This code encodes credit card data using hash algorithms and keys. I have written some Java code based on my understanding below that. I think mainly its about panmackey. I am not sure how to generate its value for Java.
Python Code:
panmackey = bytes.fromhex('449E5A196233A43819A028770880E814DC420BFFC428295787302E6285FDD685')
def pandgstsha(track2, pan_mac_key=panmackey):
h = hmac.HMAC(pan_mac_key, hashes.SHA256(), backend=default_backend())
h. (bytes(track2.split('=')[0]+track2.split('=')[1][:4],'UTF-8'))
return base64.b64encode(h.finalize())
Java code:
String tokenPan = ccNum + expiryStr;
String panmackey= "?????????????????????";//pan_mac_key from python code
Mac mac = Mac.getInstance("HmacSHA512");
byte[] decodedBytes = Base64.decodeBase64(panmackey.getBytes("UTF-16LE"));
SecretKeySpec sk = new SecretKeySpec(decodedBytes, mac.getAlgorithm());
mac.init(sk);
byte[] resultBase64 = Base64.encodeBase64(mac.doFinal(tokenPan.getBytes("ASCII")));
String sB64 = new String(resultBase64, "UTF-8");
I need to figure out panmackey and if the 2 codes do exactly same function.
Your best bet would be to use:
https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/binary/Hex.html
import org.apache.commons.codec.binary.Hex;
...
byte[] panmackey= Hex.decodeHex("Hex string goes here");
Otherwise, you'll have to implement your own conversion function like this;
public static byte[] hexStringToByteArray(String hex) {
byte[] decoded = new byte[hex.length() / 2];
for (int i = 0; i < b.length; i++) {
int index = i * 2;
int value = Integer.parseInt(hex.substring(index, index + 2), 16);
decoded[i] = (byte) value;
}
return decoded;
}
Sha256 hash function gives a longer hashed string in objective c than Java. Extra Zeros being added in objective C, how can I rationalise the hashing?
Objective C:
-(NSString*) sha256:(NSString *)clear{
const char *s=[clear cStringUsingEncoding:NSASCIIStringEncoding];
NSData *keyData=[NSData dataWithBytes:s length:strlen(s)];
uint8_t digest[CC_SHA256_DIGEST_LENGTH]={0};
CC_SHA256(keyData.bytes, keyData.length, digest);
NSData *out=[NSData dataWithBytes:digest
length:CC_SHA256_DIGEST_LENGTH];
NSString *hash=[out description];
hash = [hash stringByReplacingOccurrencesOfString:#" " withString:#""];
hash = [hash stringByReplacingOccurrencesOfString:#"<" withString:#""];
hash = [hash stringByReplacingOccurrencesOfString:#">" withString:#""];
return hash;
}
Java
public static String generateHashString(String data)
{
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] dataInBytes = data.getBytes(StandardCharsets.UTF_8);
md.update(dataInBytes);
byte[] mdbytes = md.digest();
StringBuffer hexString = new StringBuffer();
for (int i=0;i<mdbytes.length;i++) {
hexString.append(Integer.toHexString(0xFF & mdbytes[i]));
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
Integer.toHexString() on an integer less than 16 will only be one character long, whereas you want the extra '0' character.
You could use String.format():
for (int i = 0; i < mdbytes.length; i++) {
hexString.append(String.format("%02x", 0xFF & mdbytes[i]));
}
Also, you really should be using StringBuilder rather than StringBuffer in this case because only a single thread is involved.
See Java code To convert byte to Hexadecimal for some alternative solutions to hex-encoding a byte array in Java.
I've been investigating a bit about Java String encryption techniques and unfortunately I haven't find any good tutorial how to hash String with SHA-512 in Java; I read a few blogs about MD5 and Base64, but they are not as secure as I'd like to (actually, Base64 is not an encryption technique), so I prefer SHA-512.
you can use this for SHA-512 (Not a good choice for password hashing).
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public String get_SHA_512_SecurePassword(String passwordToHash, String salt){
String generatedPassword = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(salt.getBytes(StandardCharsets.UTF_8));
byte[] bytes = md.digest(passwordToHash.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for(int i=0; i< bytes.length ;i++){
sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
}
generatedPassword = sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return generatedPassword;
}
Please stop using hash functions to encode passwords! They do not provide the protection you need. Instead, you should be using an algorithm like PBKDF2, bcrypt, or scrypt.
References:
http://blog.tjll.net/please-stop-hashing-passwords/
http://security.blogoverflow.com/2011/11/why-passwords-should-be-hashed/
https://crackstation.net/hashing-security.htm
http://www.sitepoint.com/risks-challenges-password-hashing/
http://security.blogoverflow.com/2013/09/about-secure-password-hashing/
Using Guava:
Hashing.sha512().hashString(s, StandardCharsets.UTF_8).toString()
Use Apache Commons Crypt, it features SHA-512 based crypt() functions that generate salted hashes that are even compatible to libc's crypt and thus usable in PHP/Perl/Python/C and most databases, too.
https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/digest/Crypt.html#Crypt%28%29
you could use this to hash a password in java if you want to.
public static boolean isHashMatch(String password, // the password you want to check.
String saltedHash, // the salted hash you want to check your password against.
String hashAlgorithm, // the algorithm you want to use.
String delimiter) throws NoSuchAlgorithmException // the delimiter that has been used to delimit the salt and the hash.
{
// get the salt from the salted hash and decode it into a byte[].
byte[] salt = Base64.getDecoder()
.decode(saltedHash.split(delimiter)[0]);
// compute a new salted hash based on the provided password and salt.
String pw_saltedHash = computeSaltedBase64Hash(password,
salt,
hashAlgorithm,
delimiter);
// check if the provided salted hash matches the salted hash we computed from the password and salt.
return saltedHash.equals(pw_saltedHash);
}
public static String computeSaltedBase64Hash(String password, // the password you want to hash
String hashAlgorithm, // the algorithm you want to use.
String delimiter) throws NoSuchAlgorithmException // the delimiter that will be used to delimit the salt and the hash.
{
// compute the salted hash with a random salt.
return computeSaltedBase64Hash(password, null, hashAlgorithm, delimiter);
}
public static String computeSaltedBase64Hash(String password, // the password you want to hash
byte[] salt, // the salt you want to use (uses random salt if null).
String hashAlgorithm, // the algorithm you want to use.
String delimiter) throws NoSuchAlgorithmException // the delimiter that will be used to delimit the salt and the hash.
{
// transform the password string into a byte[]. we have to do this to work with it later.
byte[] passwordBytes = password.getBytes();
byte[] saltBytes;
if(salt != null)
{
saltBytes = salt;
}
else
{
// if null has been provided as salt parameter create a new random salt.
saltBytes = new byte[64];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(saltBytes);
}
// MessageDigest converts our password and salt into a hash.
MessageDigest messageDigest = MessageDigest.getInstance(hashAlgorithm);
// concatenate the salt byte[] and the password byte[].
byte[] saltAndPassword = concatArrays(saltBytes, passwordBytes);
// create the hash from our concatenated byte[].
byte[] saltedHash = messageDigest.digest(saltAndPassword);
// get java's base64 encoder for encoding.
Encoder base64Encoder = Base64.getEncoder();
// create a StringBuilder to build the result.
StringBuilder result = new StringBuilder();
result.append(base64Encoder.encodeToString(saltBytes)) // base64-encode the salt and append it.
.append(delimiter) // append the delimiter (watch out! don't use regex expressions as delimiter if you plan to use String.split() to isolate the salt!)
.append(base64Encoder.encodeToString(saltedHash)); // base64-encode the salted hash and append it.
// return a salt and salted hash combo.
return result.toString();
}
public static byte[] concatArrays(byte[]... arrays)
{
int concatLength = 0;
// get the actual length of all arrays and add it so we know how long our concatenated array has to be.
for(int i = 0; i< arrays.length; i++)
{
concatLength = concatLength + arrays[i].length;
}
// prepare our concatenated array which we're going to return later.
byte[] concatArray = new byte[concatLength];
// this index tells us where we write into our array.
int index = 0;
// concatenate the arrays.
for(int i = 0; i < arrays.length; i++)
{
for(int j = 0; j < arrays[i].length; j++)
{
concatArray[index] = arrays[i][j];
index++;
}
}
// return the concatenated arrays.
return concatArray;
}
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Hex;
public String getHashSHA512(String StringToHash, String salt){
String generatedPassword = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(salt.getBytes(StandardCharsets.UTF_8));
byte[] bytes = md.digest(StringToHash.getBytes(StandardCharsets.UTF_8));
generatedPassword = Hex.encodeHexString(bytes);
}
catch (NoSuchAlgorithmException e){
e.printStackTrace();
}
return generatedPassword;
}
It's not recommended to use hash functions for passwords though, newer alogrithms like bcrypt, or scrypt exist
With secure hashing combine 3 salt components (of 150 random characters each) to a individual user salt (user salt from the user database table, general salt in a database table (monthly change with cron job) and hide some salt in the application library). Align the for loop amount of the secure hash to your needs. See answer above for hashing method.
private static String generateSalt(int lenght){
String abcCapitals = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
String abcLowerCase = "abcdefghijklmnopqrstuvwxyz";
String numbers = "01234567890123456789";
String characters = "!##$%^&*!##$%%^^&*";
String total = abcCapitals + abcLowerCase + numbers + characters;
String response = "";
char letters[] = new char[lenght];
for (int i=0; i<lenght-1; i++){
Random r = new Random();
char letter = total.charAt(r.nextInt(total.length()));
letters[i] = letter;
}
response = Arrays.toString(letters).replaceAll("\\s+","");
response = response.replaceAll(",","");
return response;
}
private static String getHash(String passwordToHash, String salt){
String generatedPassword = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(salt.getBytes(StandardCharsets.UTF_8));
byte[] bytes = md.digest(passwordToHash.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for(int i=0; i< bytes.length ;i++){
sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
}
generatedPassword = sb.toString();
}
catch (NoSuchAlgorithmException e){
System.out.println(e);
}
return generatedPassword;
}
public static String getSecureHash(String password, String salt){
String hash = getHash(password, salt);
for (int i=0; i<20000; i++){
hash = getHash(password, hash);
}
return hash;
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
String salt = generateSalt(150);
String salt2 = generateSalt(150);
String salt3 = generateSalt(150);
String someString = "This is some string!";
String hash = getSecureHash(someString, salt + salt2 + salt3);
System.out.println(hash);
}
I wrote this simple Java program which encrypt a string and output the hex value of the iv, salt, derived key and cipher text.
public class tmp{
static Cipher encryptionCipher;
static String RANDOM_ALGORITHM = "SHA1PRNG";
static String PBE_ALGORITHM = "PBEWithSHA256And256BitAES-CBC-BC";
static String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";
static String SECRET_KEY_ALGORITHM = "AES";
static int PBE_ITERATION_COUNT = 2048;
static String PROVIDER = "BC";
public static byte[] generateIv() {
try{
SecureRandom random;
random = SecureRandom.getInstance(RANDOM_ALGORITHM);
byte[] iv = new byte[16];
random.nextBytes(iv);
return iv;
} catch(Exception e){
return null; // Always must return something
}
}
public static byte[] generateSalt() {
try {SecureRandom random;
random = SecureRandom.getInstance(RANDOM_ALGORITHM);
byte[] salt = new byte[32];
random.nextBytes(salt);
return salt;
} catch(Exception e){
return null; // Always must return something
}
}
public static SecretKey getSecretKey(String password, byte[] salt){
try {
PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, PBE_ITERATION_COUNT, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance(PBE_ALGORITHM, PROVIDER);
SecretKey tmp = factory.generateSecret(pbeKeySpec);
return new SecretKeySpec(tmp.getEncoded(), SECRET_KEY_ALGORITHM);
} catch(Exception e){
System.out.println(e); // Always must return something
return null;
}
}
public static String encrypt(String plaintext, Key key, byte[] iv) {
try {
AlgorithmParameterSpec ivParamSpec = new IvParameterSpec(iv);
encryptionCipher = Cipher.getInstance(CIPHER_ALGORITHM, PROVIDER);
encryptionCipher.init(Cipher.ENCRYPT_MODE, key, ivParamSpec);
byte[] ciphertext = encryptionCipher.doFinal(plaintext.getBytes("UTF-8"));
String cipherHexString = DatatypeConverter.printHexBinary(ciphertext);
return cipherHexString;
}
catch (Exception e) {
System.out.println(e);
return null;
}
}
public static void main (String[] Args){
SecretKey key;
//sha512(ciao)
String encami = "This is a test pharse. Thanks!!";
String password = "a0c299b71a9e59d5ebb07917e70601a3570aa103e99a7bb65a58e780ec9077b1902d1dedb31b1457beda595fe4d71d779b6ca9cad476266cc07590e31d84b206";
byte[] iv = new byte[16];
byte[] salt = new byte[32];
iv = generateIv();
salt = generateSalt();
String ll = DatatypeConverter.printHexBinary(iv);
String lp = DatatypeConverter.printHexBinary(salt);
System.out.println(ll);
System.out.println(lp);
key = getSecretKey(password, salt);
byte tt[] = new byte[32];
tt = key.getEncoded();
String lo = DatatypeConverter.printHexBinary(tt);
System.out.println(lo);
String outenc = encrypt(encami, key, iv);
System.out.println(outenc);
}
}
In the following C program iv and salt are initialized with the values given by the above Java program. No padding needed since the length of the text is 32 bytes.
#include <stdio.h>
#include <gcrypt.h>
#include <stdlib.h>
#include <string.h>
int
main (void)
{
int i;
char *encami = "This is a test pharse. Thanks!!";
char *pwd = "a0c299b71a9e59d5ebb07917e70601a3570aa103e99a7bb65a58e780ec9077b1902d1dedb31b1457beda595fe4d71d779b6ca9cad476266cc07590e31d84b206";
unsigned char iv[] = {};
unsigned char salt[] = {};
int algo = gcry_cipher_map_name("aes256");
unsigned char *devkey = NULL;
unsigned char *enc_buf = NULL;
enc_buf = gcry_malloc(32);
devkey = gcry_malloc_secure (32);
gcry_cipher_hd_t hd;
gcry_cipher_open(&hd, algo, GCRY_CIPHER_MODE_CBC, 0);
gcry_kdf_derive (pwd, strlen(pwd)+1, GCRY_KDF_PBKDF2, GCRY_MD_SHA256, salt, 32, 2048, 32, devkey);
for (i=0; i<32; i++)
printf ("%02x", devkey[i]);
printf("\n");
gcry_cipher_setkey(hd, devkey, 32);
gcry_cipher_setiv(hd, iv, 16);
gcry_cipher_encrypt(hd, enc_buf, strlen(encami)+1, encami, strlen(encami)+1);
for (i=0; i<32; i++)
printf("%02x", enc_buf[i]);
printf("\n");
gcry_cipher_close(hd);
gcry_free(enc_buf);
gcry_free (devkey);
return 0;
}
My problem is that the derived key is not the same in those two programs. Why?
Is the bouncy castle deriving function not working in the same way as gcry_kdf_derive?
Thanks!
I've now looked into the PBEWithSHA256And256BitAES-CBC-BC algorithm in the BC provider, and found that it is not compatible with GCRY_KDF_PBKDF2. The gcrypt algorithm is PKCS#5 2.0 Scheme 2, whereas the BC one is actually implementing PKCS#12.
Actually, I've so far not found a named algorithm in the provider that matches the gcrypt one, however I was able to use the BC API directly to get matching results b/w them, as follows.
Bouncy Castle:
byte[] salt = new byte[8];
Arrays.fill(salt, (byte)1);
PBEParametersGenerator pGen = new PKCS5S2ParametersGenerator(new SHA256Digest());
pGen.init(Strings.toByteArray("password"), salt, 2048);
KeyParameter key = (KeyParameter)pGen.generateDerivedParameters(256);
System.out.println(Hex.toHexString(key.getKey()));
gcrypt:
unsigned char salt[8];
memset(salt, 1, 8);
unsigned char key[32];
gcry_kdf_derive("password", 8, GCRY_KDF_PBKDF2, GCRY_MD_SHA256, salt, 8, 2048, 32, key);
for (int i = 0; i < 32; ++i)
printf("%02x", key[i]);
printf("\n");
which both output:
4182537a153b1f0da1ccb57971787a42537e38dbf2b4aa3692baebb106fc02e8
You appear to be including a terminating NULL character in your count of 32 bytes (encami), which explains the differing outputs. The java version sees a 31-character input and provides a single PKCS#7-padded output block (PKCS#7 will pad the input with a single '1' byte). The C version is passed 32 bytes, including the final '0' byte. So the inputs are different.
I recommend you stop treating the NULL terminator as part of the input; instead apply PKCS#7 padding, as the Java version is doing. I'm not familiar with gcrypt, so I don't know what the typical method is for doing this, but PKCS#7 padding is a quite simple concept in any case.