在哈希 SHA256 之后和调用签名 API 之前添加填充 RSA PKCS#1
Add padding RSA PKCS#1 after hash SHA256 and before calling API of signature
为了使用存储在远程 HSM 中的证书(使用云签名),我实际上是在尝试使用 API,它在给定 SHA256 哈希的情况下生成 PKCS#1.5 签名。问题是我需要实现的协议规范指出填充过程必须在 PKCS#1 中。
API支持的加密算法是这个OID:RSAES-PKCS1-v1_5加密方案
http://oid-info.com/get/1.2.840.113549.1.1.1
这里是协议的规范,规定填充过程必须是 PKCS#1 :
API 的开发人员提到,如果我能够在将哈希发送到 API 之前填充生成的哈希值,我将能够生成正确的签名。
这里是过程的解释:
但是我找不到任何方法在哈希生成之后和发送到 API 之前填充哈希。
...
string pathFileToBeHashed = "D:\Temp\virsct.SCT";
// Gets content of file
// Removes invalid characters before hash
string strToHash = File.ReadAllText(pathFileToBeHashed);
strToHash = strToHash.Replace(Constants.vbCr, "");
strToHash = strToHash.Replace(Constants.vbLf, "");
strToHash = strToHash.Replace(Strings.ChrW(26).ToString(), "");
// Hash SHA256
byte[] btToHash = Encoding.UTF8.GetBytes(strToHash);
string strHashed = string.Empty;
using var sha256 = new SHA256Managed()
{
strHashed = Convert.ToBase64String(sha256.ComputeHash(btToHash));
}
// Send to the API
...
当我在哈希签名后使用以下代码亲自检查签名时,结果是可以的。我用来检查签名的方法如上所述。
private static bool IsSignatureOK(string certB64, string signB64, string strCheminCompletFichier)
{
X509Certificate2 x509 = null;
try
{
x509 = new X509Certificate2(Convert.FromBase64String(certB64));
using RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)x509.PublicKey.Key;
byte[] btSign = Convert.FromBase64String(signB64);
byte[] btFichier = File.ReadAllBytes(strCheminCompletFichier);
byte[] btHashFichier = null;
using (MemoryStream ms = new MemoryStream(btFichier))
{
btHashFichier = CalculateHash(ms);
}
return rsa.VerifyHash(btHashFichier, CryptoConfig.MapNameToOID("SHA256"), btSign);
}
catch (Exception ex)
{
Interaction.MsgBox($"KO : {ex.Message}");
return false;
}
finally
{
if (x509 != null)
x509.Reset();
}
}
public static byte[] CalculateHash(MemoryStream memStream)
{
byte[] btHashWithContext = null;
int BLOC_LENGTH = 1000000;
memStream.Position = 0;
int Nb_Blocs = Convert.ToInt32(memStream.Length / BLOC_LENGTH) + (memStream.Length % BLOC_LENGTH == 0 ? 0 : 1);
using (SHA256Managed hashAlgorithm = new SHA256Managed())
{
byte[] MyBuffer = null;
for (int j = 0; j <= Nb_Blocs - 1; j++)
{
MyBuffer = new byte[BLOC_LENGTH];
int Nb_octets_lus = memStream.Read(MyBuffer, 0, BLOC_LENGTH);
if (Nb_octets_lus != BLOC_LENGTH)
Array.Resize(ref MyBuffer, Nb_octets_lus);
string restemp = Encoding.UTF8.GetString(MyBuffer);
restemp = restemp.Replace(Constants.vbCr, "");
restemp = restemp.Replace(Constants.vbLf, "");
restemp = restemp.Replace(Strings.ChrW(26).ToString(), "");
byte[] MyTempArray = Encoding.UTF8.GetBytes(restemp);
hashAlgorithm.TransformBlock(MyTempArray, 0, MyTempArray.Length, null, 0);
MyTempArray = null;
}
hashAlgorithm.TransformFinalBlock(new byte[0], 0, 0);
btHashWithContext = hashAlgorithm.Hash;
}
return btHashWithContext;
}
是否有一种方法允许在哈希生成后向其添加填充?
根据这个网站:
https://www.ibm.com/docs/en/linux-on-systems?topic=cryptography-pkcs-1-hash-formats
如果在hash值的开头加上一些额外的字节,就可以得到一个PKCS#1签名的结果
SHA256 哈希的十六进制字节如下:
SHA-256
X'3031300D 06096086 48016503 04020105 000420'
在 C# 中,我只是这样添加这些十六进制字节:
public void Main()
{
string hash = "3Z6TXNaRtoPP5kv5l4XBarx4GM+uH1M9rRK9iwRkKfY=";
byte[] btHash = Convert.FromBase64String(hash);
// Generates padding bytes to add at beginning of hash
string hexString = "3031300D060960864801650304020105000420";
byte[] btPadBytes = HexStringToByteArray(hexString);
// Combines both byte arrays
byte[] btCombined = ByteArrayCombine(bt, btHash);
string hashWithPadding = Convert.ToBase64String(btCombined);
}
public static byte[] HexStringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
public static byte[] ByteArrayCombine(byte[] first, byte[] second)
{
byte[] ret = new byte[first.Length + second.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
return ret;
}
为了使用存储在远程 HSM 中的证书(使用云签名),我实际上是在尝试使用 API,它在给定 SHA256 哈希的情况下生成 PKCS#1.5 签名。问题是我需要实现的协议规范指出填充过程必须在 PKCS#1 中。
API支持的加密算法是这个OID:RSAES-PKCS1-v1_5加密方案
http://oid-info.com/get/1.2.840.113549.1.1.1
这里是协议的规范,规定填充过程必须是 PKCS#1 :
API 的开发人员提到,如果我能够在将哈希发送到 API 之前填充生成的哈希值,我将能够生成正确的签名。
这里是过程的解释:
但是我找不到任何方法在哈希生成之后和发送到 API 之前填充哈希。
...
string pathFileToBeHashed = "D:\Temp\virsct.SCT";
// Gets content of file
// Removes invalid characters before hash
string strToHash = File.ReadAllText(pathFileToBeHashed);
strToHash = strToHash.Replace(Constants.vbCr, "");
strToHash = strToHash.Replace(Constants.vbLf, "");
strToHash = strToHash.Replace(Strings.ChrW(26).ToString(), "");
// Hash SHA256
byte[] btToHash = Encoding.UTF8.GetBytes(strToHash);
string strHashed = string.Empty;
using var sha256 = new SHA256Managed()
{
strHashed = Convert.ToBase64String(sha256.ComputeHash(btToHash));
}
// Send to the API
...
当我在哈希签名后使用以下代码亲自检查签名时,结果是可以的。我用来检查签名的方法如上所述。
private static bool IsSignatureOK(string certB64, string signB64, string strCheminCompletFichier)
{
X509Certificate2 x509 = null;
try
{
x509 = new X509Certificate2(Convert.FromBase64String(certB64));
using RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)x509.PublicKey.Key;
byte[] btSign = Convert.FromBase64String(signB64);
byte[] btFichier = File.ReadAllBytes(strCheminCompletFichier);
byte[] btHashFichier = null;
using (MemoryStream ms = new MemoryStream(btFichier))
{
btHashFichier = CalculateHash(ms);
}
return rsa.VerifyHash(btHashFichier, CryptoConfig.MapNameToOID("SHA256"), btSign);
}
catch (Exception ex)
{
Interaction.MsgBox($"KO : {ex.Message}");
return false;
}
finally
{
if (x509 != null)
x509.Reset();
}
}
public static byte[] CalculateHash(MemoryStream memStream)
{
byte[] btHashWithContext = null;
int BLOC_LENGTH = 1000000;
memStream.Position = 0;
int Nb_Blocs = Convert.ToInt32(memStream.Length / BLOC_LENGTH) + (memStream.Length % BLOC_LENGTH == 0 ? 0 : 1);
using (SHA256Managed hashAlgorithm = new SHA256Managed())
{
byte[] MyBuffer = null;
for (int j = 0; j <= Nb_Blocs - 1; j++)
{
MyBuffer = new byte[BLOC_LENGTH];
int Nb_octets_lus = memStream.Read(MyBuffer, 0, BLOC_LENGTH);
if (Nb_octets_lus != BLOC_LENGTH)
Array.Resize(ref MyBuffer, Nb_octets_lus);
string restemp = Encoding.UTF8.GetString(MyBuffer);
restemp = restemp.Replace(Constants.vbCr, "");
restemp = restemp.Replace(Constants.vbLf, "");
restemp = restemp.Replace(Strings.ChrW(26).ToString(), "");
byte[] MyTempArray = Encoding.UTF8.GetBytes(restemp);
hashAlgorithm.TransformBlock(MyTempArray, 0, MyTempArray.Length, null, 0);
MyTempArray = null;
}
hashAlgorithm.TransformFinalBlock(new byte[0], 0, 0);
btHashWithContext = hashAlgorithm.Hash;
}
return btHashWithContext;
}
是否有一种方法允许在哈希生成后向其添加填充?
根据这个网站:
https://www.ibm.com/docs/en/linux-on-systems?topic=cryptography-pkcs-1-hash-formats
如果在hash值的开头加上一些额外的字节,就可以得到一个PKCS#1签名的结果
SHA256 哈希的十六进制字节如下:
SHA-256
X'3031300D 06096086 48016503 04020105 000420'
在 C# 中,我只是这样添加这些十六进制字节:
public void Main()
{
string hash = "3Z6TXNaRtoPP5kv5l4XBarx4GM+uH1M9rRK9iwRkKfY=";
byte[] btHash = Convert.FromBase64String(hash);
// Generates padding bytes to add at beginning of hash
string hexString = "3031300D060960864801650304020105000420";
byte[] btPadBytes = HexStringToByteArray(hexString);
// Combines both byte arrays
byte[] btCombined = ByteArrayCombine(bt, btHash);
string hashWithPadding = Convert.ToBase64String(btCombined);
}
public static byte[] HexStringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
public static byte[] ByteArrayCombine(byte[] first, byte[] second)
{
byte[] ret = new byte[first.Length + second.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
return ret;
}