将 C openssl TripleDes 加密转换为 .NET
Converting C openssl TripleDes encryption to .NET
我一直在尝试为第 3 方集成复制加密过程。第 3 方使用 openssl,并给了我他们用来执行该过程的 C 代码。数周以来,我一直在尝试将此过程 across 移植到 C#,但我似乎遗漏了一些我无法解决的问题。 Most 可能我在 transposing C 代码和 OpenSSL 库中遗漏了一些东西,但我一辈子都弄不明白
主端口os OpenSSL to .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] = '[=10=]';
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 以便于传输osing)
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
- 解密工作密钥:A7E5A86DB6F41FBA0DE99DE5BC3246ABA7E5A86DB6F41FBA
- 预期加密结果:C097280EC13B486AE5DA57DB8F779184
- 以上得到的结果:C909165718FCE9A432AD432E7A104DCD
C/C++ 代码在 CBC 模式下使用 TripleDES 进行加密,没有填充。输入参数是十六进制编码的密钥 (argv[1]
) 和 EAN/PIN (argv[2]
)。 EAN/PIN前面是一个8字节的加密前值,其前7个字节是用RAND_bytes()
随机生成的,第8个字节是用chksum()
生成的校验和字节。 IV 为零。
C# 代码也是如此!当然,因为前random 7个字节,不能像你那样仅仅通过比较密文来验证,而是通过identical[=43]来比较密文=] 两个代码前导 7 个字节。
此测试的前 7 个字节可以通过使用工具或其他代码使用发布的密钥和零 IV 解密发布的 expected 密文来预先确定。此解密 returns 十六进制编码值 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"));
returns
C097280EC13B486AE5DA57DB8F779184
根据预期的密文,证明C/C++和C#代码在功能上完全相同。
请注意,C/C++ 代码实际上在幕后具有更多功能,例如检查密钥 (s. DES_set_key_checked
),但是如果密钥有效(奇校验,不是弱或半弱),这对结果没有影响。
我一直在尝试为第 3 方集成复制加密过程。第 3 方使用 openssl,并给了我他们用来执行该过程的 C 代码。数周以来,我一直在尝试将此过程 across 移植到 C#,但我似乎遗漏了一些我无法解决的问题。 Most 可能我在 transposing C 代码和 OpenSSL 库中遗漏了一些东西,但我一辈子都弄不明白 主端口os OpenSSL to .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] = '[=10=]';
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 以便于传输osing)
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
- 解密工作密钥:A7E5A86DB6F41FBA0DE99DE5BC3246ABA7E5A86DB6F41FBA
- 预期加密结果:C097280EC13B486AE5DA57DB8F779184
- 以上得到的结果:C909165718FCE9A432AD432E7A104DCD
C/C++ 代码在 CBC 模式下使用 TripleDES 进行加密,没有填充。输入参数是十六进制编码的密钥 (argv[1]
) 和 EAN/PIN (argv[2]
)。 EAN/PIN前面是一个8字节的加密前值,其前7个字节是用RAND_bytes()
随机生成的,第8个字节是用chksum()
生成的校验和字节。 IV 为零。
C# 代码也是如此!当然,因为前random 7个字节,不能像你那样仅仅通过比较密文来验证,而是通过identical[=43]来比较密文=] 两个代码前导 7 个字节。
此测试的前 7 个字节可以通过使用工具或其他代码使用发布的密钥和零 IV 解密发布的 expected 密文来预先确定。此解密 returns 十六进制编码值 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"));
returns
C097280EC13B486AE5DA57DB8F779184
根据预期的密文,证明C/C++和C#代码在功能上完全相同。
请注意,C/C++ 代码实际上在幕后具有更多功能,例如检查密钥 (s. DES_set_key_checked
),但是如果密钥有效(奇校验,不是弱或半弱),这对结果没有影响。