首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将Openssl签名转换为.NET6

将Openssl签名转换为.NET6
EN

Stack Overflow用户
提问于 2022-03-19 14:29:21
回答 1查看 736关注 0票数 0

应用程序调用openssl来使用

代码语言:javascript
复制
openssl rsautl -sign -in rasi.bin -inkey riktest.key -out allkiri.bin

如何将其转换为.NET 6,以便不需要调用openssl?

riktest.key是包含

代码语言:javascript
复制
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAfddAQEArYjDH7msMFifeYc1AG/TkKpcz2LITI73sC0eqnlgmWi3F7PD
Bo8lWrCw32h3v/FFMrK8KuktlnBtsSLaCCz1DWuXORzHaW7EqG8O8QNzFSmhIoqp
...

这是ASP.NET 6 MVC应用程序。.NET 6 System.Security.Cryptography命名空间是否包含此OpenSsl功能?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-03-20 12:01:28

为什么通常不能使用本机.NET方法

对于RSASignaturePadding.Pkcs1,本机.NET实现SignData()SignHash()遵循RFC8017中描述的5签名方案,该方案将5应用于编码操作:对消息进行散列处理,并对以下值进行签名(即用私钥加密):

代码语言:javascript
复制
EM = 0x00 || 0x01 || PS || 0x00 || T

在这里,PS包含如此多的0xff值,使得EM的大小等于键的模数的大小。T是DigestInfo值的高级编码,它包含摘要OID和散列,例如对于SHA256:

代码语言:javascript
复制
3031300d060960864801650304020105000420 || H 

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

相反,https://www.openssl.org/docs/man1.1.1/man1/rsautl.html直接使用RSA算法,如备注部分中提到的,即对以下数据进行签名:

代码语言:javascript
复制
EM' = 0x00 || 0x01 || PS || 0x00 || M

通常情况下,本机.NET方法无法实现这一点(除了特殊用例之外,请参阅下面):SignData()散列(因此失败),SignHash()不散列,但在内部(如SignData())生成DigestInfo值的DER编码。

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

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

密钥导入

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

自.NET 5以来,ImportFromPem()支持ImportFromPem()导入PEM编码密钥(私有/公共、PKCS#8/PKCS#1格式),但从.NET Core3.0开始就支持DER编码密钥的导入。PKCS#1格式的私有DER编码密钥可以用ImportRSAPrivateKey()导入( PEM和DER编码之间的转换非常简单,包括删除页眉、页脚和换行以及其余主体的Base64解码)。

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

--使用BouncyCastle实现发布的OpenSSL功能的可能性

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

代码语言:javascript
复制
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方法的一个可能的实现是:

代码语言:javascript
复制
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中的任意数据(只要满足长度条件)。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71538998

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档