c# - 将 C openssl TripleDes 加密转换为 .NET

标签 c# c openssl tripledes

我一直在尝试复制第三方集成的加密过程。第 3 方使用 openssl,并给了我他们用来执行该过程的 C 代码。几周来我一直在尝试将这个过程移植到 C#,但我似乎遗漏了一些我无法解决的问题。很可能我在转置 C 代码和 OpenSSL 库时遗漏了一些东西,但我一生都无法弄清楚它 遗憾的是,OpenSSL 到 .NET 的主要端口( https://github.com/openssl-net/openssl-net )不支持 TripleDes,因此无法使用

这是示例 C 代码

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <openssl/dh.h>
#include <openssl/pem.h>
#include <openssl/engine.h>
#include <openssl/bn.h>
#include <openssl/des.h>
#include <openssl/rand.h>

static void encrypt_ean(int argc, char **argv)
{
    size_t i;

    if (argc != 3)
        usage();
    char *mwkstr = argv[1];
    char *ean = argv[2];

    unsigned char mwk[24];
    hex2bin(mwkstr, 48, mwk);

    DES_key_schedule keysched[3];
    set_key_checked(mwk, keysched);

    unsigned char idata[16];
    unsigned char odata[16];
    DES_cblock zero_iv;
    memset(&zero_iv, 0, sizeof(zero_iv));
    if (RAND_bytes(idata+0, 8) != 1) {
        fprintf(stderr, "RAND_bytes failed.\n");
        exit(1);
    }

    for (i=0; i<8; i++)
        idata[8+i] = (i >= eanlen) ? ' ' : ean[i];
    idata[7] = chksum((char *)idata+8, 8);
    if (g_verbose) {
        printf("ean = %s\n", mean);
        printf("idata = ");
        for (i=0; i<sizeof(idata); i++)
            printf("%d %02X\n", (int)i, idata[i]);
        printf("\n");
    }
    DES_ede3_cbc_encrypt(idata, odata, sizeof(odata),
        &keysched[0], &keysched[1], &keysched[2], &zero_iv, DES_ENCRYPT);
    for (i=0; i<sizeof(odata); i++)
        printf("%02X", odata[i]);
    printf("\n");
}

static unsigned char chksum(char *data, size_t datalen)
{
    size_t i;
    unsigned char sum=0;

    for (i=0; i<datalen; i++)
        sum += data[i];
    return sum;
}

static void hex2bin(const char *str, int len, unsigned char *bin)
{
    int i, j, x;

    for (i=0, j=0; i<len; i+=2) {
        char tmpstr[3];
        tmpstr[0] = str[i+0];
        tmpstr[1] = str[i+1];
        tmpstr[2] = '\0';
        sscanf(tmpstr, "%02X", &x);
        bin[j++] = x;
    }
}

static int set_key_checked(unsigned char *key, DES_key_schedule *keysched)
{
    if (DES_set_key_checked((const_DES_cblock *)(key+0), &keysched[0]) < 0) {
set_key_err:
        fprintf(stderr, "DES_set_key_checked failed.\n");
        exit(1);
    }
    if (DES_set_key_checked((const_DES_cblock *)(key+8), &keysched[1]) < 0)
        goto set_key_err;
    if (DES_set_key_checked((const_DES_cblock *)(key+16), &keysched[2]) < 0)
        goto set_key_err;
    return 0;
}

这是我的 C# 代码(考虑 ean = pin 以便于转置)

internal static class PINEncoding
{
    internal static string EncodePIN(string unencodedPIN, string decryptedWorkingKey)
    {
        var bytes = GenerateRandomBytes();
        var asciiPin = ConvertPINToASCIIBytes(unencodedPIN);

        var checksum = new byte[1];
        checksum[0] = ComputeChecksum(asciiPin);

        var pinBlock = ObtainPinBlock(bytes, checksum, asciiPin);

        return EncryptPIN(pinBlock, decryptedWorkingKey);
    }

    private static byte[] GenerateRandomBytes()
    {
        Random rnd = new Random();
        byte[] b = new byte[7];

        rnd.NextBytes(b);
        return b;
    }

    private static byte[] ConvertPINToASCIIBytes(string pin)
    {
        return ASCIIEncoding.ASCII.GetBytes(pin);
    }

