【问题标题】:Decrypting the openssl library generated cipher on the terminal and vice versa在终端上解密 openssl 库生成的密码,反之亦然
【发布时间】:2019-10-20 04:33:35
【问题描述】:

我已经使用 OpenSSL 库来加密密码。 我能够使用代码中的库成功加密和解密密码。

但是,如果我尝试在终端上的 linux 系统上解密库生成的密码,则解密失败。

加密:

openssl enc -aes-256-cbc -base64 -salt -k <passphrase> -in plain.txt -out 
encrypt.txt

解密:

openssl enc -aes-256-cbc -base64 -salt -d -k <passphrase> -in encrypt.txt -out plain.txt

请帮忙。

我已经去除了盐,以使过程更简单。 我在终端上生成了示例 base64 密码并尝试使用库解密,但失败了。 我尝试使用 Library 生成示例 base64 密码并尝试在终端中对其进行解密,这也失败了!

size_t init_key_iv(const std::string& pass, const unsigned char* salt, unsigned char* key, unsigned char* iv ) {
size_t derived_key_size = 0;
  const unsigned char * pass_key = reinterpret_cast<const unsigned char*>( pass.c_str() );
  const size_t pass_key_len = pass.size();
  if(salt && key && iv && pass_key && pass_key_len > 0) {
    memset(key, 0, sizeof(key));
    memset( iv, 0, sizeof(iv));
    derived_key_size = EVP_BytesToKey(cipher_type, msg_digest_type, salt, pass_key, pass_key_len, 5, key, iv);
  }
 return derived_key_size;
}

void encrypt(const unsigned char* msg, unsigned char** encrypted_message, const size_t msg_len, const unsigned char *key, unsigned char *iv) {
    AES_KEY enc_key;
    AES_set_encrypt_key(key, 256, &enc_key);
    AES_cbc_encrypt(msg, *encrypted_message, msg_len, &enc_key, iv, AES_ENCRYPT);
}

void decrypt(const unsigned char* cipher, unsigned char** decrypted_msg, const size_t cipher_len, const unsigned char *key, unsigned char *iv ) {
  AES_KEY enc_key;
  AES_set_decrypt_key(key, 256, &enc_key);
  AES_cbc_encrypt(cipher, *decrypted_msg, cipher_len, &enc_key, iv, AES_DECRYPT);
}

int decode(const char* b64_msg, unsigned char** decode_msg, const size_t  decode_msg_len) {
  size_t bytes_decoded = 0;
  bytes_decoded = EVP_DecodeBlock(*decode_msg, (unsigned char *)b64_msg, strlen(b64_msg));
  return bytes_decoded;
}

int encode(const unsigned char* msg, const size_t msg_len, char** b64_msg) {
  size_t bytes_encoded = 0;
  if(msg && msg_len > 0 && b64_msg)  {
    bytes_encoded = EVP_EncodeBlock((unsigned char *) *b64_msg, msg, msg_len);
  }
  return bytes_encoded;
}

const int derived_key_size = init_key_iv(password, salt, key, iv_enc);
encrypt((unsigned char *)msg, &encrypted_message, strlen(msg), key, iv_enc);
const size_t bytes_encoded( encode((const unsigned char*)encrypted_message, strlen(reinterpret_cast<char*>(encrypted_message)), &base64_enc_str) );
const size_t bytes_decoded( CBase64::decode(cipher_base64, &cipher, cipher_len) );
decrypt(cipher, &decrypted_message, cipher_len, key, iv_dec);

期望是;库生成的 base64 密码应在 openssl 终端中解密,反之亦然。

【问题讨论】:

  • 那会是什么执行副总裁?除了EVP_BytesToKey 和编码/解码之外,我没有看到任何以EVP_ 开头的函数,但没有看到加密。
  • AES_ 在内部使用 EVP。
  • 不过,为了避免混淆,我会更正!感谢您的提示。

标签: c security encryption openssl ubuntu-14.10


【解决方案1】:

