如何在.NET 9与BouncyCastle 2.6.0中实现签名预哈希(PreHash)
实现BouncyCastle推荐的RSA签名预哈希方案(适配.NET 9 + BouncyCastle 2.6.0)
原OpenSSL签名命令
openssl rsautl -sign -in rasi.bin -inkey riktest.key -out allkiri.bin
原基于BouncyCastle的C#实现
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.OpenSsl; using Org.BouncyCastle.Security; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Encodings; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Signers; using System; using System.Text; using System.IO; using System.Security.Cryptography; public class Program { public static void Main() { // 测试用512位密钥,实际生产必须使用>=2048位密钥保障安全! string privatePkcs1Pem = @"-----BEGIN RSA PRIVATE KEY----- MIIBOwIBAAJBANoHbFSEZoOSB9Kxt7t8PoBwmauaODjECHqJgtTU3h4MW5K3857+ 04Flc6x6a9xxyvCKS5RtOP2gaOlOVtrph0ECAwEAAQJBALu8LpRr2RWrdV7/tfQT HIJd8oQnbAe9DIvuwh/fF08IwApOE/iGL+Ded49eoHHu1OXycZhpHavN/sQMnssP FNECIQDyDIW7V5UUu16ZAeupeQ7zdV6ykVngd0bb3FEn99EchQIhAOaYe3ll211q SIXVjKHudMn3xe6Vvguc9O7cwCB+gyqNAiEAsr3kk6/de23SMZNlf8TR8Z8eyybj BAuQ3BMaKzWpyjECIFMR0UFNYTYIyLF12aCoH2h2mtY1GW5jj5TQ72GFUcktAiAf WWXnts7m8kZWuKjfD0MQiW+w4iAph+51j+wiL3EMAQ== -----END RSA PRIVATE KEY-----"; byte[] dataToSign = SHA256.HashData(Encoding.UTF8.GetBytes("Data to sign")); // 导入PKCS#1格式PEM私钥 PemReader pemReader = new PemReader(new StringReader(privatePkcs1Pem)); AsymmetricKeyParameter privateKeyParameter = ((AsymmetricCipherKeyPair)pemReader.ReadObject()).Private; // 签名原始数据 ISigner signer = new GenericSigner(new Pkcs1Encoding(new RsaBlindedEngine()), new NullDigest()); signer.Init(true, privateKeyParameter); signer.BlockUpdate(dataToSign, 0, dataToSign.Length); byte[] signature = signer.GenerateSignature(); Console.WriteLine(Convert.ToHexString(signature)); } }
BouncyCastle开发者的预哈希方案建议
将
NullDigest替换为PreHash.For(new Sha256Digest())会更实用。当然,也可以直接把哈希操作整合到签名流程里——只需要把
NullDigest替换成Sha256Digest即可。
适配.NET 9 + BouncyCastle 2.6.0的实现方案
方案一:直接整合哈希与签名流程
当前版本不支持PreHash.For(),但开发者提到的第二种方案可直接使用:将NullDigest替换为Sha256Digest,签名器会自动完成数据哈希→PKCS#1填充→RSA签名的完整流程,无需提前手动计算哈希:
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.OpenSsl; using Org.BouncyCastle.Security; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Encodings; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Signers; using System; using System.Text; using System.IO; public class Program { public static void Main() { // 测试用512位密钥,实际生产必须使用>=2048位密钥保障安全! string privatePkcs1Pem = @"-----BEGIN RSA PRIVATE KEY----- MIIBOwIBAAJBANoHbFSEZoOSB9Kxt7t8PoBwmauaODjECHqJgtTU3h4MW5K3857+ 04Flc6x6a9xxyvCKS5RtOP2gaOlOVtrph0ECAwEAAQJBALu8LpRr2RWrdV7/tfQT HIJd8oQnbAe9DIvuwh/fF08IwApOE/iGL+Ded49eoHHu1OXycZhpHavN/sQMnssP FNECIQDyDIW7V5UUu16ZAeupeQ7zdV6ykVngd0bb3FEn99EchQIhAOaYe3ll211q SIXVjKHudMn3xe6Vvguc9O7cwCB+gyqNAiEAsr3kk6/de23SMZNlf8TR8Z8eyybj BAuQ3BMaKzWpyjECIFMR0UFNYTYIyLF12aCoH2h2mtY1GW5jj5TQ72GFUcktAiAf WWXnts7m8kZWuKjfD0MQiW+w4iAph+51j+wiL3EMAQ== -----END RSA PRIVATE KEY-----"; // 直接传入原始数据,无需提前计算哈希 byte[] rawData = Encoding.UTF8.GetBytes("Data to sign"); // 导入PKCS#1格式PEM私钥 PemReader pemReader = new PemReader(new StringReader(privatePkcs1Pem)); AsymmetricKeyParameter privateKeyParameter = ((AsymmetricCipherKeyPair)pemReader.ReadObject()).Private; // 使用Sha256Digest替代NullDigest,签名器自动完成哈希+签名 ISigner signer = new GenericSigner(new Pkcs1Encoding(new RsaBlindedEngine()), new Sha256Digest()); signer.Init(true, privateKeyParameter); signer.BlockUpdate(rawData, 0, rawData.Length); byte[] signature = signer.GenerateSignature(); Console.WriteLine(Convert.ToHexString(signature)); } }
方案二:模拟PreHash预哈希逻辑(针对已提前计算哈希的场景)
如果必须提前计算哈希值,再进行签名,可以手动构造符合PKCS#1 v1.5标准的哈希摘要ASN.1结构,再传入签名器完成填充与签名,以此模拟PreHash.For()的效果:
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.OpenSsl; using Org.BouncyCastle.Security; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Encodings; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Signers; using System; using System.Text; using System.IO; using System.Security.Cryptography; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Asn1.X509; public class Program { public static void Main() { // 测试用512位密钥,实际生产必须使用>=2048位密钥保障安全! string privatePkcs1Pem = @"-----BEGIN RSA PRIVATE KEY----- MIIBOwIBAAJBANoHbFSEZoOSB9Kxt7t8PoBwmauaODjECHqJgtTU3h4MW5K3857+ 04Flc6x6a9xxyvCKS5RtOP2gaOlOVtrph0ECAwEAAQJBALu8LpRr2RWrdV7/tfQT HIJd8oQnbAe9DIvuwh/fF08IwApOE/iGL+Ded49eoHHu1OXycZhpHavN/sQMnssP FNECIQDyDIW7V5UUu16ZAeupeQ7zdV6ykVngd0bb3FEn99EchQIhAOaYe3ll211q SIXVjKHudMn3xe6Vvguc9O7cwCB+gyqNAiEAsr3kk6/de23SMZNlf8TR8Z8eyybj BAuQ3BMaKzWpyjECIFMR0UFNYTYIyLF12aCoH2h2mtY1GW5jj5TQ72GFUcktAiAf WWXnts7m8kZWuKjfD0MQiW+w4iAph+51j+wiL3EMAQ== -----END RSA PRIVATE KEY-----"; // 提前计算SHA256哈希 byte[] dataHash = SHA256.HashData(Encoding.UTF8.GetBytes("Data to sign")); // 导入PKCS#1格式PEM私钥 PemReader pemReader = new PemReader(new StringReader(privatePkcs1Pem)); AsymmetricKeyParameter privateKeyParameter = ((AsymmetricCipherKeyPair)pemReader.ReadObject()).Private; // 构造PKCS#1 v1.5要求的哈希摘要ASN.1结构 AlgorithmIdentifier sha256AlgId = new AlgorithmIdentifier(X509ObjectIdentifiers.IdSha256); DigestInfo digestInfo = new DigestInfo(sha256AlgId, dataHash); byte[] encodedDigest = digestInfo.GetDerEncoded(); // 使用NullDigest签名编码后的摘要结构,模拟PreHash的效果 ISigner signer = new GenericSigner(new Pkcs1Encoding(new RsaBlindedEngine()), new NullDigest()); signer.Init(true, privateKeyParameter); signer.BlockUpdate(encodedDigest, 0, encodedDigest.Length); byte[] signature = signer.GenerateSignature(); Console.WriteLine(Convert.ToHexString(signature)); } }
内容的提问来源于stack exchange,提问作者Andrus
相关产品推荐
相关产品推荐

