当前位置: 首页 > 知识库问答 >
问题:

将Php open_ssl_decrypt AES 256 CBC实现为加密JS

佟翰林
2023-03-14
    function decryptOpensslDigestSHA256Sum($data)
    {
        $key = hash('sha256', 'Nootric2703202'); //My password has 14 characters

        $method = 'AES-256-CBC';

        $data = base64_decode($data);

        $iv_size = openssl_cipher_iv_length($method);

        $salt_header = substr($data, 0, $iv_size);

        if (substr($salt_header, 0, 8) != "Salted__") {
            return "";
        }
        $salt = substr($salt_header, 8);

        $creds = extractOpenSSLCreds($key, $salt, $iv_size);

        $data = openssl_decrypt(substr($data, $iv_size), $method, $creds['password'], OPENSSL_RAW_DATA, $creds['iv']);

        return $data;
    }

    function extractOpenSSLCreds($key, $salt, $iv_size)
    {
        $m = "";
        while (strlen($m) < 48) {
            $m .= hashCryptoDigestSHA256Sum($m, $key, $salt);
        }
        $result = array(
            'password' => substr($m, 0, 32),
            'iv' => substr($m, 32, $iv_size)
        );
        return $result;
    }

    function hashCryptoDigestSHA256Sum($hash, $key, $salt)
    {
        $hash.= $key.$salt;
        $prev = openssl_digest($hash, "sha256", true);
        return $prev;
    }
$data = "U2FsdGVkX1++7PN6CsF5Bi38t0N3EjXpH5oGpaIZXUwk4T8QCwcATjvA4b/8VaxD8nf/MZhKPnWb1L8raLR4lw==";
echo "Data urlEncoded: $data<br>";
$decryption = decryptOpensslDigestSHA256Sum($data);
echo "Data decrypted: $decryption<br><br>";
function CryptoJSAesDecrypt(encrypted){
    // 1. Separate ciphertext and salt
    var encryptedWA = CryptoJS.enc.Base64.parse(encrypted);
    var prefixWA = CryptoJS.lib.WordArray.create(encryptedWA.words.slice(0, 8/4)); // Salted__ prefix

    var saltWA = CryptoJS.lib.WordArray.create(encryptedWA.words.slice(8/4, 16/4));  // 8 bytes salt: 0x0123456789ABCDEF
    var ciphertextWA = CryptoJS.lib.WordArray.create(encryptedWA.words.slice(16/4, encryptedWA.words.length)); // ciphertext        

    // 2. Determine key and IV using PBKDF2
    var password = 'Nootric2703202'
    var keyIvWA = CryptoJS.PBKDF2(
    password, 
    saltWA, 
    {
        keySize: (32+16)/4,          // key and IV
        iterations: 10000,
        hasher: CryptoJS.algo.SHA256
    }
    );
    var keyWA = CryptoJS.lib.WordArray.create(keyIvWA.words.slice(0, 32/4));
    var ivWA = CryptoJS.lib.WordArray.create(keyIvWA.words.slice(32/4, (32+16)/4));

    // 3. Decrypt
    var decryptedWA = CryptoJS.AES.decrypt(
    {ciphertext: ciphertextWA}, 
    keyWA, 
    {iv: ivWA}
    );
    var decrypted = decryptedWA.toString(CryptoJS.enc.Utf8)
    return decrypted;
}

    const dec = CryptoJSAesDecrypt("U2FsdGVkX1++7PN6CsF5Bi38t0N3EjXpH5oGpaIZXUwk4T8QCwcATjvA4b/8VaxD8nf/MZhKPnWb1L8raLR4lw==");
    console.log("Data Decrypted: " + dec);
    const dec = CryptoJSAesDecrypt("U2FsdGVkX18BI0VniavN78vlhR6fryIan0VvUrdIr+YeLkDYhO2xyA+/oVXJj/c35swVVkCqHPh9VdRbNQG6NQ==");
    console.log("Data Decrypted: " + dec);

