首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C加密逻辑与java不匹配

C加密逻辑与java不匹配
EN

Stack Overflow用户
提问于 2020-12-08 07:58:37
回答 1查看 201关注 0票数 0

我正在为下面的java代码寻找等价的C代码。我试图用下面的逻辑编写两个应用程序--一个用java编写,另一个用C.用Java应用程序加密/解密"string“,当使用下面的java方法时,它正在工作。

代码语言:javascript
复制
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代码中了解到:

用于加密的

  1. 我们使用HMAC-sha256来生成“密钥”,它接受"salt",“密码”。
  2. 填充输入数据。
  3. 我们使用AES-CBC-256加密填充的输入数据,使用上面生成的“密钥”和"iv“。
  4. 我们用base64编码加密数据。

用于解密:

  1. 我们使用HMAC-sha256来生成“密钥”,它接受"salt",“密码”。
  2. 我们用base64解码并获取加密数据。
  3. 我们使用AES-CBC-256解密加密数据,使用上面生成的密钥和iv。
  4. 整理解密后的数据。

为了在C中复制相同的内容,我从下面的链接中使用了加密/解密方法;EVP对称加解密

为了生成密钥,我使用了"PKCS5_PBKDF2_HMAC“和EVP_MD作为"EVP_sha256()”。

代码语言:javascript
复制
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中有任何示例函数,这将是非常有用的。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-12-08 23:18:43

用于加密:

  1. 我们使用HMAC-sha256来生成“密钥”,它接受"salt",“密码”。

不,您使用的是PBKDF2和 HMAC-SHA256 256.这和普通的HMAC- the 256完全不一样。但是,如果您给出了正确的参数,那么所标识的OpenSSL函数确实与此匹配。这也适用于解密步骤1。

  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进行互操作。错误处理是最小的,我在一个函数中进行加密和解密;您可能希望能够将它们分开。(更正)

代码语言:javascript
复制
/* 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中获得的内容相匹配,并正确地解密。看看你得到了什么。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65195128

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档