0

I have a message encrypted in PHP like this:

function encode(string $input): string
{
    return base64EncodeSafe(encodeOpenSsl($input));
}

function encodeOpenSsl(string $string): string
{
    if (!defined('CRYPT_KEY') || !defined('CRYPT_CIPHER')) {
        return $string;
    }

    $key = hex2bin(CRYPT_KEY);
    $ivLength = openssl_cipher_iv_length(CRYPT_CIPHER);
    do {
        $iv = openssl_random_pseudo_bytes($ivLength, $cstrong);
    } while (!$iv && !$cstrong);
    $encryptedString = $iv . openssl_encrypt($string, CRYPT_CIPHER, $key, OPENSSL_RAW_DATA, $iv, $tag);

    if (!empty($tag)) {
        $encryptedString .= '||' . $tag;
    }
    return base64_encode($encryptedString . '||' . $tag);
}

function base64EncodeSafe(string $string): string
{
    return str_replace(['/', '+'], ['_', '-'], base64_encode($string));
}

And I need to decrypt it in Javascript. This is my decryption code:

async function decode(input) {
    return await decodeOpenSsl(base64DecodeSafe(input));
}

function base64DecodeSafe(string) {
    return atob(string.replace(/_/g, '/').replace(/-/g, '+'));
}

async function decodeOpenSsl(data) {
    const key = hexStringToByteArray(CRYPT_KEY);
    const parts = atob(data).split('||');
    const ivAndEncrypted = new Uint8Array(parts[0].split('').map(c => c.charCodeAt(0)));
    const tag = new Uint8Array(parts[1].split('').map(c => c.charCodeAt(0)));
    const iv = ivAndEncrypted.slice(0, 12);
    const encrypted = ivAndEncrypted.slice(12);

    const encryptedWithTag = new Uint8Array([...encrypted, ...tag]);

    const cryptoKey = await crypto.subtle.importKey(
        'raw',
        key,
        { name: 'AES-GCM' },
        false,
        ['decrypt']
    );

    const decrypted = await crypto.subtle.decrypt(
        { name: 'AES-GCM', iv: iv, additionalData: new Uint8Array(), tagLength: 128 },
        cryptoKey,
        encryptedWithTag
    );

    return new TextDecoder().decode(decrypted);
}

function hexStringToByteArray(hexString) {
    var result = [];
    while (hexString.length >= 2) {
        result.push(parseInt(hexString.substring(0, 2), 16));
        hexString = hexString.substring(2, hexString.length);
    }
    return new Uint8Array(result);
}

But I keep getting the following:

DOMException [OperationError]: The operation failed for an operation-specific reason at AESCipherJob.onDone (node:internal/crypto/util:445:19) { [cause]: [Error: Cipher job failed]

The error doesn't give much info probably for security reasons, but maybe someone had a problem like this and could help. Thank you.

5
  • 2
    CRYPT_CIPHER is what? Commented May 8, 2024 at 0:04
  • 1
    The PHP code appends the tag twice (each time using the separator ||). Note that although the WebCrypto API concatenates ciphertext and tag (only once, of course), it does so without a separator. In addition, the ciphertext is Base64 encoded and then Base64url encoded again. The double encoding is unnecessary. Commented May 8, 2024 at 0:36
  • @PresidentJamesK.Polk sorry, it's "aes-256-gcm". Commented May 8, 2024 at 6:10
  • @Topaco thank you for the suggestions, I did try it out, but seem it still results in the error. Commented May 8, 2024 at 6:11
  • 1
    Please edit your question and post the fixed PHP code (do not overwrite the old PHP code so that future readers can reproduce the problem). Also post sample data: key (CRYPT_KEY), plaintext and ciphertext generated with the fixed PHP code. Commented May 8, 2024 at 8:06

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.