正在将Openssl签名转换为.NET6



应用程序调用openssl使用进行签名

openssl rsautl -sign -in rasi.bin -inkey riktest.key -out allkiri.bin

如何将其转换为。NET 6,这样就不需要调用openssl了?

riktest.key是包含的文本文件

-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAfddAQEArYjDH7msMFifeYc1AG/TkKpcz2LITI73sC0eqnlgmWi3F7PD
Bo8lWrCw32h3v/FFMrK8KuktlnBtsSLaCCz1DWuXORzHaW7EqG8O8QNzFSmhIoqp
...

这是ASP。NET 6 MVC应用程序。做NET 6系统。安全加密命名空间是否包含此OpenSsl功能?

为什么一般是原生的。NET方法不能使用

对于RSASignaturePadding.Pkcs1,本机。NET实现SignData()SignHash()遵循RFC8017中描述的RSASSA-PKCS1-v1_5签名方案,该方案应用EMSA-PKCS1-v1_5作为编码操作:对消息进行哈希处理,并对以下值进行签名(即使用私钥加密(:

EM = 0x00 || 0x01 || PS || 0x00 || T

这里,PS由许多0xff值组成,使得EM的大小等于密钥的模数的大小。T是DigestInfo值的DER编码,该值包含摘要OID和哈希,例如,对于SHA256:

3031300d060960864801650304020105000420 || H 

其中H是要签名的消息M的32字节SHA56散列。

相反,openssl rsoutl直接使用RSA算法,如NOTES部分所述,即对以下数据进行签名:

EM' = 0x00 || 0x01 || PS || 0x00 || M

这是本机无法实现的。NET方法(特殊用例除外,请参阅下文(:SignData()散列,因此失败,SignHash()不散列,但在内部(与SignData()一样(生成DigestInfo值的DER编码。

另一种选择是BouncyCastle,它使用算法NoneWithRSA进行签名,就像openssl rsoutl一样。

该算法的一个缺点是,由于缺少哈希,只能对短消息进行签名,因为对于较长的消息无法满足长度标准(根据该标准,EM’的大小必须与密钥的模数大小相对应(。

密钥导入

发布的密钥是PKCS#1格式的PEM编码的私钥。

NET支持使用ImportFromPem()导入PEM编码的密钥(private/public,PKCS#8/PKCS#1格式(。NET 5,但此后一直支持导入DER编码的密钥。NET Core 3.0。PKCS#1格式的私有DER编码密钥可以用ImportRSAPrivateKey()导入(PEM和DER编码之间的转换很简单,包括删除页眉、页脚和换行符以及对剩余正文进行Base64解码(。

BouncyCastle支持使用PemReader类导入PEM编码的密钥。

BouncyCastle发布的OpenSSL功能的可能实现

rasi.bin保存来自dataToSign的数据并且riktest.key保留来自privatePkcs1Pem的密钥时,以下代码生成与OpenSSL语句相同的签名:

using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using System;
using System.IO;
...
// For testing purposes a 512 bits key is used.
// In practice, keys >= 2048 bits must be used for security reasons!
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 = Convert.FromHexString("3031300d060960864801650304020105000420d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592");
// Import private PKCS#1 key, PEM encoded
PemReader pemReader = new PemReader(new StringReader(privatePkcs1Pem));
AsymmetricKeyParameter privateKeyParameter = ((AsymmetricCipherKeyPair)pemReader.ReadObject()).Private;
// Sign raw data
ISigner signer = SignerUtilities.GetSigner("NoneWithRSA");
signer.Init(true, privateKeyParameter);
signer.BlockUpdate(dataToSign, 0, dataToSign.Length);
byte[] signature = signer.GenerateSignature();
Console.WriteLine(Convert.ToHexString(signature)); // 8C83CAD897EDA249FEC9EBA231061D585DAFC99177267E3E71BB8A3FCE07CC6663BF4DF7AF2E1C1945D2A6BB42EB25F042228B591FC18CDA82D92CAAE844670C

特殊用例-当可以使用本机C#方法时

如果rasi.bin包含DigestInfo值的DER编码,则不需要BouncyCastle
以下示例假设rasi.bin包含消息DigestInfo值的DER编码。快速的棕色狐狸用SHA256作为摘要跳过懒惰的狗。即,最后32个字节对应于SHA256散列
本机的可能实现。NET方法是:

using System;
using System.Security.Cryptography;
...
// For testing purposes a 512 bits key is used.
// In practice, keys >= 2048 bits must be used for security reasons!
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[] sha256DigestInfoDer = Convert.FromHexString("3031300d060960864801650304020105000420d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592");
byte[] sha256HashToSign = new byte[32];
Buffer.BlockCopy(sha256DigestInfoDer, sha256DigestInfoDer.Length - sha256HashToSign.Length, sha256HashToSign, 0, sha256HashToSign.Length);
using (RSA rsa = RSA.Create())
{ 
rsa.ImportFromPem(privatePkcs1Pem);
byte[] signature = rsa.SignHash(sha256HashToSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); // pass the SHA256 hash, internally the DER encoding of the DigestInfo is generated (which is why the digest must be specified)
Console.WriteLine(Convert.ToHexString(signature)); // 8C83CAD897EDA249FEC9EBA231061D585DAFC99177267E3E71BB8A3FCE07CC6663BF4DF7AF2E1C1945D2A6BB42EB25F042228B591FC18CDA82D92CAAE844670C
}

这给出了相同的签名,因为rasi.bin在两种情况下都是相同的。

但是,请记住,最后一种方法仅适用于rasi.bin包含DigestInfo值的DER编码的情况,而第一种解决方案适用于rasi.bin中的任意数据(只要满足长度标准(。

最新更新