openssl EVP_DecryptFinal_ex 错误

狂风暴雨 2016-04-16 06:57:56
在做chrome cookie Decrypt的功能小工具。
目前遇到问题EVP_DecryptFinal_ex 的时候经常返回值为0。
知道是padding的问题,但是不知道到底该如何处理这个padding。有大神指导一下吗。



#include <iostream>
#include <string>

#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/aes.h>
#include <openssl/rand.h>
#include <openssl/hmac.h>
#include <openssl/engine.h>

#include "sqlite3.h"

using namespace std;

const char kSalt[] = "saltysalt";
const int kDerivedKeySizeInBits = 128;
const int kEncryptionIterations = 1003;
const char kEncryptionVersionPrefix[] = "v10";

struct Param {
string pass;
string host;
string key;
};

static std::string &rtrim(std::string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
return s;
}

bool deriveKeyFromPassword(const char *password, int pass_len, const unsigned char *salt, int salt_len, unsigned char *out) {
if(PKCS5_PBKDF2_HMAC_SHA1(password, pass_len, salt, salt_len, kEncryptionIterations, kDerivedKeySizeInBits/8, out) != 0) {
return true;
} else {
cout << "0.PKCS5_PBKDF2_HMAC_SHA1 failed\n";
return false;
}
}

int decrypt(const unsigned char *ciphertext, int ciphertext_len, const unsigned char *key, const unsigned char *iv, unsigned char *plaintext) {
EVP_CIPHER_CTX *ctx;

int len;
int plaintext_len = -1;

if(!(ctx = EVP_CIPHER_CTX_new())) {
ERR_print_errors_fp(stderr);
return -1;
}

if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv)) {
ERR_print_errors_fp(stderr);
return -1;
}
// EVP_CIPHER_CTX_set_padding(ctx, 0);

if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)) {
ERR_print_errors_fp(stderr);
goto CLEARUP;
}

plaintext_len = len;
if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) {
ERR_print_errors_fp(stderr);
plaintext_len = -1;
goto CLEARUP;
}
plaintext_len += len;

CLEARUP:
EVP_CIPHER_CTX_free(ctx);

return plaintext_len;
}

bool chrome_decrypt(const string& password, const string &enc_value, string *dec_value) {
if (enc_value.find(kEncryptionVersionPrefix) != 0) {
cout << "invalid encrypted data\n";
return false;
}

string raw_enc_value = enc_value.substr(strlen(kEncryptionVersionPrefix));

unsigned char iv[AES_BLOCK_SIZE] = {0};
memset(iv, ' ', AES_BLOCK_SIZE);

unsigned char *decryptedtext = new unsigned char[raw_enc_value.size() + 64];
int decryptedtext_len = 0;
bool ret = false;

unsigned char aes_key[kDerivedKeySizeInBits/8] = {0};
if (deriveKeyFromPassword(password.c_str(), password.size(), (unsigned char *)kSalt, (int)strlen(kSalt), aes_key)) {
decryptedtext_len = decrypt((const unsigned char *)raw_enc_value.c_str(), raw_enc_value.size(), aes_key, iv, decryptedtext);
if (decryptedtext_len > 0) {
*dec_value = string((char *)decryptedtext, decryptedtext_len);
ret = true;
}
}

delete[] decryptedtext;

return ret;
}

int db_callback(void* param, int row_count, char** argv, char** col_name) {
Param* _param = (Param*)param;
if (!_param->host.empty() && argv[1] != _param->host)
return 0;

bool show_detail = _param->key.empty();

for(int i = 0; i < row_count; i++) {
if (show_detail) {
if (i == 12) {
string value = "";
chrome_decrypt(_param->pass, argv[i], &value);
cout << col_name[i] << ": " << rtrim(value) << "\n";
} else {
cout << col_name[i] << ": " << argv[i] << "\n";
}
} else {
if (argv[2] == _param->key && i == 12) {
string value = "";
chrome_decrypt(_param->pass, argv[i], &value);
cout << rtrim(value) << value.length() << "\n";
}
}
}
if (show_detail)
cout << "--------------------------------------------\n";
return 0;
}

void read_db(const string& db_path, const string& password, const string& host, const string& key) {
sqlite3* handle = NULL;
sqlite3_open(db_path.data(), &handle);

if (handle == NULL) {
cout << "打开cookie数据库失败!";
return;
}

sqlite3_stmt* stmp = NULL;

Param param;
param.pass = password;
param.host = host;
param.key = key;

char* error = NULL;
string sql = "select * from cookies";
sqlite3_exec(handle, sql.data(), db_callback, (void*)(¶m), &error);
sqlite3_close(handle);
}

int main(int argc, const char * argv[]) {
// read_db("/Users/bitebit/Library/Application Support/Google/Chrome/Default/Cookies", "F3/F95DvICIA==", "", "");
// read_db("/Users/bitebit/Library/Application Support/Google/Chrome/Default/Cookies", "F3/F95DvICIA==", "10.2.69.69", "");
// read_db("/Users/bitebit/Library/Application Support/Google/Chrome/Default/Cookies", "F3/F95DvICIA==", "10.2.69.69", "LBCLUSTERID");

if (argc < 3) {
cout << "使用方法: \n\t decryptor cookie文件路径 chrome钥匙串 [cookie的host] [cookie的名字]\n" << endl;
cout << "Mac下获取chrome钥匙串: \n\t security find-generic-password -w -s \"Chrome Safe Storage\"" << endl;
return 1;
}

if (argc == 5)
read_db(argv[1], argv[2], argv[3], argv[4]);
else if (argc == 4)
read_db(argv[1], argv[2], argv[3], "");
else if (argc == 3)
read_db(argv[1], argv[2], "", "");

return 0;
}

