c - 在终端上解密 openssl 库生成的密码,反之亦然

标签 c security encryption openssl ubuntu-14.10

我已经使用 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 密码并尝试使用库解密,但失败了。 我已经尝试使用库生成示例 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 终端中解密,反之亦然。

最佳答案

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

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

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

  • 您使用 strlen(ciphertext) 作为(raw = before 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;
}

关于c - 在终端上解密 openssl 库生成的密码,反之亦然,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56447374/

相关文章:

Java 相当于 MD5CryptoServiceProvider computeHash & ToBase64String

javascript - 客户端代码 Web 应用程序的许可系统

c - mmap函数中的MAP_SHARED和MAP_PRIVATE有什么区别?

c - 可移植地防止函数内联

c - 尝试打印指针值时出现堆异常

javascript - 捕获跨源 iframe 事件

security - HS512 JWT算法中秘钥的理想特征是什么?

将不带引号的斜杠转换为换行符,无需 strtok 或进一步分配内存

c# - Windows Identity Foundation 中的 TrustedIssuers

wordpress - 如何在 Wordpress 中更新新密码之前从 db 验证旧密码