c - C加密逻辑与java不匹配
问题描述
我正在寻找以下 java 代码的等效 C 代码。我正在尝试编写两个应用程序,一个在 java 中,另一个在 C 中。Java 应用程序使用以下逻辑加密/解密“字符串”,并且在使用以下 java 方法时它正在工作。
public class AEScrypt {
public static String encryptString(String strToEncrypt, String secret, String salt, byte[] iv) {
try {
IvParameterSpec ivspec = new IvParameterSpec(iv);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec keySpec = new PBEKeySpec(secret.toCharArray(), salt.getBytes(), 65536, 256);
SecretKey secretKeySpec = keyFactory.generateSecret(keySpec);
SecretKeySpec secretKey = new SecretKeySpec(secretKeySpec.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);
int length = 0;
if (strToEncrypt.length() <= 16) {
length = 16;
} else if (strToEncrypt.length() > 16 && strToEncrypt.length() <= 32) {
length = 16;
}
strToEncrypt = fixedLengthString(strToEncrypt, length);
return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes(StandardCharsets.UTF_8)));
} catch (Exception exception) {
System.out.println("Error while encrypting value : "+exception.getMessage());
}
return null;
}
private static String fixedLengthString(String string, int length) {
return String.format("%" + length + "s", string);
}
public static String decryptString(String strToDecrypt, String secret, String salt, byte[] iv) {
try {
IvParameterSpec ivspec = new IvParameterSpec(iv);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec keySpec = new PBEKeySpec(secret.toCharArray(), salt.getBytes(), 65536, 256);
SecretKey secretKeySpec = keyFactory.generateSecret(keySpec);
SecretKeySpec secretKey = new SecretKeySpec(secretKeySpec.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivspec);
return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt))).trim();
} catch (Exception e) {
e.getMessage();
}
return null;
}
}
我从上面的 JAVA 代码中了解到的是:
对于加密:
- 我们使用 HMAC-sha256 来生成“key”,它需要“salt”、“password”。
- 填充输入数据。
- 我们使用 AES-CBC-256 加密填充的输入数据,使用上面生成的“key”和“iv”。
- 我们使用 base64 对加密数据进行编码。
解密:
- 我们使用 HMAC-sha256 来生成“key”,它需要“salt”、“password”。
- 我们使用 base64 解码并获取加密数据。
- 我们使用 AES-CBC-256 来解密加密数据,使用上面生成的密钥和 iv。
- 修剪解密的数据。
为了在 C 中复制相同的内容,我使用了下面链接中的加密/解密方法; EVP对称加解密
为了生成密钥,我使用“PKCS5_PBKDF2_HMAC”和 EVP_MD 作为“EVP_sha256()”。
int PKCS5_PBKDF2_HMAC(const char *pass, int passlen,
const unsigned char *salt, int saltlen, int iter,
const EVP_MD *digest,
int keylen, unsigned char *out);
对于 base64 编码/解码: base64 编码/解码
我还处理了填充和修剪逻辑。但是我从 java 和 c 代码中得到了不同的加密数据。我在这里错过了什么吗?
如果您在 C 中有任何示例函数,那将非常有帮助。
解决方案
对于加密:
- 我们使用 HMAC-sha256 来生成“key”,它需要“salt”、“password”。
不,您正在使用带有HMAC-SHA256的PBKDF2 。这与普通的 HMAC-SHA256 完全不同。但是,假设您为其提供正确的参数,您确定的 OpenSSL 函数与此匹配。这也适用于解密步骤 1。
- 填充输入数据。
有点。该填充仅适用于最多 16 个字符的输入数据,所有这些字符都是 ASCII(因为您将其编码为 UTF-8,并且任何非 ASCII 字符都会产生一个以上的字节,从而使编码值成为非法长度) . 大多数较长的值会失败,尽管少数会因运气不好而成功。甚至对于“成功”的价值观,也会有一些改变;这被认为是不好的做法,基本上自 1980 年以来所有设计良好的加密方案都旨在保护所有数据。特别是非常常见的 PKCS5(由于技术原因,有时称为 PKCS7 或 PKCS5/PKCS7)标准填充正确地保留了所有数据,并且已经在 Java 和 OpenSSL 以及几乎所有其他体面的加密库和设备中实现,并且将是更好的选择,也更简单。
固定填充后,Java 端可以处理非 ASCII 数据,但前提是您既要对要加密的明文进行编码,又要在解密后对明文进行适当的解码。您有.getBytes(StandardCharsets.UTF_8)
on encrypt,但需要将其与new String(cipher.doFinal(...), StandardCharsets.UTF_8)
on decrypt 匹配,否则它可能会或可能不会工作,具体取决于您用于运行它的平台和环境。
C面可能更难。OpenSSL 基于在 1995 年和 1999 年版本的 C 开始处理非英文字符之前开始的老式 C 代码,它只理解字节,它可以是单字节又名“窄”字符。您必须使用调用代码来包装它,该代码以多字节编码(例如 UTF-8)处理“宽”字符(并使用字节调用 OpenSSL 部分),或者您必须在程序外部通过控制环境(例如作为终端或模拟器)或文件。您的问题甚至没有提供任何提示,因此不可能提出任何建议。
因为您将“秘密”(密码)、盐和 IV 视为String
s,所以同样的考虑适用于它们,除了它们可能来自与数据不同的来源。IV 和 salt 被设计为字节序列,将 IV 特别限制为 ASCII 甚至 UTF-8 编码可能会降低一些安全性,但由于 SO 的主题是编程而不是安全性,我不会追求这一点。在 PKCS5 中的实际 PBKDF2 密码也是八位字节(Java 字节),但它“建议”将文本(字符)编码为 ASCII 或 UTF-8,而 Java 确实接受char[]
并PBEKeySpec
编码为 UTF-8,因此对于非 ASCII OpenSSL 调用者或环境需要与之匹配。
鉴于这些限制:所有值都只是 ASCII,数据不超过 16 个字符=字节,IV 正好是 16,以下 C 代码匹配并且可以与您的 Java 互操作。错误处理很少,我在一个函数中同时进行加密和解密;您希望能够将它们分开。(更正)
/* SO65195128.c 20dec09,11 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
void err (int n, const char * s){ printf("%s:%d\n", s, n); ERR_print_errors_fp(stdout); exit(1); }
int main (int argc, char **argv){
if( argc != 5 || strlen(argv[3]) != 16 || strlen(argv[4]) > 16 ){ printf("bad args\n"); exit(1); }
const char * pw = argv[1], * salt = argv[2], * iv = argv[3], * org = argv[4];
unsigned char key [32], pad [16], enc [16], b64 [25], unb [16], dec [16];
int rc, len, temp, i, j;
SSL_library_init();
/* for both */
rc = PKCS5_PBKDF2_HMAC (pw, strlen(pw), (unsigned char*)salt, strlen(salt), 65536, EVP_sha256(), 32, key);
if( rc != 1 ) err(rc,"PBKDF2");
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
/* for encrypt */
len = strlen(org); memset(pad, ' ', 16-len); memcpy (pad+16-len, org, len);
rc = EVP_EncryptInit (ctx, EVP_aes_256_cbc(), key, (unsigned char*)iv);
if( rc != 1 ) err(rc,"EncryptInit");
rc = EVP_CIPHER_CTX_set_padding(ctx,0);
if( rc != 1 ) err(rc,"set_padding");
rc = EVP_EncryptUpdate (ctx, enc, &len, pad, 16);
if( rc != 1 || len != 16 ) err(rc,"EncryptUpdate");
rc = EVP_EncryptFinal (ctx, enc+len, &temp);
if( rc != 1 || temp != 0 ) err(rc,"EncryptFinal");
rc = EVP_EncodeBlock(b64, enc, 16);
if( rc <= 0 ) err(rc,"EncodeBlock");
printf ("%.*s\n", rc, b64);
/* for decrypt */
rc = EVP_DecodeBlock(unb, b64, /*len*/rc)-(b64[rc-1]=='=')-(b64[rc-2]=='=');
/* this is a hack, should go for DecodeInit,Update,Final */
if( rc != 16 ) err(rc,"DecodeBlock");
rc = EVP_DecryptInit (ctx, EVP_aes_256_cbc(), key, (unsigned char*)iv);
if( rc != 1 ) err(rc,"DecryptInit");
rc = EVP_CIPHER_CTX_set_padding(ctx,0);
if( rc != 1 ) err(rc,"set_padding");
rc = EVP_DecryptUpdate (ctx, dec, &len, unb, 16);
if( rc != 1 || len != 16 ) err(rc,"DecryptUpdate");
rc = EVP_DecryptFinal (ctx, dec+len, &temp);
if( rc != 1 || temp != 0 ) err(rc,"DecryptFinal");
i=0; while(i<16&&dec[i]<=' ') i++; j=16; while(j>0&&dec[j-1]<=' ') j--;
printf ("%.*s\n", j-i, dec+i);
/* note this is NOT a C string -- needs to be copied and NUL added for that */
return 0;
}
(添加)密码/秘密 SEKRIT salt NOSAILOR(一个笑话)和 IV STARTINGSTARTING
数据 SOMEDATA 我得到MOOn6FicaVcnVLokSANQsw==
的数据但我得到5NsJUO4z1Bbap0U85ZClMg==
的两者都与我从你的 Java 得到的相匹配,并正确解密。看看你得到了什么。
推荐阅读
- sql - 计算给定日期处于特定状态的项目数
- java - 如果靠近数组中的某个值,则减少数组中数字的值
- javascript - Selenium JavaScript 单击下拉菜单
- python - 如何在字典键上交替小写和大写?蟒蛇 3
- r - 我需要从一个函数中迭代一个 R 平方
- java - 如何在java中使用google drive api分块下载文件?
- post - 如何在继续使用 HttpWebRequest 发布数据的同时检查 HTTP 响应
- java - Criteria Api Vs QueryDsl Vs JPA 元模型
- postgresql - 选择没有价值的记录
- jquery - 复选框字段“重置”功能,更改时取消选中,直到再次选中