在多台服务器上使用PFX进行加密和解密

时间:2019-03-07 13:10:18

标签: c# encryption x509certificate2 pfx

我目前正在尝试构建一个对客户数据进行加密并将其添加到另一台服务器上的远程MSMQ队列的系统。然后,每隔X分钟运行一次的作业将拾取数据,该作业将尝试解密并处理数据。

我们有一个必须使用.PFX证书进行加密/解密的要求(我知道这不是最有效的处理方式,但是有此要求,我无法更改此设置)。

我当前正在使用通过以下方式使用Open-SSL的自签名证书:

openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem

openssl pkcs12 -inkey key.pem -in certificate.pem -export -out certificate.p12

我已经成功加密了数据,但是每次尝试解密时,都会收到通用的“参数不正确”异常。

要加载证书,我们将.PFX文件保存在本地计算机上,并使用X509Certificate2类,将其导入并用于加密和解密。这是我正在使用的辅助程序类的简化版本:

public static string EncryptData(string data)
{
    var certificate = GetCertificate();

    using (var rsa = certificate.PublicKey.Key as RSACryptoServiceProvider)
    {
        var dataBytes = Convert.FromBase64String(data);
        var encryptedBytes = rsa.Encrypt(dataBytes, false);

        return Convert.ToBase64String(encryptedBytes);
    }
}

public static string DecryptData(string data)
{
    var certificate = GetCertificate();

    using (var rsa = certificate.PrivateKey as RSACryptoServiceProvider)
    {
        var dataBytes = Convert.FromBase64String(data);
        var decryptedBytes = rsa.Decrypt(dataBytes, false);

        return Convert.ToBase64String(decryptedBytes);
    }
}

private static X509Certificate2 GetCertificate()
{   
    var certificate = new X509Certificate2();
    certificate.Import("certificatePath", "certificatePassword", X509KeyStorageFlags.PersistKeySet);
    return certificate;
}

错误始终在“ rsa.Decrypt()”调用上发生。

我尝试了以下操作:

  • 加密后立即在我的“ EncryptData”方法中调用“ rsa.Decrypt()”。这没有问题,并且“ rsa.Decrypt”为我提供了与原始数据字节相同的字节。

  • 在调用“ EncryptData”之后立即调用“ DecryptData”方法。发生相同的问题,并且出现“参数不正确”的异常

这就是为什么我怀疑创建“新的” X509Certificate2的事实,即私钥不再相同并且无法再解密数据。

请注意,我不是安全专家,因此还没有使用X509证书或任何加密技术,因此我有点不了解,可能做的事情很愚蠢,所以请告诉我我是否<。 / p>

更新1(08/03/2019)

已按照@bartonjs给出的推荐点1-3和5更新了代码。

public static string EncryptData(string data)
{
    var certificate = GetCertificate();

    using (var rsa = certificate.GetRSAPublicKey())
    {
        var dataBytes = Convert.FromBase64String(data);
        var encryptedBytes = rsa.Encrypt(dataBytes, RSAEncryptionPadding.OaepSHA1);

        return Convert.ToBase64String(encryptedBytes);
    }
}

public static string DecryptData(string data)
{
    var certificate = GetCertificate();

    using (var rsa = certificate.GetRSAPrivateKey())
    {
        var dataBytes = Convert.FromBase64String(data);
        var decryptedBytes = rsa.Decrypt(dataBytes, RSAEncryptionPadding.OaepSHA1);

        return Convert.ToBase64String(decryptedBytes);
    }
}

private static X509Certificate2 GetCertificate()
{   
    var certificate = new X509Certificate2("certificatePath", "certificatePassword", X509KeyStorageFlags.PersistKeySet);
    return certificate;
}

添加的错误消息:

Message: The parameter is incorrect.

Stack Trace:
 at System.Security.Cryptography.NCryptNative.DecryptData[T](SafeNCryptKeyHandle key, Byte[] data, T& paddingInfo, AsymmetricPaddingMode paddingMode, NCryptDecryptor`1 decryptor)
 at System.Security.Cryptography.NCryptNative.DecryptDataOaep(SafeNCryptKeyHandle key, Byte[] data, String hashAlgorithm)
 at System.Security.Cryptography.RSACng.Decrypt(Byte[] data, RSAEncryptionPadding padding)

1 个答案:

答案 0 :(得分:0)

很抱歉,这里几乎所有内容都不对。

GetCertificate

private static X509Certificate2 GetCertificate()
{   
    var certificate = new X509Certificate2();
    certificate.Import("certificatePath", "certificatePassword", X509KeyStorageFlags.PersistKeySet);
    return certificate;
}

您正在使用PersistKeySet进行导入,因此您正在慢慢填充硬盘驱动器。参见What is the rationale for all the different X509KeyStorageFlags?

此外,您正在使用certificate.Import,它在.NET Core中不可用(因为突变X509Certificate2对象是意外的)。只需使用构造函数即可。所以整个方法应该是

private static X509Certificate2 GetCertificate()
{
    // Assuming you do nothing else with the certificate than what's shown here,
    // EphemeralKeySet will work for you (except on macOS).
    return new X509Certificate2(path, password, X509KeyStorageFlags.EphemeralKeySet);
}

EncryptData

public static string EncryptData(string data)
{
    var certificate = GetCertificate();

    using (var rsa = certificate.PublicKey.Key as RSACryptoServiceProvider)
    {
        var dataBytes = Convert.FromBase64String(data);
        var encryptedBytes = rsa.Encrypt(dataBytes, false);

        return Convert.ToBase64String(encryptedBytes);
    }
}

这里有些错误。

1)私钥是一个共享属性,因此,如果多次读取该私钥,您将把该对象从另一个调用者中释放出去。

2)如果您碰巧获得了非RSA证书,则不会将其丢弃。

3)您正在使用PrivateKey,它不支持更好的支持现代选项的RSA或DSA类。

4)您是证书的唯一处理者,但没有处理它。也许您的所有权语义可能更清楚。

从安全角度来看,也是5)您使用的是PKCS#1填充而不是OAEP

从数据角度来看,还有6)为什么要给base64数据而不是原始数据提供加密?

我不会讲4-6号。

public static string EncryptData(string data)
{
    var certificate = GetCertificate();

    using (RSA rsa = certificate.GetRSAPublicKey())
    {
        var dataBytes = Convert.FromBase64String(data);
        var encryptedBytes = rsa.Encrypt(dataBytes, RSAEncryptionPadding.Pkcs1);

        return Convert.ToBase64String(encryptedBytes);
    }
}

在这种情况下,将私钥放在using语句中是正确的,GetRSAPrivateKey()方法始终返回一个新对象。

DecryptData

解密方式应与加密方式类似。

如果毕竟,您仍然遇到异常,请提供确切的消息和堆栈跟踪信息(至少是从调用到RSA的部分。解密通过抛出的地方)