并在Javascript函数中替换这一行:

var password = 'Nootric2703202'

用这个:

var password = 'mypassword'

这个很管用!但是对于我自己的密码&我加密的数据,这个JS解密代码不起作用。请帮帮我?

const dec = CryptoJSAesDecrypt();
console.log("Decrypted: " + dec);

function CryptoJSAesDecrypt(encrypted){
    // 1. Separate ciphertext and salt
    var encrypted = "U2FsdGVkX1++7PN6CsF5Bi38t0N3EjXpH5oGpaIZXUwk4T8QCwcATjvA4b/8VaxD8nf/MZhKPnWb1L8raLR4lw==";
    console.log("Encrypted:", encrypted);

    var encryptedWA = CryptoJS.enc.Base64.parse(encrypted);
    var prefixWA = CryptoJS.lib.WordArray.create(encryptedWA.words.slice(0, 8/4)); // Salted__ prefix

    var saltWA = CryptoJS.lib.WordArray.create(encryptedWA.words.slice(8/4, 16/4)); // 8 bytes salt: 0x0123456789ABCDEF 

    var ciphertextWA = CryptoJS.lib.WordArray.create(encryptedWA.words.slice(16/4, encryptedWA.words.length)); // ciphertext 

    // 2. Determine key and IV using PBKDF2
    var password = 'Nootric2703202'
    var keyIvWA = CryptoJS.PBKDF2(
    password, 
    saltWA, 
    {
        keySize: (32+16)/4,          // key and IV
        iterations: 10000,
        hasher: CryptoJS.algo.SHA256
    }
    );
    var keyWA = CryptoJS.lib.WordArray.create(keyIvWA.words.slice(0, 32/4));
    var ivWA = CryptoJS.lib.WordArray.create(keyIvWA.words.slice(32/4, (32+16)/4));

    // 3. Decrypt
    var decryptedWA = CryptoJS.AES.decrypt(
    {ciphertext: ciphertextWA}, 
    keyWA, 
    {iv: ivWA}
    );
    var decrypted = decryptedWA.toString(CryptoJS.enc.Utf8)
    return decrypted;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
PHP_OPENSSL_API zend_string* php_openssl_decrypt(
    const char *data, size_t data_len,
    const char *method, size_t method_len,
    const char *password, size_t password_len,
    zend_long options,
    const char *iv, size_t iv_len,
    const char *tag, zend_long tag_len,
    const char *aad, size_t aad_len)
{
    const EVP_CIPHER *cipher_type;
    EVP_CIPHER_CTX *cipher_ctx;
    struct php_openssl_cipher_mode mode;
    int i = 0, outlen;
    zend_string *base64_str = NULL;
    bool free_iv = 0, free_password = 0;
    zend_string *outbuf = NULL;

    PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(data_len, data);
    PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(password_len, password);
    PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(aad_len, aad);
    PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(tag_len, tag);


    cipher_type = EVP_get_cipherbyname(method);
    if (!cipher_type) {
        php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
        return NULL;
    }

    cipher_ctx = EVP_CIPHER_CTX_new();
    if (!cipher_ctx) {
        php_error_docref(NULL, E_WARNING, "Failed to create cipher context");
        return NULL;
    }

    php_openssl_load_cipher_mode(&mode, cipher_type);

    if (!(options & OPENSSL_RAW_DATA)) {
        base64_str = php_base64_decode((unsigned char*)data, data_len);
        if (!base64_str) {
            php_error_docref(NULL, E_WARNING, "Failed to base64 decode the input");
            EVP_CIPHER_CTX_free(cipher_ctx);
            return NULL;
        }
        data_len = ZSTR_LEN(base64_str);
        data = ZSTR_VAL(base64_str);
    }

    if (php_openssl_cipher_init(cipher_type, cipher_ctx, &mode,
                &password, &password_len, &free_password,
                &iv, &iv_len, &free_iv, tag, tag_len, options, 0) == FAILURE ||
            php_openssl_cipher_update(cipher_type, cipher_ctx, &mode, &outbuf, &outlen,
                data, data_len, aad, aad_len, 0) == FAILURE) {
        outbuf = NULL;
    } else if (mode.is_single_run_aead ||
            EVP_DecryptFinal(cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + outlen, &i)) {
        outlen += i;
        ZSTR_VAL(outbuf)[outlen] = '\0';
        ZSTR_LEN(outbuf) = outlen;
    } else {
        php_openssl_store_errors();
        zend_string_release_ex(outbuf, 0);
        outbuf = NULL;
    }

    if (free_password) {
        efree((void *) password);
    }
    if (free_iv) {
        efree((void *) iv);
    }
    if (base64_str) {
        zend_string_release_ex(base64_str, 0);
    }
    EVP_CIPHER_CTX_reset(cipher_ctx);
    EVP_CIPHER_CTX_free(cipher_ctx);
    return outbuf;
}

/* {{{ Takes raw or base64 encoded string and decrypts it using given method and key */
PHP_FUNCTION(openssl_decrypt)
{
    zend_long options = 0;
    char *data, *method, *password, *iv = "", *tag = NULL, *aad = "";
    size_t data_len, method_len, password_len, iv_len = 0, tag_len = 0, aad_len = 0;
    zend_string *ret;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|lsss", &data, &data_len, &method, &method_len,
                    &password, &password_len, &options, &iv, &iv_len, &tag, &tag_len, &aad, &aad_len) == FAILURE) {
        RETURN_THROWS();
    }

    if (!method_len) {
        zend_argument_value_error(2, "cannot be empty");
        RETURN_THROWS();
    }

    if ((ret = php_openssl_decrypt(data, data_len, method, method_len, password, password_len, options, iv, iv_len, tag, tag_len, aad, aad_len))) {
        RETVAL_STR(ret);
    } else {
        RETVAL_FALSE;
    }
}
/* {{{ Computes digest hash value for given data using given method, returns raw or binhex encoded string */
PHP_FUNCTION(openssl_digest)
{
    bool raw_output = 0;
    char *data, *method;
    size_t data_len, method_len;
    const EVP_MD *mdtype;
    EVP_MD_CTX *md_ctx;
    unsigned int siglen;
    zend_string *sigbuf;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|b", &data, &data_len, &method, &method_len, &raw_output) == FAILURE) {
        RETURN_THROWS();
    }
    mdtype = EVP_get_digestbyname(method);
    if (!mdtype) {
        php_error_docref(NULL, E_WARNING, "Unknown digest algorithm");
        RETURN_FALSE;
    }

    siglen = EVP_MD_size(mdtype);
    sigbuf = zend_string_alloc(siglen, 0);

    md_ctx = EVP_MD_CTX_create();
    if (EVP_DigestInit(md_ctx, mdtype) &&
            EVP_DigestUpdate(md_ctx, (unsigned char *)data, data_len) &&
            EVP_DigestFinal (md_ctx, (unsigned char *)ZSTR_VAL(sigbuf), &siglen)) {
        if (raw_output) {
            ZSTR_VAL(sigbuf)[siglen] = '\0';
            ZSTR_LEN(sigbuf) = siglen;
            RETVAL_STR(sigbuf);
        } else {
            int digest_str_len = siglen * 2;
            zend_string *digest_str = zend_string_alloc(digest_str_len, 0);

            make_digest_ex(ZSTR_VAL(digest_str), (unsigned char*)ZSTR_VAL(sigbuf), siglen);
            ZSTR_VAL(digest_str)[digest_str_len] = '\0';
            zend_string_release_ex(sigbuf, 0);
            RETVAL_NEW_STR(digest_str);
        }
    } else {
        php_openssl_store_errors();
        zend_string_release_ex(sigbuf, 0);
        RETVAL_FALSE;
    }

    EVP_MD_CTX_destroy(md_ctx);
}