您的代码不完整,有几处未显示可能错误,但显示肯定或可能错误的是:

  • 当命令行 enc 使用 count=1 时,您使用 count=5 调用 EVP_BytesToKey。此外,您没有显示 cipher_typemsg_digest_type 是什么,它们可能是错误的;特别是,命令行enc 中用于BytesToKey 的默认摘要取决于OpenSSL 的版本,并且您没有说明您正在使用或将要使用或可能使用的版本。尽管指定 -md $hash 会覆盖该默认值,但这是一种更强大、更清晰的解决方案。

  • 你没有显示明文的来源,特别是你是否以及如何填充它。默认情况下,命令行enc 使用 PKCS5/7 填充,并且可以选择不使用填充,但在这种情况下,明文长度必须始终是 16 的精确倍数——你保证了吗?

  • 你使用strlen(ciphertext)作为(原始=base64之前)密文的长度;这通常是错误的。密文实际上是随机位,并且可以很容易地包含一个值为 0 的字节,从而给出一个太小的 strlen(),但如果没有发生这种情况,它不一定会跟随或终止一个 0 字节,给出一个 strlen () 太大了。

  • 在使用 salt 时,您没有在 base64 编码中包含命令行enc 所需的文件头(又名“魔术”)。显示的代码没有添加命令行enc 文件格式所需的换行符,但这可以在其他地方完成,并且只有当您加密(并想要解密)的值是或(曾经)可以超过 31 个字节时才重要.

  • 另外,当 memset (key, 0, sizeof(key))iv 是指针时,它们也一样;这只会清除 指针 的大小,在现代系统中为 4 或 8 个字节,而不是指向的对象。但由于这些对象会立即被BytesToKey 覆盖,所以这个错误并不重要。

无论如何,这里有一个 完整的最小化代码,并生成一个输出,该输出可通过命令行enc -aes-256-cbc -d -a -k $password-md sha256 解密,适用于低于 1.1.0 的版本,这不是默认值.为方便起见,我将输入限制为 80 字节,但如果需要如何增加它应该很明显。

/* SO56447374 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/aes.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rand.h>

void err (const char *label){
  fprintf (stderr, "%s:\n", label); ERR_print_errors_fp (stderr); exit (1);
}

int main (int argc, char**argv)
{
  ERR_load_crypto_strings(); 
  //OPENSSL_add_all_algorithms_noconf(); 

  const char * pass = argv[1];
  unsigned char salt [8], key [32], iv [16],
        plain [80], buffer [16+96], *cipher = buffer+16;
  int inlen = fread (plain, 1, 80-1, stdin), pad = 16-inlen%16U;
  AES_KEY aeskey;

  RAND_bytes (salt, 8);
  if( !EVP_BytesToKey (EVP_aes_256_cbc(), EVP_sha256(), salt, 
        (unsigned char*)pass, strlen(pass), 1, key, iv) ) err("BytesToKey");
  AES_set_encrypt_key (key, 256, &aeskey);
  memset (plain+inlen, pad, pad); // PKCS5/7 
  AES_cbc_encrypt (plain, cipher, inlen+pad, &aeskey, iv, AES_ENCRYPT);
  memcpy (buffer+0, "Salted__", 8); memcpy (buffer+8, salt, 8);
  BIO *bio1 = BIO_new (BIO_f_base64()); // does b64 with linebreaks (by default)
  BIO_push (bio1, BIO_new_fp (stdout, BIO_NOCLOSE));
  BIO_write (bio1, buffer, 16+inlen+pad);
  BIO_flush (bio1);
  BIO_free_all (bio1);
  return 0;
}

【讨论】:

  • 首先,感谢您的反馈!对不起,我错过了一些细节。 1. 我使用的是 Ubuntu 14.04 中包含的 openssl 版本 1.0.1f 对于密码类型和消息摘要,这里是:const EVP_CIPHER * CAES::cipher_type = EVP_aes_256_cbc(); const EVP_MD * CAES::msg_digest_type = EVP_sha256(); 2. 我没有包含任何填充代码,我不知道这一点。 3. 明白了,我会相应地改变。 4. 你能再解释一下吗? 5.我需要先让代码工作,然后我一定会修复泄漏/代码问题:-)
  • 我可以使用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) 导出正确的密钥吗?我还需要做显式填充吗?
  • @moonking1863 您需要使用填充将明文适合块长度(填充与密钥生成算法无关)。对于键拉伸,您可以使用 EVP 或 PBKDF,但不能混合使用它们
  • @moonking1863:正如我所说,带有 SHA256 的 BytesToKey 对于 OpenSSL 1.1.0 以上是正确的,但默认情况下不低于,但可以被覆盖。 OpenSSL 1.1.1 但不是更低版本可以使用 PBKDF2(_HMAC) 而不是 BytesToKey 来处理enc(非标准地生成密钥和 IV,如 PKCS5v1/PBKDF1 和 BytesToKey 所做的)。正如 gusto2 所说,密钥(和 IV)派生和填充是独立且不相关的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-12
  • 2017-11-14
  • 2019-08-16
  • 2011-12-25
  • 2021-11-10
相关资源
最近更新 更多