无法获取Openssl签名数据以匹配.NET签名数据或BouncyCastle签名数据

时间:2013-01-21 18:12:42

标签: c# openssl bouncycastle

上周五我一直都在这,但仍然感到难过。

当我运行此命令时:

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

然后使用X509Certificate2RSACryptoServiceProvider类加载.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

my.pem -

  

-----开始私钥-----       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 + 8cNaYZLzbI5XhQJANyqbajqGQB2Awj8cum81BUvU0K2LhxoW       i5hoXXprmynfTyL3N2r99gSNswcuqkqRPT9KfBRuMSzhZUi5IZ05tQJBAKdQ3mJZ       1Vys2nEAXbQD5 / LDI1 + VF / 0t4Z + JxqFBjqtsAoASBN + kSiPAnRl3r175oZ9m9gkd       5YISN0L + WD5Bf4U =       ----- END PRIVATE KEY -----

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

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

TIA

2 个答案:

答案 0 :(得分:3)

我猜想openssl pkeyutl命令实际上是直接签署数据,而不是签署数据摘要。

pkeyutl上的openssl文档包含这个暗示性评论:

  

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

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

  

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

另请参阅openssl dgst的文档。

答案 1 :(得分:1)

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