共有1个答案

湛宏旷
2023-03-14

PHP实现使用evp_bytestokey()作为密钥派生函数,因此与CryptoJS密钥派生兼容。

但是,CryptoJS默认应用MD5作为摘要,而PHP代码使用SHA256(注意OpenSSL从V1.1.0版本开始将默认摘要从MD5更改为SHA256)
而且,为密钥派生函数应用的密码不是密码本身(即Nootric2703202),而是密码的十六进制编码SHA256哈希。

如果考虑到这一点,则使用CryptoJS进行解密:

var password = 'Nootric2703202';
var passwordHashWA = CryptoJS.SHA256(password);
var passwordHashHex = passwordHashWA.toString(CryptoJS.enc.Hex); 

var ciphertext = 'U2FsdGVkX1++7PN6CsF5Bi38t0N3EjXpH5oGpaIZXUwk4T8QCwcATjvA4b/8VaxD8nf/MZhKPnWb1L8raLR4lw==';

CryptoJS.algo.EvpKDF.cfg.hasher = CryptoJS.algo.SHA256.create();           
var data = CryptoJS.AES.decrypt(ciphertext, passwordHashHex);
console.log(data.toString(CryptoJS.enc.Utf8));
html prettyprint-override"><script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
 类似资料:
  • 本文向大家介绍JS实现的base64加密、md5加密及sha1加密详解,包括了JS实现的base64加密、md5加密及sha1加密详解的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了JS实现的base64加密、md5加密及sha1加密。分享给大家供大家参考,具体如下: 1、base64加密 在页面中引入base64.js文件,调用方法为: 2、md5加密 在页面中引用md5.js文件,调用

  • 本文向大家介绍PHP实现加强版加密解密类实例,包括了PHP实现加强版加密解密类实例的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了PHP实现加强版加密解密类。分享给大家供大家参考。具体如下: 希望本文所述对大家的php程序设计有所帮助。

  • 我试图学习并在java中实现JWE来解决问题。我试图理解内容加密密钥是如何使用特定算法生成的(比如RSA-PKCS1\u 1.5)。 我知道如何使用密钥生成器生成一对密钥,然后使用公钥进行加密,使用私钥进行解密。此外,我知道如何通过给出声明来创建简单的JWT令牌,以及如何对其进行签名。我试着遵循以下步骤: 消息加密过程如下: 生成随机内容加密密钥(CEK)。CEK的长度必须至少等于所需加密密钥的长

  • 本文向大家介绍WebApi实现通讯加密,包括了WebApi实现通讯加密的使用技巧和注意事项,需要的朋友参考一下 一. 场景介绍: 如题如何有效的,最少量的现有代码侵入从而实现客户端与服务器之间的数据交换加密呢? 二. 探究: 1.需求分析 webapi服务端 有如下接口: 无加密请求 GET /api/apitest?id=10 返回结果 response "value10" 我们想要达到的效果为

  • 本文向大家介绍python实现RSA加密(解密)算法,包括了python实现RSA加密(解密)算法的使用技巧和注意事项,需要的朋友参考一下 RSA是目前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的绝大多数密码攻击,已被ISO推荐为公钥数据加密标准。 今天只有短的RSA钥匙才可能被强力方式解破。到2008年为止,世界上还没有任何可靠的攻击RSA算法的方式。只要其密钥的长度足够长,用RSA加密

  • 本文向大家介绍java实现Base64加密解密算法,包括了java实现Base64加密解密算法的使用技巧和注意事项,需要的朋友参考一下 Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一,大家可以查看RFC2045~RFC2049,上面有MIME的详细规范。Base64编码可用于在HTTP环境下传递较长的标识信息。例如,在Java Persistence系统Hibernate中,就