消息关闭
    暂无新消息!
在做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*)(&param), &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;
}


3个回答

︿ 3
此函数调用失败的原因很多, 理解函数描述及使用细节很关键。
  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的字节就应该增加在数据的后面。
︿ 0
/*
 * 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");
}