使用导入的私钥进行 RSA 签名
RSA signing with imported private key
我买了一个SSL证书b/carvixe不支持自签名证书。我在 .pem 文件中有一个私钥,我想用它来对银行交易参数进行 RSA 签名。到目前为止,我还没有找到可以做到的方法。
certificate.PrivateKey 抛出 Keyset not found 异常。使用充气城堡导入私钥 (.pem) 文件效果很好,直到我需要转换为 RSACryptoServiceProvider。那时,DotNetUtilities.ToRSA 抛出一个 File Not Found 异常。必须有更好的方法来做到这一点!!!
这是我的代码中的相关片段:
public string SignRsa(string stringToSign)
{
var encoder = new ASCIIEncoding();
var binData = encoder.GetBytes(stringToSign);
byte[] binSignature;
if (Request.Url.OriginalString.IndexOf("localhost", StringComparison.Ordinal) < 0)
{
var store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates;
var signed = string.Empty;
X509Certificate2 ipCert = certificates.Find(X509FindType.FindBySubjectName, "www.ingyenpiac.com", false).OfType<X509Certificate2>().First();
RSACryptoServiceProvider rsaCsp;
if (ipCert != null)
{
AsymmetricCipherKeyPair keyPair;
using (var reader = System.IO.File.OpenText(Server.MapPath("~/App_Data/private_key.pem"))) // file containing RSA PKCS1 private key
keyPair = (AsymmetricCipherKeyPair)new PemReader(reader).ReadObject();
PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);
byte[] serializedPrivateBytes = privateKeyInfo.ToAsn1Object().GetDerEncoded();
string serializedPrivate = Convert.ToBase64String(serializedPrivateBytes);
SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
byte[] serializedPublicBytes = publicKeyInfo.ToAsn1Object().GetDerEncoded();
string serializedPublic = Convert.ToBase64String(serializedPublicBytes);
RsaPrivateCrtKeyParameters privateKey = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(serializedPrivate));
RsaKeyParameters publicKey = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(serializedPublic));
var kpp = keyPair.Private as RsaPrivateCrtKeyParameters;
var ppk = DotNetUtilities.ToRSA(kpp); // <==== File not found exception!!!! WTF???
RSACryptoServiceProvider tempRcsp = (RSACryptoServiceProvider)ppk;
RSACryptoServiceProvider rcsp = new RSACryptoServiceProvider(new CspParameters(1, "Microsoft Strong Cryptographic Provider", new Guid().ToString(), new CryptoKeySecurity(), null));
rcsp.ImportCspBlob(tempRcsp.ExportCspBlob(true));
ipCert.PrivateKey = rcsp;
if (ipCert.Verify())
{
rsaCsp = (RSACryptoServiceProvider)ipCert.PrivateKey;
}
else
throw new ApplicationException("Certificate failed to verify.");
}
else
throw new ApplicationException("SignRsa: No certifciate found");
using (var sha = new SHA1CryptoServiceProvider())
{
binSignature = rsaCsp.SignData(binData, sha);
}
if (rsaCsp.VerifyData(binData, new SHA1CryptoServiceProvider(), binSignature))
signed = BitConverter.ToString(binSignature).Replace("-", string.Empty);
store.Close();
return signed;
}
return null;
}
我当然希望有人能帮助我!
哇!我真的把事情复杂化了。不需要充气城堡,也不需要做扭曲。答案很简单,如下所示:
public string SignRsa(string stringToSign)
{
ASCIIEncoding encoder;
var signed = string.Empty;
var ipCert = new X509Certificate2(Server.MapPath("~/App_Data/pfxFile.pfx"), "password");
var RSA = (RSACryptoServiceProvider)ipCert.PrivateKey;
encoder = new ASCIIEncoding();
var binData = encoder.GetBytes(stringToSign);
byte[] binSignature;
using (var sha1 = new SHA1CryptoServiceProvider())
binSignature = RSA.SignData(binData, sha1);
if (RSA.VerifyData(binData, new SHA1CryptoServiceProvider(), binSignature))
signed = BitConverter.ToString(binSignature).Replace("-", string.Empty);
return signed;
}
在 .NET 4.6 中,这段代码可以稍微简化一下:
public string SignRsa(string stringToSign)
{
var signed = string.Empty;
using (var ipCert = new X509Certificate2(Server.MapPath("~/App_Data/pfxFile.pfx"), "password"))
using (var RSA = ipCert.GetRSAPrivateKey())
{
// Note, if the cert was not RSA, or had no private key, RSA
// will be null. But you didn't check it, so I won't.
var binData = System.Text.Encoding.ASCII.GetBytes(stringToSign);
byte[] binSignature = RSA.SignData(binData, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
// Not sure why you want to re-verify the signature, but OK:
if (RSA.VerifyData(binData, binSignature, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1))
signed = BitConverter.ToString(binSignature).Replace("-", string.Empty);
return signed;
}
}
我买了一个SSL证书b/carvixe不支持自签名证书。我在 .pem 文件中有一个私钥,我想用它来对银行交易参数进行 RSA 签名。到目前为止,我还没有找到可以做到的方法。
certificate.PrivateKey 抛出 Keyset not found 异常。使用充气城堡导入私钥 (.pem) 文件效果很好,直到我需要转换为 RSACryptoServiceProvider。那时,DotNetUtilities.ToRSA 抛出一个 File Not Found 异常。必须有更好的方法来做到这一点!!!
这是我的代码中的相关片段:
public string SignRsa(string stringToSign)
{
var encoder = new ASCIIEncoding();
var binData = encoder.GetBytes(stringToSign);
byte[] binSignature;
if (Request.Url.OriginalString.IndexOf("localhost", StringComparison.Ordinal) < 0)
{
var store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates;
var signed = string.Empty;
X509Certificate2 ipCert = certificates.Find(X509FindType.FindBySubjectName, "www.ingyenpiac.com", false).OfType<X509Certificate2>().First();
RSACryptoServiceProvider rsaCsp;
if (ipCert != null)
{
AsymmetricCipherKeyPair keyPair;
using (var reader = System.IO.File.OpenText(Server.MapPath("~/App_Data/private_key.pem"))) // file containing RSA PKCS1 private key
keyPair = (AsymmetricCipherKeyPair)new PemReader(reader).ReadObject();
PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);
byte[] serializedPrivateBytes = privateKeyInfo.ToAsn1Object().GetDerEncoded();
string serializedPrivate = Convert.ToBase64String(serializedPrivateBytes);
SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
byte[] serializedPublicBytes = publicKeyInfo.ToAsn1Object().GetDerEncoded();
string serializedPublic = Convert.ToBase64String(serializedPublicBytes);
RsaPrivateCrtKeyParameters privateKey = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(serializedPrivate));
RsaKeyParameters publicKey = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(serializedPublic));
var kpp = keyPair.Private as RsaPrivateCrtKeyParameters;
var ppk = DotNetUtilities.ToRSA(kpp); // <==== File not found exception!!!! WTF???
RSACryptoServiceProvider tempRcsp = (RSACryptoServiceProvider)ppk;
RSACryptoServiceProvider rcsp = new RSACryptoServiceProvider(new CspParameters(1, "Microsoft Strong Cryptographic Provider", new Guid().ToString(), new CryptoKeySecurity(), null));
rcsp.ImportCspBlob(tempRcsp.ExportCspBlob(true));
ipCert.PrivateKey = rcsp;
if (ipCert.Verify())
{
rsaCsp = (RSACryptoServiceProvider)ipCert.PrivateKey;
}
else
throw new ApplicationException("Certificate failed to verify.");
}
else
throw new ApplicationException("SignRsa: No certifciate found");
using (var sha = new SHA1CryptoServiceProvider())
{
binSignature = rsaCsp.SignData(binData, sha);
}
if (rsaCsp.VerifyData(binData, new SHA1CryptoServiceProvider(), binSignature))
signed = BitConverter.ToString(binSignature).Replace("-", string.Empty);
store.Close();
return signed;
}
return null;
}
我当然希望有人能帮助我!
哇!我真的把事情复杂化了。不需要充气城堡,也不需要做扭曲。答案很简单,如下所示:
public string SignRsa(string stringToSign)
{
ASCIIEncoding encoder;
var signed = string.Empty;
var ipCert = new X509Certificate2(Server.MapPath("~/App_Data/pfxFile.pfx"), "password");
var RSA = (RSACryptoServiceProvider)ipCert.PrivateKey;
encoder = new ASCIIEncoding();
var binData = encoder.GetBytes(stringToSign);
byte[] binSignature;
using (var sha1 = new SHA1CryptoServiceProvider())
binSignature = RSA.SignData(binData, sha1);
if (RSA.VerifyData(binData, new SHA1CryptoServiceProvider(), binSignature))
signed = BitConverter.ToString(binSignature).Replace("-", string.Empty);
return signed;
}
在 .NET 4.6 中,这段代码可以稍微简化一下:
public string SignRsa(string stringToSign)
{
var signed = string.Empty;
using (var ipCert = new X509Certificate2(Server.MapPath("~/App_Data/pfxFile.pfx"), "password"))
using (var RSA = ipCert.GetRSAPrivateKey())
{
// Note, if the cert was not RSA, or had no private key, RSA
// will be null. But you didn't check it, so I won't.
var binData = System.Text.Encoding.ASCII.GetBytes(stringToSign);
byte[] binSignature = RSA.SignData(binData, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
// Not sure why you want to re-verify the signature, but OK:
if (RSA.VerifyData(binData, binSignature, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1))
signed = BitConverter.ToString(binSignature).Replace("-", string.Empty);
return signed;
}
}