使用BouncyCastle(java)和Gcrypt(C)进行加密会得到不同的结果
我编写了这个简单的Java程序,它加密字符串并输出iv、salt、派生密钥和密文的十六进制值使用BouncyCastle(java)和Gcrypt(C)进行加密会得到不同的结果,java,c,aes,bouncycastle,libgcrypt,Java,C,Aes,Bouncycastle,Libgcrypt,我编写了这个简单的Java程序,它加密字符串并输出iv、salt、派生密钥和密文的十六进制值 public class tmp{ static Cipher encryptionCipher; static String RANDOM_ALGORITHM = "SHA1PRNG"; static String PBE_ALGORITHM = "PBEWithSHA256And256BitAES-CBC-BC"; static String CIPHER_ALGOR
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);
}
}
在下面的C程序中,iv和salt使用上述Java程序给出的值进行初始化。无需填充,因为文本长度为32字节
#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;
}
#包括
#包括
#包括
#包括
int
主(空)
{
int i;
char*encami=“这是一个测试法尔赛。谢谢!!”;
char*pwd=“A0C299B71A9E59D5EBB07917E70601A3570AA103E99A7BB65A58E780EC9077B1902DDB31B1457BEDA595FE4D71D779B6CA9CAD476266CC07590E31D84B206”;
无符号字符iv[]={};
无符号字符盐[]={};
int algo=gcry\u cipher\u map\u name(“aes256”);
无符号字符*devkey=NULL;
无符号字符*enc_buf=NULL;
enc_buf=gcry_malloc(32);
devkey=gcry\u malloc\u secure(32);
gcry\u cipher\u hd\u t hd;
gcry_密码打开(&hd、algo、gcry_密码模式CBC,0);
gcry_kdf_派生(pwd,strlen(pwd)+1,gcry_kdf_PBKDF2,gcry_MD_SHA256,salt,322048,32devkey);
对于(i=0;i您似乎在32字节计数(encami)中包含一个终止空字符,这解释了不同的输出。java版本看到一个31字符的输入,并提供一个单独的PKCS#7填充输出块(PKCS#7将用一个“1”字节填充输入).C版本传递了32个字节,包括最后的“0”字节。因此输入不同
我建议您停止将空终止符视为输入的一部分;而是像Java版本那样应用PKCS#7填充。我不熟悉gcrypt,因此我不知道执行此操作的典型方法是什么,但PKCS#7填充在任何情况下都是一个非常简单的概念。我现在已经研究了PBewithsha256和256biates CBC BC alg算法,并发现它与GCRY#u KDF#u PBKDF2不兼容。gcrypt算法是PKCS#5 2.0方案2,而BC算法实际上是实现PKCS#12
实际上,到目前为止,我还没有在提供程序中找到与gcrypt算法匹配的命名算法,但是我能够直接使用bcapi来获得匹配结果,如下所示
弹跳城堡:
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");
unsigned char salt[8];
memset(salt,1,8);
无符号字符键[32];
gcry_kdf_派生(“密码”,8,gcry_kdf_PBKDF2,gcry_MD_SHA256,salt,82048,32,密钥);
对于(int i=0;i<32;++i)
printf(“%02x”,键[i]);
printf(“\n”);
这两个输出:
4182537a153b1f0da1ccb57971787a42537e38dbf2b4aa3692baebb106fc02e8
您好,谢谢您的回答。但是,这只能解释为什么我会得到不同的加密输出,但无法解释为什么我会得到不同的派生密钥(我尝试在不使用NULL的情况下给出密钥,但它显然发生了变化,但与Java的不一样)。您还在“gcry_KDF_deriver”调用的KDF输入中包含“pwd”的NULL,java版本没有这样做。只需在那里使用“strlen(pwd)”?我已经尝试只给出strlen(pwd)
但没有任何变化。在这一点上,我认为Java使用的密钥派生方法与Gcrypt使用的密钥派生方法不同。尽管这很奇怪,因为它是PBKDF2-PKCS#5的标准。。。