如何使用 .pfx 证书加密、解密和签名?
How can I encrypt, decrypt and sign using .pfx certificate?
我的计算机上有一个 .pfx 证书文件。我想用 public 密钥加密一条消息,然后用私有密钥对其进行解密。
另外我想用它的私钥签署另一条消息,然后检查签名。我需要从该消息中获取有关消息签名的证书的信息。
如何使用 System.Security.Cryptography 来实现?
您可以在 .NET 中打开 PFX,如下所示:
var path = <YOUR PFX FILE PATH>;
var password = <YOUR PASSWORD>;
var collection = new X509Certificate2Collection();
collection.Import(path, password, X509KeyStorageFlags.PersistKeySet);
然后,枚举完X509Certificate2Collection
。一旦你有了证书(假设只有一个证书),那么:
var certificate = collection[0];
要加密数据,您可以使用:
var publicKey = certificate.PublicKey.Key as RSACryptoServiceProvider;
var encryptedData = publicKey.Encrypt(<yourdata>, false);
这里,我没有使用OAEP
进行加密,但是你可以通过将第二个参数fOAEP
设置为true
来使用
要解密数据,您可以使用:
var privateKey = certificate.PrivateKey as RSACryptoServiceProvider;
var data = privateKey.Decrypt(encryptedData, false);
PFX中的证书可能没有对应的私钥,所以在访问PrivateKey
之前可以使用下面的属性检查私钥是否存在属性
if (!certificate.HasPrivateKey)
throw new Exception("The certificate does not have a private key");
如果您使用 OAEP
加密,则必须将 fOAEP
设置为 true
进行解密。
要对数据进行签名,您可以使用:
var signature = privateKey.SignData(<yourdata>, "SHA1");
要验证签名,您可以使用:
var isValid = publicKey.VerifyData(<yourdata>, "SHA1", signature);
这里我用了SHA1
,算不上强。您可以使用其他更强大的哈希算法,例如 SHA256
。
最后,如果你的消息是一个小字符串,那么前面的过程是没问题的。但是,如果您要加密大数据,那么我建议您使用对称加密,然后使用 public 密钥加密对称密钥。 (完整示例请参阅 X509Certificate2 Class。)
从 .NET 4.6 开始,减少了对 *CryptoServiceProvider 类型的依赖:
private static byte[] SignArbitrarily(byte[] data, X509Certificate2 cert)
{
Debug.Assert(data != null);
Debug.Assert(cert != null);
Debug.Assert(cert.HasPrivateKey);
// .NET 4.6(.0):
using (RSA rsa = cert.GetRSAPrivateKey())
{
if (rsa != null)
{
// You need to explicitly pick a hash/digest algorithm and padding for RSA,
// these are just some example choices.
return rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
}
// .NET 4.6.1:
using (ECDsa ecdsa = cert.GetECDsaPrivateKey())
{
if (ecdsa != null)
{
// ECDSA signatures need to explicitly choose a hash algorithm, but there
// are no padding choices (unlike RSA).
return ecdsa.SignData(data, HashAlgorithmName.SHA256);
}
}
// .NET 4.6.2 (currently in preview):
using (DSA dsa = cert.GetDSAPrivateKey())
{
if (dsa != null)
{
// FIPS 186-1 (legacy) DSA does not have an option for the hash algorithm,
// SHA-1 was the only option.
// FIPS 186-3 (current) DSA allows any of SHA-1, SHA-2-224, SHA-2-256,
// SHA-2-384, and SHA-2-512 (.NET does not support SHA-2-224).
// KeySize < 1024 is FIPS-186-1 mode, > 1024 is 186-3, and == 1024 can
// be either (depending on the hardware/software provider).
// So, SHA-1 is being used in this example as the "most flexible",
// but may not currently be considered "secure".
return dsa.SignData(data, HashAlgorithmName.SHA1);
}
}
throw new InvalidOperationException("No algorithm handler");
}
// Uses the same choices as SignArbitrarily.
private static bool VerifyArbitrarily(byte[] data, byte[] signature, X509Certificate2 cert)
{
Debug.Assert(data != null);
Debug.Assert(signature != null);
Debug.Assert(cert != null);
using (RSA rsa = cert.GetRSAPublicKey())
{
if (rsa != null)
{
return rsa.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
}
using (ECDsa ecdsa = cert.GetECDsaPublicKey())
{
if (ecdsa != null)
{
return ecdsa.VerifyData(data, signature, HashAlgorithmName.SHA256);
}
}
using (DSA dsa = cert.GetDSAPublicKey())
{
if (dsa != null)
{
return dsa.VerifyData(data, signature, HashAlgorithmName.SHA1);
}
}
throw new InvalidOperationException("No algorithm handler");
}
对于非对称encrypt/decrypt,RSA是唯一的算法,需要填充模式。新的 RSAEncryptionPadding.OaepSHA1
与 RSACryptoServiceProvider 中的 fOAEP: true
相同。 .NET 支持较新的 OAEP,但并非所有底层提供程序都支持。
rsaPublic.Encrypt(data, RSAEncryptionPadding.OaepSHA1);
rsaPrivate.Decrypt(encryptedData, RSAEncryptionPadding.OaepSHA1);
我的计算机上有一个 .pfx 证书文件。我想用 public 密钥加密一条消息,然后用私有密钥对其进行解密。
另外我想用它的私钥签署另一条消息,然后检查签名。我需要从该消息中获取有关消息签名的证书的信息。
如何使用 System.Security.Cryptography 来实现?
您可以在 .NET 中打开 PFX,如下所示:
var path = <YOUR PFX FILE PATH>;
var password = <YOUR PASSWORD>;
var collection = new X509Certificate2Collection();
collection.Import(path, password, X509KeyStorageFlags.PersistKeySet);
然后,枚举完X509Certificate2Collection
。一旦你有了证书(假设只有一个证书),那么:
var certificate = collection[0];
要加密数据,您可以使用:
var publicKey = certificate.PublicKey.Key as RSACryptoServiceProvider;
var encryptedData = publicKey.Encrypt(<yourdata>, false);
这里,我没有使用OAEP
进行加密,但是你可以通过将第二个参数fOAEP
设置为true
来使用
要解密数据,您可以使用:
var privateKey = certificate.PrivateKey as RSACryptoServiceProvider;
var data = privateKey.Decrypt(encryptedData, false);
PFX中的证书可能没有对应的私钥,所以在访问PrivateKey
之前可以使用下面的属性检查私钥是否存在属性
if (!certificate.HasPrivateKey)
throw new Exception("The certificate does not have a private key");
如果您使用 OAEP
加密,则必须将 fOAEP
设置为 true
进行解密。
要对数据进行签名,您可以使用:
var signature = privateKey.SignData(<yourdata>, "SHA1");
要验证签名,您可以使用:
var isValid = publicKey.VerifyData(<yourdata>, "SHA1", signature);
这里我用了SHA1
,算不上强。您可以使用其他更强大的哈希算法,例如 SHA256
。
最后,如果你的消息是一个小字符串,那么前面的过程是没问题的。但是,如果您要加密大数据,那么我建议您使用对称加密,然后使用 public 密钥加密对称密钥。 (完整示例请参阅 X509Certificate2 Class。)
从 .NET 4.6 开始,减少了对 *CryptoServiceProvider 类型的依赖:
private static byte[] SignArbitrarily(byte[] data, X509Certificate2 cert)
{
Debug.Assert(data != null);
Debug.Assert(cert != null);
Debug.Assert(cert.HasPrivateKey);
// .NET 4.6(.0):
using (RSA rsa = cert.GetRSAPrivateKey())
{
if (rsa != null)
{
// You need to explicitly pick a hash/digest algorithm and padding for RSA,
// these are just some example choices.
return rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
}
// .NET 4.6.1:
using (ECDsa ecdsa = cert.GetECDsaPrivateKey())
{
if (ecdsa != null)
{
// ECDSA signatures need to explicitly choose a hash algorithm, but there
// are no padding choices (unlike RSA).
return ecdsa.SignData(data, HashAlgorithmName.SHA256);
}
}
// .NET 4.6.2 (currently in preview):
using (DSA dsa = cert.GetDSAPrivateKey())
{
if (dsa != null)
{
// FIPS 186-1 (legacy) DSA does not have an option for the hash algorithm,
// SHA-1 was the only option.
// FIPS 186-3 (current) DSA allows any of SHA-1, SHA-2-224, SHA-2-256,
// SHA-2-384, and SHA-2-512 (.NET does not support SHA-2-224).
// KeySize < 1024 is FIPS-186-1 mode, > 1024 is 186-3, and == 1024 can
// be either (depending on the hardware/software provider).
// So, SHA-1 is being used in this example as the "most flexible",
// but may not currently be considered "secure".
return dsa.SignData(data, HashAlgorithmName.SHA1);
}
}
throw new InvalidOperationException("No algorithm handler");
}
// Uses the same choices as SignArbitrarily.
private static bool VerifyArbitrarily(byte[] data, byte[] signature, X509Certificate2 cert)
{
Debug.Assert(data != null);
Debug.Assert(signature != null);
Debug.Assert(cert != null);
using (RSA rsa = cert.GetRSAPublicKey())
{
if (rsa != null)
{
return rsa.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
}
using (ECDsa ecdsa = cert.GetECDsaPublicKey())
{
if (ecdsa != null)
{
return ecdsa.VerifyData(data, signature, HashAlgorithmName.SHA256);
}
}
using (DSA dsa = cert.GetDSAPublicKey())
{
if (dsa != null)
{
return dsa.VerifyData(data, signature, HashAlgorithmName.SHA1);
}
}
throw new InvalidOperationException("No algorithm handler");
}
对于非对称encrypt/decrypt,RSA是唯一的算法,需要填充模式。新的 RSAEncryptionPadding.OaepSHA1
与 RSACryptoServiceProvider 中的 fOAEP: true
相同。 .NET 支持较新的 OAEP,但并非所有底层提供程序都支持。
rsaPublic.Encrypt(data, RSAEncryptionPadding.OaepSHA1);
rsaPrivate.Decrypt(encryptedData, RSAEncryptionPadding.OaepSHA1);