我正在为下面的java代码寻找等价的C代码。我试图用下面的逻辑编写两个应用程序--一个用java编写,另一个用C.用Java应用程序加密/解密"string“,当使用下面的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代码中了解到:
用于加密的:
用于解密:
为了在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中有任何示例函数,这将是非常有用的。
发布于 2020-12-08 23:18:43
用于加密:
不,您使用的是PBKDF2和 HMAC-SHA256 256.这和普通的HMAC- the 256完全不一样。但是,如果您给出了正确的参数,那么所标识的OpenSSL函数确实与此匹配。这也适用于解密步骤1。
说大也大吧。该填充只对多达16个字符的输入数据正确工作,所有这些字符都是ASCII (因为您将其编码为UTF-8,并且任何非ASCII字符都会产生多个字节,使编码值成为非法长度)。大多数较长的价值观都会失败,尽管少数人会因运气不好而成功。甚至对于“成功”的价值观,也会有一些改变;这被认为是错误的做法,而且从本质上讲,自1980年以来,所有设计良好的密码方案都是为了保存所有数据。特别是非常常见的PKCS5 (有时因技术原因被称为PKCS7或PKCS7 5/PKCS7 7)标准填充保存了所有数据,并且已经在Java和OpenSSL以及几乎所有其他体面的密码库和设备中实现,这将是一个更好的选择,也是一个更简单的选择。
在固定填充的情况下,Java端可以执行非ASCII数据,但前提是您必须对要加密的明文进行编码,并在解密后对明文进行适当的解码。您有加密的.getBytes(StandardCharsets.UTF_8),但需要将其与解密时的new String(cipher.doFinal(...), StandardCharsets.UTF_8)匹配,否则它可能会工作,也可能无法工作,这取决于运行它所使用的平台和环境。
C面可能会更硬。OpenSSL是基于在1995年和1999年C版开始处理非英语字符之前开始的老式C代码,它只理解字节,这可以是单字节,也就是“窄”字符。要么您必须用调用代码包装它,该代码以多字节编码(如UTF-8 )处理“宽”字符(并使用字节调用OpenSSL部件),要么您必须在程序之外通过控制环境(例如终端或模拟器)或文件来完成这一任务。你的问题甚至没有给出任何一个提示,所以不可能提出任何建议。
因为您将“机密”(密码)、salt和IV视为String,所以同样的考虑也适用于它们,只不过它们可能来自与数据不同的来源。IV和salt被设计成字节序列,特别是将IV限制在ASCII或甚至UTF-8编码可能会降低安全性,但是由于编程而不是安全性的主题,我不会继续这样做。在实际的PBKDF2中,PKCS5密码也是八进制(Java字节),但它“建议”将文本(字符)编码为ASCII或UTF-8,而Java在PBEKeySpec中使用char[]并将代码编码为UTF-8,因此对于非ASCII,OpenSSL调用方或环境需要与之匹配。
鉴于这些限制:所有的值都是ASCII,数据不超过16 chars=bytes,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;
}(加)密码/秘密盐(笑话)和第四条
对于数据SOMEDATA,我得到了MOOn6FicaVcnVLokSANQsw==
对于数据BUTNOTMUCH,我得到5NsJUO4z1Bbap0U85ZClMg==
两者都与我从您的Java中获得的内容相匹配,并正确地解密。看看你得到了什么。
https://stackoverflow.com/questions/65195128
复制相似问题