上周五我一直都在这,但仍然感到难过。
当我运行此命令时:
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
然后使用X509Certificate2
和RSACryptoServiceProvider
类加载.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
答案 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参数更改它),实际上,如果两个签名都可以被另一方正确验证