4

上周五我一直在做这件事,但仍然很难过。

当我运行这个命令时:

echo -n "Data To Sign" | openssl pkeyutl -sign -inkey path/to/my.pem | openssl base64

通过 openssl 我得到输出:

1c284UkFgC6pqyJ+woSU+DiWB4MabWVDVhhUbBrTtF7CkpG8MjY+KkPFsZm9ZNM8vCjZjf...Kw=

我想在 C# 中复制这种行为。我所做的第一次尝试是使用 openssl 从我的 .pem 文件创建一个 .p12 文件。我通过运行以下两个 openssl 命令来做到这一点:

openssl req -new -key path/to/my.pem -x509 -batch > my.crt
openssl pkcs12 -export -in my.crt -inkey path/to/my.pem -out my.p12

然后使用X509Certificate2andRSACryptoServiceProvider类加载 .p12 并签名。下面的代码:

var dataToSign = "Data To Sign";
var p12 = new X509Certificate2(@"path\to\my.p12","");
var rsa = (RSACryptoServiceProvider)p12.PrivateKey;
var signedBytes = rsa.SignData(Encoding.UTF8.GetBytes(dataToSign), "SHA1");
var result = Convert.ToBase64String(signedBytes);

产生:

B2qM6MTjoZFSbnckezzpXrKFq67vFgsCPYBmaAbKOFmzVQLIU4a+GC6LWTMdNO4...Q0=

不幸的是,输出不匹配。在与这个问题斗争了一段时间之后,我决定走 SO 上的几个答案所建议的 BouncyCastle 路线。这是我使用该库提出的代码:

StreamReader sr = new StreamReader(@"path\to\my.pem");
PemReader pr = new PemReader(sr);
var pemKeyParams = (RsaPrivateCrtKeyParameters)pr.ReadObject();
var cypherParams = new RsaKeyParameters(true, pemKeyParams.Modulus, pemKeyParams.Exponent);
ISigner sig = SignerUtilities.GetSigner("SHA1withRSA");
sig.Init(true, cypherParams);
var bytes = Encoding.UTF8.GetBytes("Data To Sign");
sig.BlockUpdate(bytes, 0, bytes.Length);
byte[] signature = sig.GenerateSignature();
var result = Convert.ToBase64String(signature);

这也产生:

B2qM6MTjoZFSbnckezzpXrKFq67vFgsCPYBmaAbKOFmzVQLIU4a+GC6LWTMdNO4...Q0=

BouncyCastle 输出与使用 Security 命名空间中的本机库的 C# 代码给出的输出相匹配,但我想匹配 openssl 的输出。我究竟做错了什么?

版本 -
OpenSSL 1.0.1c
.NET 4.0
BouncyCastle 1.7.0
Windows 7

我的.pem -

-----BEGIN PRIVATE KEY----- MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOtk3bYdQsjeG1Xy 2KgF8ecWcPudPLEnV32OIbtA+h2hXQ853ZRsxusopm7vmqtI2/aVfc2vyw9AGY0U cjqPnyEq7et5oQydo5+aTEW3PenP9DR3MJ273ipPbrYX+I3XzJ+I6//k6DO/OAIA JLlXc9iT1pblSrHymFNEkIFiUgj3AgMBAAECgYBtP1Lmwo3MS8jECwEieh/a8D9f h4ozbd7dFqnxDicGuW1HM8PyrsljOmqD8hAGjroHpznLzFqhqU4ye9rH8wAWsKUj Qst/RjyDU3SNscyU/eg+ezuawUXafpPUEUTJ0aofdHn9GIVipiIi/4uaPP/IYtuC U2smep4C2+geqfTugQJBAP5MTaRQjoYBGKS/ Bgd0JB16MHFV6FPDCX3NZ2CLTyZm o8edQZI4SbWoxkJaGqBOqDbz/dSmTLfRNmpAmC+az5sCQQDs+CyDLbs3URvD7ajx JjsJoPbuVmqPBPGmAy/4Qt3QVp9AWk+9uckU90DYMqJp5bdGoeokmA65uuEcvqbs yzfVAkEA018FIlE7RjNfEoEdN9DXvBC2d14a0JTLLOAwz1S8I4UpGWCjAjD7Q53X vYs7mogG1jaUg87+8cNaYZLzbI5XhQJANyqbajqGQB2Awj8cum81BUvU0K2LhxoWi5hoXXprmynfTyL3N2r99gSNswcuqkqRPT9KfBRuMSzhZUi5IZ05tQJBAKdQ3mJZ 1Vys2nEAXbQD5/ldi1+VF/0t4Z+JxqFBjqtsAoASBN+kSiPANRl3r175oZ9m9gkd 5YISN0L+WD5Bf4U=-----结束私钥-----

出于这个问题的目的,我生成了一个新的 .pem,上面的密钥不保护任何重要的东西,但我提供它是为了重现我上面的步骤。

到目前为止我考虑过的事情:
* 编码(ASCII vs UTF8)
* Endianess(openssl 的-rev 仍然不匹配任何编码结果)
* hashalg(我找不到任何地方“SHA1”是正式的正确哈希要使用的算法,但它似乎是其他人正在使用的并且尝试其他选项没有帮助)
*换行符(我相信 echo 上的 -n 必须与库相提并论,但删除它没有帮助)
*询问SO(结果待定:-))

TIA

4

2 回答 2

3

我猜 openssl pkeyutl 命令实际上是直接对数据进行签名,而不是对数据摘要进行签名。

pkeyutl 上的 openssl 文档包含以下提示性评论:

“除非另有说明,否则所有算法都支持摘要:alg 选项,该选项指定用于签名、验证和验证恢复操作的摘要。值 alg 应表示 EVP_get_digestbyname() 函数中使用的摘要名称,例如 sha1。”

我在您的命令行上没有看到摘要选项。此外,来自相同的文档(在 RSA 部分):

“在 PKCS#1 填充中,如果未设置消息摘要,则直接对提供的数据进行签名或验证,而不是使用 DigestInfo 结构。如果设置了摘要,则使用 DigestInfo 结构,其长度必须与摘要相对应类型。”

另请参阅 openssl dgst 的文档。

于 2013-01-22T09:22:20.553 回答
1

RSA 签名可以使用不同的填充模式(PKCS#1、PSS 等)。最后一个它使用一些随机盐,所以每次签名都会不同。因此,首先检查openssl每次是否生成相同的值(我没有发现openssl代码使用的默认填充,您可以使用-rsa_padding_mode参数更改它),实际上,如果两个签名都可以被对方正确验证.

于 2013-01-21T19:53:54.560 回答