我有问题。
<span>
.Net Framework 4.7和.Net Core 2.1中的这段代码会产生不同的结果...
我的消息长度是73个字节
.Net Framework中的结果-1520字节
.Net Core的结果-1523字节
为什么?
答案 0 :(得分:4)
在.NET Framework上,如果您的.exe目标是4.7或更早版本,则new CmsSigner(cert)
指示签名者应将SHA-1作为摘要,在4.7.1及更高版本中,默认算法更改为SHA-2 -256。
在.NET Core 2.1上,默认值已为SHA-2-256。
您可以通过设置DigestAlgorithm
的{{1}}属性,将它们中的任何一种更改为所需的摘要算法。例如CmsSigner
现在使它们两个都使用SHA-2-256。
此外,.NET Framework将RSA签名算法标识符编码为cmsSigner.DigestAlgorithm = new Oid("2.16.840.1.101.3.4.2.1");
,. NET Core将其编码为{ id-rsaEncryption, NULL }
(省略了可选的第二个值)。这部分内容不会改变内容的语义解释,但意味着在两者上使用相同的摘要算法时,它们的长度仍不会相同。
SHA-2-256算法标识符的DER编码比SHA-1标识符的DER编码长两个字节(假定SHA-2-256的参数值被省略,而SHA-1的参数值被省略)一片空白)。它被记下两次(+4到核心),然后RSA签名算法标识符表示方式的差异使.NET Core版本缩小了2个字节(净值:+2到核心)
看到三个字节而不是两个字节的区别的原因是,文档结构的某些部分最终超过了需要长度前缀(可变长度)的阈值之一获得一个额外的字节来保存该值。这些点是除127-> 128之外的标准“字节边界”(255-> 256、65535-> 65536等)。您的SignerInfo块在.NET Framework文档中可能为254或255字节,并且跨越到256或257,因为SHA-2-256的标识符值更长。
答案 1 :(得分:0)
我使用方法“ ComputeSignature”中的BouncyCastle.NetCore解决了我的问题(在带有.Net Core 2.0的Debian 9中工作)。 结果长度为2537字节,但是外部应用程序可以使用此签名正常工作。
public static byte[] ComputeSignature(byte[] message, string PrivateKeyPath, string password, string CertPath)
{
try
{
// Load end certificate and signing key
var signerCert = ReadCertFromFile(PrivateKeyPath, password, out AsymmetricKeyParameter key);
// Read CA cert
var caCert = ReadCertFromFile(CertPath);
var certChain = new X509Certificate[] { caCert };
var result = SignWithSystem(
message,
key,
signerCert,
certChain);
return result;
}
catch (Exception ex)
{
Console.WriteLine("Failed : " + ex.ToString());
return null;
}
}
protected static byte[] SignWithSystem(byte[] data, AsymmetricKeyParameter key, X509Certificate cert, X509Certificate[] chain)
{
var generator = new CmsSignedDataGenerator();
// Add signing key
generator.AddSigner(
key,
cert,
"2.16.840.1.101.3.4.2.1"); // SHA256 digest ID
var storeCerts = new List<X509Certificate>
{
cert // NOTE: Adding end certificate too
};
storeCerts.AddRange(chain); // I'm assuming the chain collection doesn't contain the end certificate already
// Construct a store from the collection of certificates and add to generator
var storeParams = new X509CollectionStoreParameters(storeCerts);
var certStore = X509StoreFactory.Create("CERTIFICATE/COLLECTION", storeParams);
generator.AddCertificates(certStore);
// Generate the signature
var signedData = generator.Generate(
new CmsProcessableByteArray(data),
false); // encapsulate = false for detached signature
return signedData.GetEncoded();
}
public static X509Certificate ReadCertFromFile(string strCertificatePath)
{
// Create file stream object to read certificate
using (var keyStream = new FileStream(strCertificatePath, FileMode.Open, FileAccess.Read))
{
var parser = new X509CertificateParser();
return parser.ReadCertificate(keyStream);
}
}
// This reads a certificate from a file.
// Thanks to: http://blog.softwarecodehelp.com/2009/06/23/CodeForRetrievePublicKeyFromCertificateAndEncryptUsingCertificatePublicKeyForBothJavaC.aspx
public static X509Certificate ReadCertFromFile(string strCertificatePath, string strCertificatePassword, out AsymmetricKeyParameter key)
{
key = null;
// Create file stream object to read certificate
using (var keyStream = new FileStream(strCertificatePath, FileMode.Open, FileAccess.Read))
{
// Read certificate using BouncyCastle component
var inputKeyStore = new Pkcs12Store();
inputKeyStore.Load(keyStream, strCertificatePassword.ToCharArray());
var keyAlias = inputKeyStore.Aliases.Cast<string>().FirstOrDefault(n => inputKeyStore.IsKeyEntry(n));
// Read Key from Aliases
if (keyAlias == null)
throw new NotImplementedException("Alias");
key = inputKeyStore.GetKey(keyAlias).Key;
//Read certificate into 509 format
return (X509Certificate)inputKeyStore.GetCertificate(keyAlias).Certificate;
}
}