...全文
2786 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
cocoabird 2016-04-17
  • 打赏
  • 举报
回复
/*
 * key:加密密钥,一般设置位24,不知为啥
 * iv:加密初始向量
 * in_dec:密文数组,输入数组
 * out_dec:解密后的数组,输出数组
 * in_len:密文长度
 * out_len:明文长度
 * */
//解密函数
int DecryptBuffer(unsigned char * key,unsigned char *iv,unsigned char * in_dec, unsigned char *out_dec,int in_len,int *out_len)
{
 int outl;  //第一次使用update解密的数据长度
 int outl2; //剩余的字段,经过final解密并去除填充后的长度
 int rv;

 EVP_CIPHER_CTX ctx;
 //初始化ctx
 EVP_CIPHER_CTX_init(&ctx);
 //设置解密的算法、key和iv
 rv = EVP_DecryptInit_ex(&ctx,EVP_des_ede3_ecb(),NULL,key,iv);
 if(rv != 1)
 {
  EVP_CIPHER_CTX_cleanup(&ctx);
  return -1;
 }

 //循环读取原文,解密后后保存到明文文件。
 rv = EVP_DecryptUpdate(&ctx,out_dec,&outl,in_dec,in_len);//解密
 if(rv != 1)
 {
  EVP_CIPHER_CTX_cleanup(&ctx);
  return -1;
 }

 //解密结束
 rv = EVP_DecryptFinal_ex(&ctx,out_dec+outl,&outl2);

  if(rv != 1)
 {
  EVP_CIPHER_CTX_cleanup(&ctx);
  return -1;
 }
 *out_len=outl+outl2;
 EVP_CIPHER_CTX_cleanup(&ctx);//清除EVP加密上下文环境
 printf("解密已完成\n");
}
狂风暴雨 2016-04-17
  • 打赏
  • 举报
回复
那到底解密时该怎么做呢。 都是说的加密啊
引用 2 楼 yuelengdihai 的回复:
此函数调用失败的原因很多, 理解函数描述及使用细节很关键。   1、函数原型:   int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out,   int *outl);   2、函数描述:   该函数处理最后(Final)的一段数据。在函数在padding功能打开的时候(缺省)才有效,这时候,它将剩余的最后的所有数据进行加密处理。该算法使用标志的块padding方式(AKA PKCS padding)。加密后的数据写入到参数out里面,参数out的长度至少应该能够一个加密块。写入的数据长度信息输入到outl参数里面。该函数调用后,表示所有数据都加密完了,不应该再调用EVP_EncryptUpdate函数。如果没有设置padding功能,那么本函数不会加密任何数据,如果还有剩余的数据,那么就会返回错误信息,也就是说,这时候数据总长度不是块长度的整数倍。操作成功返回1,否则返回0。   PKCS padding标准是这样定义的,在被加密的数据后面加上n个值为n的字节,使得加密后的数据长度为加密块长度的整数倍。无论在什么情况下,都是要加上padding的,也就是说,如果被加密的数据已经是块长度的整数倍,那么这时候n就应该等于块长度。比如,如果块长度是9,要加密的数据长度是11,那么5个值为5的字节就应该增加在数据的后面。
cocoabird 2016-04-17
  • 打赏
  • 举报
回复
此函数调用失败的原因很多, 理解函数描述及使用细节很关键。   1、函数原型:   int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out,   int *outl);   2、函数描述:   该函数处理最后(Final)的一段数据。在函数在padding功能打开的时候(缺省)才有效,这时候,它将剩余的最后的所有数据进行加密处理。该算法使用标志的块padding方式(AKA PKCS padding)。加密后的数据写入到参数out里面,参数out的长度至少应该能够一个加密块。写入的数据长度信息输入到outl参数里面。该函数调用后,表示所有数据都加密完了,不应该再调用EVP_EncryptUpdate函数。如果没有设置padding功能,那么本函数不会加密任何数据,如果还有剩余的数据,那么就会返回错误信息,也就是说,这时候数据总长度不是块长度的整数倍。操作成功返回1,否则返回0。   PKCS padding标准是这样定义的,在被加密的数据后面加上n个值为n的字节,使得加密后的数据长度为加密块长度的整数倍。无论在什么情况下,都是要加上padding的,也就是说,如果被加密的数据已经是块长度的整数倍,那么这时候n就应该等于块长度。比如,如果块长度是9,要加密的数据长度是11,那么5个值为5的字节就应该增加在数据的后面。
狂风暴雨 2016-04-17
  • 打赏
  • 举报
回复
木有人知道该怎么处理么

24,852

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 工具平台和程序库
社区管理员
  • 工具平台和程序库社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