使用C#、BouncyCastle和导入的RSA密钥进行RSA签名和验证-内部有可工作的Python示例和不可工作的C#
本文关键字:工作 RSA 内部 Python 导入 BouncyCastle 密钥 使用 验证 | 更新日期: 2023-09-27 18:28:34
我一直在努力获得一个使用C#和BouncyCastle进行RSA数据签名和验证的琐碎例子。
在我使用Python和M2Crypto的示例中,RSACryptoServiceProvider.VerifyHash()总是返回false。
我已经验证了工作示例和C#示例之间的哈希签名是相同的,我就被卡住了。我觉得我遗漏了一些重要的细节。
下面是可工作的Python代码和不可工作的C#代码。
密钥是用生成的
openssl genrsa -out testkey.pem 1024
openssl rsa -in testkey.pem -pubout > testkey.pub
Python代码(工作):
private = """-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCxSHbp1ID/XHEdzVzgqYR1F/79PeMbqzuKNZChvt1gFObBhKyB
pgaHDHw3UZrO8s/hBEE/Mpe2Lh90ZAFdPIXq+HyVoznXMoJYBv0uLDApvSQbJNOd
f7T5VwmpBbkfj1NAlm39Eun9uBSokEOnB24g+bczPQPGOASrQ2Ly9afOZQIDAQAB
AoGBAIEzQIZ1OnXgVwfTLMcGg+QaUtkYizUU+9Vj6D4YrZliYjHSkS4DY2p0rOpb
7Ki5yMpCoZJ/OpWo03+tilj6zNUU6X3aHrPPSv8jcsE0sDi9zYJr/Ztk3EG23sad
bM28Bb4fV/Z0/E6FZJrmuvI2dZP/57oQSHGOhtkHFO21cW5BAkEA3l/i5Rc34YXc
WHYEsOYe0kAxS4dCmhbLWaWvsghW/TomjdxzePsO70GZZqRMdzkfA1iS1OrK+pP4
4suL2rSLrQJBAMwXFnBp4Jmek0CTSxoYf6q91eFm/IRkGLnzE1fEZ76vQOBTas8T
/mpjNQHSEywo/QB62h9A8hy7XNrfZJAMJJkCQA5TYwybKFBxDTbts3Op/4ZP+F0D
Q7klisglsmHnw6Lgoic1coLyuY2UTkucfgiYN3VBuYPZ9GWcLsZ9km7ufqkCQQCz
NVa70Qyqd+cfXfcla/u2pskHCtKTQf3AUmRavhjHBMa39CemvAy7yG9EMP4q2bcH
U9jydqnidtdbTavVHQSJAkA0zJtLzHGPtQqQaI7K6kBDXYPqloGnQxHxad0HPx2e
Vj2qv1tEsqeG6HC7aL/afXOtxcfjq4oMHbGUjDv+dGfP
-----END RSA PRIVATE KEY-----"""
public = """-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxSHbp1ID/XHEdzVzgqYR1F/79
PeMbqzuKNZChvt1gFObBhKyBpgaHDHw3UZrO8s/hBEE/Mpe2Lh90ZAFdPIXq+HyV
oznXMoJYBv0uLDApvSQbJNOdf7T5VwmpBbkfj1NAlm39Eun9uBSokEOnB24g+bcz
PQPGOASrQ2Ly9afOZQIDAQAB
-----END PUBLIC KEY-----"""
message = "test input string"
import base64
# Use EVP api to sign message
from M2Crypto import EVP
key = EVP.load_key_string(private)
key.reset_context(md='sha1')
key.sign_init()
key.sign_update(message)
signature = key.sign_final()
encoded = base64.b64encode(signature)
print encoded
with open("python_sig2.txt","w") as f:
f.write(encoded)
# Use EVP api to verify signature
from M2Crypto import BIO, RSA, EVP
bio = BIO.MemoryBuffer(public)
rsa = RSA.load_pub_key_bio(bio)
pubkey = EVP.PKey()
pubkey.assign_rsa(rsa)
pubkey.reset_context(md="sha1")
pubkey.verify_init()
pubkey.verify_update(message)
decoded = base64.b64decode(encoded)
print pubkey.verify_final(decoded) == 1
C#代码(verifyhash()返回false):
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
namespace RsaSignTest
{
class Program
{
private const string privateKey =
@"-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCxSHbp1ID/XHEdzVzgqYR1F/79PeMbqzuKNZChvt1gFObBhKyB
pgaHDHw3UZrO8s/hBEE/Mpe2Lh90ZAFdPIXq+HyVoznXMoJYBv0uLDApvSQbJNOd
f7T5VwmpBbkfj1NAlm39Eun9uBSokEOnB24g+bczPQPGOASrQ2Ly9afOZQIDAQAB
AoGBAIEzQIZ1OnXgVwfTLMcGg+QaUtkYizUU+9Vj6D4YrZliYjHSkS4DY2p0rOpb
7Ki5yMpCoZJ/OpWo03+tilj6zNUU6X3aHrPPSv8jcsE0sDi9zYJr/Ztk3EG23sad
bM28Bb4fV/Z0/E6FZJrmuvI2dZP/57oQSHGOhtkHFO21cW5BAkEA3l/i5Rc34YXc
WHYEsOYe0kAxS4dCmhbLWaWvsghW/TomjdxzePsO70GZZqRMdzkfA1iS1OrK+pP4
4suL2rSLrQJBAMwXFnBp4Jmek0CTSxoYf6q91eFm/IRkGLnzE1fEZ76vQOBTas8T
/mpjNQHSEywo/QB62h9A8hy7XNrfZJAMJJkCQA5TYwybKFBxDTbts3Op/4ZP+F0D
Q7klisglsmHnw6Lgoic1coLyuY2UTkucfgiYN3VBuYPZ9GWcLsZ9km7ufqkCQQCz
NVa70Qyqd+cfXfcla/u2pskHCtKTQf3AUmRavhjHBMa39CemvAy7yG9EMP4q2bcH
U9jydqnidtdbTavVHQSJAkA0zJtLzHGPtQqQaI7K6kBDXYPqloGnQxHxad0HPx2e
Vj2qv1tEsqeG6HC7aL/afXOtxcfjq4oMHbGUjDv+dGfP
-----END RSA PRIVATE KEY-----";
private const string publicKey =
@"-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxSHbp1ID/XHEdzVzgqYR1F/79
PeMbqzuKNZChvt1gFObBhKyBpgaHDHw3UZrO8s/hBEE/Mpe2Lh90ZAFdPIXq+HyV
oznXMoJYBv0uLDApvSQbJNOdf7T5VwmpBbkfj1NAlm39Eun9uBSokEOnB24g+bcz
PQPGOASrQ2Ly9afOZQIDAQAB
-----END PUBLIC KEY-----";
static void Main(string[] args)
{
var data = "test input string";
var sig = SignWithPrivateKey(data);
var valid = VerifyWithPublicKey(data,sig);
}
private static byte[] SignWithPrivateKey(string data)
{
RSACryptoServiceProvider rsa;
using (var keyreader = new StringReader(privateKey))
{
var pemreader = new PemReader(keyreader);
var y = (AsymmetricCipherKeyPair) pemreader.ReadObject();
var rsaPrivKey = (RsaPrivateCrtKeyParameters)y.Private;
rsa = (RSACryptoServiceProvider)RSACryptoServiceProvider.Create();
var rsaParameters = DotNetUtilities.ToRSAParameters(rsaPrivKey);
rsa.ImportParameters(rsaParameters);
}
// compute sha1 hash of the data
var sha = new SHA1CryptoServiceProvider();
byte[] hash = sha.ComputeHash(Encoding.ASCII.GetBytes(data));
// actually compute the signature of the SHA1 hash of the data
var sig = rsa.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
// base64 encode the signature and write to compare to the python version
String b64signature = Convert.ToBase64String(sig);
using (var sigwriter = new StreamWriter(@"C:'scratch'csharp_sig2.txt"))
{
sigwriter.Write(b64signature);
}
return sig;
}
private static bool VerifyWithPublicKey(string data,byte[] sig)
{
RSACryptoServiceProvider rsa;
using (var keyreader = new StringReader(publicKey))
{
var pemReader = new PemReader(keyreader);
var y = (RsaKeyParameters) pemReader.ReadObject();
rsa = (RSACryptoServiceProvider) RSACryptoServiceProvider.Create();
var rsaParameters = new RSAParameters();
rsaParameters.Modulus = y.Modulus.ToByteArray();
rsaParameters.Exponent = y.Exponent.ToByteArray();
rsa.ImportParameters(rsaParameters);
}
// compute sha1 hash of the data
var sha = new SHA1CryptoServiceProvider();
byte[] hash = sha.ComputeHash(Encoding.ASCII.GetBytes(data));
// This always returns false
return rsa.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"),sig);
}
}
}
在这一点上,我不知道如何进行,任何帮助都将不胜感激。
重新构造私钥/公钥的方式有问题。很明显,在python中,您需要注意做到这一点。
我创建了新的密钥,使用以下代码进行验证(以不同的格式):
private static void GenerateKeys(out string forPubKey, out string forPrivKey)
{
GenerateKeys(out forPubKey, out forPrivKey, 2048, 65537, 80);
}
/// <summary>
///
/// </summary>
/// <param name="forPubKey"></param>
/// <param name="forPrivKey"></param>
/// <param name="keyStrength">1024, 2048,4096</param>
/// <param name="exponent">Typically a fermat number 3, 5, 17, 257, 65537, 4294967297, 18446744073709551617,</param>
/// <param name="certaninty">Should be 80 or higher depending on Key strength number (exponent)</param>
private static void GenerateKeys(out string forPubKey, out string forPrivKey, int keyStrength, int exponent, int certaninty)
{
// Create key
RsaKeyPairGenerator generator = new RsaKeyPairGenerator();
/*
* This value should be a Fermat number. 0x10001 (F4) is current recommended value. 3 (F1) is known to be safe also.
* 3, 5, 17, 257, 65537, 4294967297, 18446744073709551617,
*
* Practically speaking, Windows does not tolerate public exponents which do not fit in a 32-bit unsigned integer. Using e=3 or e=65537 works "everywhere".
*/
BigInteger exponentBigInt = new BigInteger(exponent.ToString());
var param = new RsaKeyGenerationParameters(
exponentBigInt, // new BigInteger("10001", 16) publicExponent
new SecureRandom(), // SecureRandom.getInstance("SHA1PRNG"),//prng
keyStrength, //strength
certaninty);//certainty
generator.Init(param);
AsymmetricCipherKeyPair keyPair = generator.GenerateKeyPair();
// Save to export format
SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
byte[] ret = info.GetEncoded();
forPubKey = Convert.ToBase64String(ret);
// EncryptedPrivateKeyInfo asdf = EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo(
// DerObjectIdentifier.Ber,,,keyPair.Private);
//// demonstration: how to serialise option 1
//TextWriter textWriter = new StringWriter();
//PemWriter pemWriter = new PemWriter(textWriter);
//pemWriter.WriteObject(keyPair);
//pemWriter.Writer.Flush();
//string ret2 = textWriter.ToString();
//// demonstration: how to serialise option 1
//TextReader tr = new StringReader(ret2);
//PemReader read = new PemReader(tr);
//AsymmetricCipherKeyPair something = (AsymmetricCipherKeyPair)read.ReadObject();
//// demonstration: how to serialise option 2 (don't know how to deserailize)
//PrivateKeyInfo pKinfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);
//byte[] privRet = pKinfo.GetEncoded();
//string forPrivKey2Test = Convert.ToBase64String(privRet);
PrivateKeyInfo pKinfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);
byte[] privRet = pKinfo.GetEncoded();
string forPrivKey2Test = Convert.ToBase64String(privRet);
forPrivKey = forPrivKey2Test;
}
然后将它们转换回RSA对象,如下所示:
// Private key
RsaPrivateCrtKeyParameters kparam = ConvertToRSAPrivateKey(privateKey);
RSAParameters p1 = DotNetUtilities.ToRSAParameters(kparam);
rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(p1);
// Public key
RsaKeyParameters kparam = ConvertToRSAPublicKey(publicKey);
RSAParameters p1 = DotNetUtilities.ToRSAParameters(kparam);
rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(p1);