    private static byte ComputeChecksum(byte[] data)
    {
        long longSum = data.Sum(x => (long)x);
        return unchecked((byte)longSum);
    }

    private static byte[] ObtainPinBlock(byte[] random, byte[] checksum, byte[] asciiPin)
    {
        var result = new byte[random.Length + checksum.Length + asciiPin.Length];
        Buffer.BlockCopy(random, 0, result, 0, random.Length);
        Buffer.BlockCopy(checksum, 0, result, random.Length, checksum.Length);
        Buffer.BlockCopy(asciiPin, 0, result, random.Length + checksum.Length, asciiPin.Length);

        return result;
    }

    private static string EncryptPIN(byte[] eanBlock, string decryptedWorkingKey)
    {
        var keyAsBytes = HexStringBytesConverter.ConvertHexStringToByteArray(decryptedWorkingKey);

        var byteResult = TripleDESEncryption.Encrypt(eanBlock, keyAsBytes);

        return BitConverter.ToString(byteResult).Replace("-", "");
    }
}
public static class TripleDESEncryption
{
    public static byte[] Encrypt(byte[] toEncrypt, byte[] key)
    {
        using (var tdes = new TripleDESCryptoServiceProvider
        {
            Key = key,
            IV = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
            Mode = CipherMode.CBC,
            Padding = PaddingMode.None
        })
        {
            var cTransform = tdes.CreateEncryptor();

            return cTransform.TransformFinalBlock(toEncrypt, 0, toEncrypt.Length);
        }
    }
}

我的示例输入和预期输出之一是

  • 未编码的 PIN:71548715
  • 解密的工作 key :A7E5A86DB6F41FBA0DE99DE5BC3246ABA7E5A86DB6F41FBA
  • 预期加密结果:C097280EC13B486AE5DA57DB8F779184
  • 以上得到的结果:C909165718FCE9A432AD432E7A104DCD

最佳答案

C/C++ 代码在 CBC 模式下使用 TripleDES 执行加密,无需填充。输入参数是十六进制编码 key ( argv[1] ) 和 EAN/PIN ( argv[2] )。 EAN/PIN 前面有一个加密前的 8 字节值,其前 7 字节是用 RAND_bytes() 随机生成的。其第 8 个字节是由 chksum() 生成的校验和字节。 。应用零 IV 作为 IV。

C# 代码的作用相同!当然,由于前 7 个字节是随机的,因此不能像您那样仅通过比较密文来验证这一点,而是通过使用中的前 7 个字节来比较密文来验证。两个代码。

可以通过使用发布的 key 和使用工具或其他代码的零 IV 解密发布的预期密文来预先确定此测试的前 7 个字节。此解密返回十六进制编码的值 51174b043d6274a63731353438373135 (例如使用 http://tripledes.online-domain-tools.com/ 执行),其中最后 8 个字节是 ASCII 解码的 71548715,因此对应于发布的 EAN/PIN。前 7 个字节是十六进制编码的 51174b043d6274。

如果是为了测试EncodePIN()中的C#代码线

var bytes = GenerateRandomBytes();

被替换为

var bytes = HexStringBytesConverter.ConvertHexStringToByteArray("51174b043d6274");

通话

Console.WriteLine(PINEncoding.EncodePIN("71548715", "A7E5A86DB6F41FBA0DE99DE5BC3246ABA7E5A86DB6F41FBA"));

返回

C097280EC13B486AE5DA57DB8F779184

与预期的密文一致,证明C/C++和C#代码在功能上是相同的。

请注意,C/C++ 代码实际上有更多的功能,例如检查 key (s. DES_set_key_checked ),但是如果 key 有效(奇奇偶校验,非弱或半弱),则对结果没有影响。

关于c# - 将 C openssl TripleDes 加密转换为 .NET,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69169991/

相关文章:

c++ - 为什么 C++ 初始分配比 C 大得多?

c - 如何让gcc跳过预处理?

c++ - Boost.Asio SSL 上下文未验证证书

openssl - 如何让 Visual Studio Code 信任我们的自签名代理证书?

c# - jQuery Ajax、MVC 和查询字符串

c# - 将鼠标悬停在 XAML wpf 上的按钮默认突出显示颜色更改为透明?

c - 是否可以绕过多核处理器中的 L1 缓存

c# - 排序列表是最好的解决方案吗?

c# - 从屏幕抓取中解析文本

java - SSL 连接重置