3

我正在尝试解密使用 pgcrypto 加密的数据。我没有使用 IV,因为它只是一个测试,但我无法在 C# 中解密数据。

PostGres中的加密:

enc_key := '\\xAACE38F289EC3EA209B48D';

-- Time insertions
ts_start := clock_timestamp();
FOR i IN 1..num_loops LOOP

    -- The text to insert and its key
    plaintext := 'Number: ' || i;
    plaintext_pk := gen_random_uuid();
    plaintext_pk_as_text := plaintext_pk::text;

    -- The ref entries
    user_pk := gen_random_uuid();
    user_ref_pk := encrypt(plaintext_pk_as_text::bytea, enc_key, 'aes');

    -- Add the enries
    INSERT INTO "Text" VALUES(plaintext_pk, plaintext);
    INSERT INTO "User" VALUES(user_ref_pk, user_pk);

END LOOP;
ts_end := clock_timestamp();
elapsed_raw := cast(extract(epoch from (ts_end - ts_start)) as numeric(18,3));

在 C# 中解密:

// The decryption key
byte[] enc_key = new byte[] {   0xAA, 0xCE, 0x38, 0xF2, 0x89, 0xEC, 0x3E, 0xA2, 0x09, 0xB4, 0x8D,
0x00, 0x00, 0x00, 0x00, 0x00 };

public static string AESDecryptByteArray(byte [] encoded_data, byte [] key)
{
    string result = "";
    byte [] result_ba = new byte[64];

    using (Aes myAes = Aes.Create())
    {
        if (myAes == null)
        {
            throw new Exception("Failed to create AES object.");
        }

        myAes.Key = key;
        myAes.Mode = CipherMode.CBC;
        myAes.Padding = PaddingMode.PKCS7;

        MemoryStream streamMem = new MemoryStream(encoded_data);

        byte[] IV = new byte[16];
        // streamMem.Read(IV, 0, 16);
        for (int i = 0; i < 16; ++i )
        {
            IV[i] = 0;
        }
        myAes.IV = IV;

        int iNumBytes = 0;
        var decryptor = myAes.CreateDecryptor();
        using (CryptoStream streamCrypt = new CryptoStream(streamMem, decryptor, CryptoStreamMode.Read))
        {
           iNumBytes = streamCrypt.Read(result_ba, 0, 48);
        }

        result = System.Text.Encoding.ASCII.GetString(result_ba);
    }

    return result;

} // AESDecryptByteArray

我从其中一行复制了生成的加密数据和二进制密钥,但 C# 代码不断出现 CryptographicException(“填充无效且无法删除”)异常。我的理解是 pgcrypto 的 encrypt() 默认为 cbc\pkcs。显然,我错过了一些东西。

感激地收到任何帮助。

亚当。

4

1 回答 1

3

尝试了迈克尔的建议,当然没有得到正确的结果。发现问题。PG 的 string 到 bytea 的转换不适合粗心的人。重要线索来自

DO $$

    declare enc_data    bytea;
        enc_key     bytea;

        dec_bytea   bytea;
        dec_text    text;

begin

    enc_data := '\305\347fyau\030 \223\014E\307\346\267|\365R\3236l\322f\344\312z\220\271\207C\003\255\210+\316\330&\205l>\342\203\350\214$W\253\370D';
    enc_key := '\\xAACE38F289EC3EA209B48D';

    dec_bytea := decrypt(enc_data, enc_key, 'aes');
    dec_text := dec_bytea::text;

    raise info 'Decoded text ->  %', dec_text;

    DROP TABLE IF EXISTS tmpTable;
    CREATE TEMPORARY TABLE tmpTable AS
        select  dec_text as "Decoded text",
            char_length(dec_text) as "Decoded length",
            length(enc_data) as "Encoded length",
            enc_key as "Enc Key",
            length(enc_key) as "Enc Key Len",
            encode(enc_key, 'hex') as "Hex key",
            encode(enc_key, 'escape') as "Esc key";

END $$;

select * from tmpTable;

这表明 PG 中的二进制密钥是 24 个字节长 - 而不是我预期的 11 个字节。这归结为我对 PG 的字符串到 bytea 转换如何工作的误解。我认为“\\xAACE38F289EC3EA209B48D”将转换为 11 字节数组(https://www.postgresql.org/docs/9.6/static/datatype-binary.html,第 8.4.1 节),但不需要加倍的反斜杠。所以我的字符串翻译成 '\', 'x', 'A' ... 'D' - 一个 24 字节数组。

//
// In C# this is the key needed
//
byte[] enc_key_aaaahhhh =
new byte[] {    0x5c,   0x78,   0x41,   0x41,   0x43,   0x45,   0x33,   0x38,
                0x46,   0x32,   0x38,   0x39,   0x45,   0x43,   0x33,   0x45,
                0x41,   0x32,   0x30,   0x39,   0x42,   0x34,   0x38,   0x44    };

//
// This is wrong.
// For this key you'd need to enter '\xAACE38F289EC3EA209B48D' in PG - only one backslash
//
byte[] enc_key = new byte[] {   0xAA, 0xCE, 0x38, 0xF2, 0x89, 0xEC, 0x3E, 0xA2, 0x09, 0xB4, 0x8D,
                                0x00, 0x00, 0x00, 0x00, 0x00 };

(没有帮助我将错误的 GUID 复制到我的 C# 代码中进行比较 - 真正的 GUID 是“d6edd775-47c5-4779-a761-7f8297130073”。)

希望有一天这可能对某人有所帮助。

  • 亚当。
于 2016-11-09T11:37:50.580 回答