Python 与 C# 脚本输出相同的 base64 哈希
Python base64 hash with same output as C# Script
我正在与拥有 API 的供应商合作。为了调用 API,他们要求我们对请求的整个 body 进行哈希处理,并将其添加到 Content-Digest 摘要 header 键中。 Content-Digest: SHA256=<digest>
。他们为我们提供了一个 RSA 私钥以及一个用 C# 编写的 linq LINQPad 文件。此脚本输出进入 Content-Digest.
的 base64 编码哈希
问题是,我不知道任何 C#,我们将要使用此 API 的应用程序是用 Python 编写的。我正在寻找的是一种在 Python 脚本中输出完全相同格式的散列的方法。
这是他们提供的 C# 代码:
void Main()
{
var payload = GetPayload();
SignData(payload);
}
private void SignData(string payload)
{
var keyFormatted = GetRSAKey();
byte[] privateKeyInDER = Convert.FromBase64String(keyFormatted);
var rsa = DecodeRSAPrivateKey(privateKeyInDER);
var data = Encoding.Default.GetBytes(payload);
using var hasher = new SHA512Managed();
var signBytes = rsa.SignData(data, hasher);
var computedSignature = Convert.ToBase64String(signBytes);
computedSignature.Dump();
}
private static int GetIntegerSize(BinaryReader binary)
{
var bt = binary.ReadByte();
if (bt != 0x02)
{
return 0;
}
bt = binary.ReadByte();
int count;
if (bt == 0x81)
{
count = binary.ReadByte();
}
else if (bt == 0x82)
{
var highbyte = binary.ReadByte();
var lowbyte = binary.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = BitConverter.ToInt32(modint, 0);
}
else
{
count = bt;
}
while (binary.ReadByte() == 0x00)
{
count--;
}
binary.BaseStream.Seek(-1, SeekOrigin.Current);
return count;
}
public static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
{
byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
// --------- Set up stream to decode the asn.1 encoded RSA private key ------
MemoryStream mem = new MemoryStream(privkey);
BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading
try
{
var twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
{
binr.ReadByte();//advance 1 byte
}
else if (twobytes == 0x8230)
{
binr.ReadInt16();
} //advance 2 bytes
else
{
return null;
}
twobytes = binr.ReadUInt16();
if (twobytes != 0x0102) //version number
{
return null;
}
var bt = binr.ReadByte();
if (bt != 0x00)
{
return null;
}
var elems = GetIntegerSize(binr);
MODULUS = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
E = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
D = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
P = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
Q = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DP = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DQ = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
IQ = binr.ReadBytes(elems);
// ------- create RSACryptoServiceProvider instance and initialize with private key -----
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAparams = new RSAParameters
{
Modulus = MODULUS,
Exponent = E,
D = D,
P = P,
Q = Q,
DP = DP,
DQ = DQ,
InverseQ = IQ
};
RSA.ImportParameters(RSAparams);
return RSA;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
finally
{
binr.Close();
}
}
private string GetRSAKey() {
return "private key";
}
private string GetPayload()
{
return @"{
""key"":""value"",
""key2"":{
""subkey"": true,}
}";
}
它输出如下内容(344 个字符):
15vgzyv8Cke3Mkkwc3ryAgDMmY6olRfvgLqNyfhfti2GAfLb6s/vgrWc1p5jlWgQHh37Ir7UYThXldspriBz5NPl+BSFIW2dxXTMO2NMpzgc/5fmFN2maJCgwzDP0aqupmUGrw/DZp8zMAKtxWqs+8TGQTDthAW+4Y8g0hoLYSTEIHwvbkBUCspWo4Qr0MXj86P1Gsu5DbQ4Fs23fbajPuZqRHTyYzeANvxnma9mm30CwLD6blnKOLa+xRVd6eeuHu+Hp+F8hl5xSJS0Bcse4K0ZKccDD6sm4KSX2vaNQeQQ45fIDYLRUXYckGifqu7nJLwHILEenxue10841IHleA==
我一直试图在 Python 中解决这个问题,这与我得到的最接近:
import hashlib
import base64
import hmac
key = '''
private key
'''
msg = '''
{"body to hash"}
'''
print((base64.b64encode(hmac.new(bytearray(key.upper(), "ASCII") , bytearray(msg,"ASCII") , hashlib.sha512).digest())).decode("ASCII"))
但是,这个 returns 哈希看起来像这样:
9dfgSdEzLEdGHze/SrYCSGVHurEvFabe3YgBSqKowxHb96UznenFFoeTDjx2dlk2B53qq9ISKVwv+xFBXMBePQ==
如有哪位大侠能提供帮助,将不胜感激!
我希望这能让你走上正确的方向。
请注意,我正在生成一个随机私钥,只是为了有一个工作示例。这会生成 344 个字符的输出,正如您所期望的那样:
b'Q9X/TOUJwJI101e5pXSg75zhNXk0VKA+cbbFJLF1OttmVIT3Pfa6xcpSvjE3ErW6SBKFlK+e/3AxNRr6h1TXhQQEtbMl9GmcBgJnvKOOWN8Ev40NvO+Ut7MEiHXDWZ888AXYe4sNMc61oUlj1d7wop0mZIL/+hMTQi9zVldfxWB/5PLLe/J3T451Ldj3XH5lL2AnesoCDgQTwWS20iCX8SE5JGh0pAJj+rImgyPinqvbf49uBq1DByKrAI5SVtB/6IoWpztyKKOfjy7QtcM71/CIWBrTAUi7TBXUlJ2si9s8alm+NUNKZZkWNS5SIkcZQrWPz+no6J9CGJt+JAXTRw=='
比较结果时请小心输入。在您的示例中,.NET 应用程序在有效负载末尾不包含新行 - 但是 python 应用程序包含。
from Crypto.Cipher import DES
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA512, SHA384, SHA256, SHA, MD5
from Crypto import Random
from base64 import b64encode, b64decode
def sign(message, priv_key):
signer = PKCS1_v1_5.new(priv_key)
digest = SHA512.new()
digest.update(message)
return signer.sign(digest)
# pem prefix and suffix in case it's not provided in your key
#pem_prefix = '-----BEGIN RSA PRIVATE KEY-----\n'
#pem_suffix = '\n-----END RSA PRIVATE KEY-----'
# here should be your key (without pem prefix and suffix)
#key = "thisisyourkey"
# assemble
#key = '{}{}{}'.format(pem_prefix, key, pem_suffix)
# generate private key rsa object
#private = RSA.importKey(key)
# the following 3 lines are just to showcase a working key
random_generator = Random.new().read
keysize = 2048
private = RSA.generate(keysize, random_generator)
# payload to sign
msg = '''
{"body to hash"}
'''.encode("utf-8")
signature = sign(msg, private)
print(b64encode(signature))
我正在与拥有 API 的供应商合作。为了调用 API,他们要求我们对请求的整个 body 进行哈希处理,并将其添加到 Content-Digest 摘要 header 键中。 Content-Digest: SHA256=<digest>
。他们为我们提供了一个 RSA 私钥以及一个用 C# 编写的 linq LINQPad 文件。此脚本输出进入 Content-Digest.
问题是,我不知道任何 C#,我们将要使用此 API 的应用程序是用 Python 编写的。我正在寻找的是一种在 Python 脚本中输出完全相同格式的散列的方法。
这是他们提供的 C# 代码:
void Main()
{
var payload = GetPayload();
SignData(payload);
}
private void SignData(string payload)
{
var keyFormatted = GetRSAKey();
byte[] privateKeyInDER = Convert.FromBase64String(keyFormatted);
var rsa = DecodeRSAPrivateKey(privateKeyInDER);
var data = Encoding.Default.GetBytes(payload);
using var hasher = new SHA512Managed();
var signBytes = rsa.SignData(data, hasher);
var computedSignature = Convert.ToBase64String(signBytes);
computedSignature.Dump();
}
private static int GetIntegerSize(BinaryReader binary)
{
var bt = binary.ReadByte();
if (bt != 0x02)
{
return 0;
}
bt = binary.ReadByte();
int count;
if (bt == 0x81)
{
count = binary.ReadByte();
}
else if (bt == 0x82)
{
var highbyte = binary.ReadByte();
var lowbyte = binary.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = BitConverter.ToInt32(modint, 0);
}
else
{
count = bt;
}
while (binary.ReadByte() == 0x00)
{
count--;
}
binary.BaseStream.Seek(-1, SeekOrigin.Current);
return count;
}
public static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
{
byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
// --------- Set up stream to decode the asn.1 encoded RSA private key ------
MemoryStream mem = new MemoryStream(privkey);
BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading
try
{
var twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
{
binr.ReadByte();//advance 1 byte
}
else if (twobytes == 0x8230)
{
binr.ReadInt16();
} //advance 2 bytes
else
{
return null;
}
twobytes = binr.ReadUInt16();
if (twobytes != 0x0102) //version number
{
return null;
}
var bt = binr.ReadByte();
if (bt != 0x00)
{
return null;
}
var elems = GetIntegerSize(binr);
MODULUS = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
E = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
D = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
P = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
Q = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DP = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DQ = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
IQ = binr.ReadBytes(elems);
// ------- create RSACryptoServiceProvider instance and initialize with private key -----
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAparams = new RSAParameters
{
Modulus = MODULUS,
Exponent = E,
D = D,
P = P,
Q = Q,
DP = DP,
DQ = DQ,
InverseQ = IQ
};
RSA.ImportParameters(RSAparams);
return RSA;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
finally
{
binr.Close();
}
}
private string GetRSAKey() {
return "private key";
}
private string GetPayload()
{
return @"{
""key"":""value"",
""key2"":{
""subkey"": true,}
}";
}
它输出如下内容(344 个字符):
15vgzyv8Cke3Mkkwc3ryAgDMmY6olRfvgLqNyfhfti2GAfLb6s/vgrWc1p5jlWgQHh37Ir7UYThXldspriBz5NPl+BSFIW2dxXTMO2NMpzgc/5fmFN2maJCgwzDP0aqupmUGrw/DZp8zMAKtxWqs+8TGQTDthAW+4Y8g0hoLYSTEIHwvbkBUCspWo4Qr0MXj86P1Gsu5DbQ4Fs23fbajPuZqRHTyYzeANvxnma9mm30CwLD6blnKOLa+xRVd6eeuHu+Hp+F8hl5xSJS0Bcse4K0ZKccDD6sm4KSX2vaNQeQQ45fIDYLRUXYckGifqu7nJLwHILEenxue10841IHleA==
我一直试图在 Python 中解决这个问题,这与我得到的最接近:
import hashlib
import base64
import hmac
key = '''
private key
'''
msg = '''
{"body to hash"}
'''
print((base64.b64encode(hmac.new(bytearray(key.upper(), "ASCII") , bytearray(msg,"ASCII") , hashlib.sha512).digest())).decode("ASCII"))
但是,这个 returns 哈希看起来像这样:
9dfgSdEzLEdGHze/SrYCSGVHurEvFabe3YgBSqKowxHb96UznenFFoeTDjx2dlk2B53qq9ISKVwv+xFBXMBePQ==
如有哪位大侠能提供帮助,将不胜感激!
我希望这能让你走上正确的方向。
请注意,我正在生成一个随机私钥,只是为了有一个工作示例。这会生成 344 个字符的输出,正如您所期望的那样:
b'Q9X/TOUJwJI101e5pXSg75zhNXk0VKA+cbbFJLF1OttmVIT3Pfa6xcpSvjE3ErW6SBKFlK+e/3AxNRr6h1TXhQQEtbMl9GmcBgJnvKOOWN8Ev40NvO+Ut7MEiHXDWZ888AXYe4sNMc61oUlj1d7wop0mZIL/+hMTQi9zVldfxWB/5PLLe/J3T451Ldj3XH5lL2AnesoCDgQTwWS20iCX8SE5JGh0pAJj+rImgyPinqvbf49uBq1DByKrAI5SVtB/6IoWpztyKKOfjy7QtcM71/CIWBrTAUi7TBXUlJ2si9s8alm+NUNKZZkWNS5SIkcZQrWPz+no6J9CGJt+JAXTRw=='
比较结果时请小心输入。在您的示例中,.NET 应用程序在有效负载末尾不包含新行 - 但是 python 应用程序包含。
from Crypto.Cipher import DES
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA512, SHA384, SHA256, SHA, MD5
from Crypto import Random
from base64 import b64encode, b64decode
def sign(message, priv_key):
signer = PKCS1_v1_5.new(priv_key)
digest = SHA512.new()
digest.update(message)
return signer.sign(digest)
# pem prefix and suffix in case it's not provided in your key
#pem_prefix = '-----BEGIN RSA PRIVATE KEY-----\n'
#pem_suffix = '\n-----END RSA PRIVATE KEY-----'
# here should be your key (without pem prefix and suffix)
#key = "thisisyourkey"
# assemble
#key = '{}{}{}'.format(pem_prefix, key, pem_suffix)
# generate private key rsa object
#private = RSA.importKey(key)
# the following 3 lines are just to showcase a working key
random_generator = Random.new().read
keysize = 2048
private = RSA.generate(keysize, random_generator)
# payload to sign
msg = '''
{"body to hash"}
'''.encode("utf-8")
signature = sign(msg, private)
print(b64encode(signature))