1

我想加密 SQL-Server 上的数据并在 .NET 应用程序上解密。在我的情况下,最好的方法是使用证书。

在我的测试数据库中,我必须创建一个主密钥:

CREATE MASTER KEY ENCRPTION 
BY PASSWORD = 'TestEncryption1212'

然后我在 SQL-Server 上创建了一个证书

CREATE CERTIFICATE TestCertificate1212
WITH SUBJECT = 'TestEncryption1212'

接下来我将它导出到文件系统:

BACKUP CERTIFICATE TestCertificate1212
TO FILE = 'C:\temp\TestCertificate1212.cer'
WITH PRIVATE KEY (
    FILE = 'C:\temp\TestPrivateKey1212.pvk',
    ENCRYPTION BY PASSWORD = 'TestEncryption1212'
    )

如果我添加一个

DECRYPT BY PASSWORD = 'TestEncryption1212'

我收到一条错误消息,指出私钥已由主密码加密。

我在我的测试数据库中加密了一个列

ALTER TABLE dbo.Encrypt
ADD EncryptedCol varbinary (256)

UPDATE dbo.Encrypt
SET EncryptedCol = 
    ENCRYPTBYCERT(
        CERT_ID('TestCertificate1212'),
        ClearTextCol
    )

到目前为止,一切都很好。

下一步是生成一个 PFX 文件,它将证书与私钥文件结合起来

我将 cmd 中的目录更改为 pvk2pfx.exe 所在的文件夹并执行它:

>cd "Program Files\Microsoft SDK
s\Windows\v6.1\Bin"
>pvk2pfx.exe -pvk C:\temp\TestPrivateKey1212.pvk -spc C:\temp\TestCertificate1212.cer -pfx C:\temp\TestPFX.pfx

在我的 .NET 应用程序(C#-ConsoleApplication)中,我通过 SqlConnection 获取数据。我将数据传递给我调用的函数

public string DecryptDocIDWithFileCert(string pfxFilePath, byte[] EncryptedDocID)

此函数应打开证书并解密数据并将明文返回给应用程序:

public string DecryptDocIDWithFileCert(string pfxFilePath, byte[] EncryptedDocID)
{
    string DecryptedDocID = "";
    X509Certificate2 cert = new X509Certificate2(pfxFilePath, "TestEncryption1212");
    if (cert == null)
    {
        throw new Exception("Certificate " + pfxFilePath + " Does not exist");
    }
    if (cert.HasPrivateKey)
    {
        RSACryptoServiceProvider RsaCSP = (RSACryptoServiceProvider)cert.PrivateKey;
        byte[] ret = RsaCSP.Decrypt(EncryptedDocID, true);
        if (ret == null)
        {
            throw new Exception("Decryption with RSA failed");
        }
        DecryptedDocID = System.Text.Encoding.UTF8.GetString(ret);
    }
    else
    {
        throw new Exception("Certificate " + pfxFilePath + " has no Private Key; ");
    }

    return DecryptedDocID;
}

当涉及到解密时,我得到了错误:

System.Security.Cryptography.CryptographicException: The specified network password is not correct.

   at System.Security.Cryptography.CryptographicException.ThrowCryptogaphicException(Int32 hr)
   at System.Security.Cryptography.X509Certificates.X509Utils._LoadCertFromFile(String fileName, IntPtr password, UInt32 dwFlags, Boolean persistKeySet, SafeCertContextHandle& pCertCtx)
   at System.Security.Cryptography.X509Certificates.X509Certificate.LoadCertificateFromFile(String fileName, Object password, X509KeyStorageFlags keyStorageFlags)
   at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(String fileName, String password)
   at TestDecryption.MyDecryptor.DecryptDocIDWithFileCert(String pfxFilePath, Byte[] EncryptedDocID) in C:\Users\developmentUser\Documents\Visual Studio 2008\Projects\TestDecryption\TestDecryption\MyDecryptor.cs:line 57
   at TestDecryption.Program.Main(String[] args) in C:\Users\developmentUser\Documents\Visual Studio 2008\Projects\TestDecryption\TestDecryption\Program.cs:line 37

我厌倦了寻找错误。有没有人看到错误并可以告诉我我做错了什么。我认为问题在于私钥是用主密钥加密的,我无法解密它。我想我需要的只是一个 sql 语句,我可以在没有主密钥的情况下设置私钥的密码?还是我走错了路?

请帮我!


更新

如果我使用以下命令在 SQL_Server 之外制作证书:

C:\temp\createCert>makecert -sv PrivateKey.pvk -n "cn=TestCertificate" TestCerti
ficate.cer -b 01/01/2013 -e 01/01/2014 -sky Exchange -pe

再次运行我所有的 SQL 脚本:

ALTER TABLE dbo.Encrypt
DROP COLUMN EncryptedCol

DROP CERTIFICATE TestCertificate1212
DROP MASTER KEY

CREATE CERTIFICATE TestCertificate
    FROM FILE = 'C:\TEMP\createCert\TestCertificate.cer'
    WITH PRIVATE KEY (
        FILE = 'C:\TEMP\createCert\PrivateKey.pvk',
        ENCRYPTION BY PASSWORD = 'TestEncryption123'
--      DECRYPTION BY PASSWORD = 'TestEncryption123'
    )

ALTER TABLE dbo.Encrypt
ADD EncryptedCol varbinary (256)

UPDATE dbo.Encrypt
SET EncryptedCol = ENCRYPTBYCERT(CERT_ID('TestCertificate'),SecondCol)

然后尝试解密它我得到几个错误:

我用的八点

byte [] ret = RsaCSP.Decrypt(EncryptedDochID, true);

并得到:

System.Security.Cryptography.CryptographicException: Error occurred while decoding OAEP padding.
   at System.Security.Cryptography.Utils._DecryptPKWin2KEnh(SafeKeyHandle hPubKey, Byte[] key, Boolean fOAEP, Int32& hr)
   at System.Security.Cryptography.RSACryptoServiceProvider.Decrypt(Byte[] rgb,Boolean fOAEP)
   at TestDecryption.MyDecryptor.DecryptDocIDWithFileCert(String pfxFilePath, Byte[] EncryptedDocID) in C:\Users\developmentUser\Documents\Visual Studio 2008\Projects\TestDecryption\TestDecryption\MyDecryptor.cs:line 71
   at TestDecryption.Program.Main(String[] args) in C:\Users\developmentUser\Documents\Visual Studio 2008\Projects\TestDecryption\TestDecryption\Program.cs:line 37

或者我使用:

byte [] ret = RsaCSP.Decrypt(EncryptedDochID, false);

并得到:

System.Security.Cryptography.CryptographicException: Bad Data.
   at System.Security.Cryptography.CryptographicException.ThrowCryptogaphicException(Int32 hr)
   at System.Security.Cryptography.Utils._DecryptKey(SafeKeyHandle hPubKey, Byte[] key, Int32 dwFlags)
   at System.Security.Cryptography.RSACryptoServiceProvider.Decrypt(Byte[] rgb, Boolean fOAEP)
   at TestDecryption.MyDecryptor.DecryptDocIDWithFileCert(String pfxFilePath, Byte[] EncryptedDocID) in C:\Users\developmentUser\Documents\Visual Studio 2008\Projects\TestDecryption\TestDecryption\MyDecryptor.cs:line 71
   at TestDecryption.Program.Main(String[] args) in C:\Users\developmentUser\Documents\Visual Studio 2008\Projects\TestDecryption\TestDecryption\Program.cs:line 37

或者

System.Security.Cryptography.CryptographicException: Not enough storage is available to process this command.
   at System.Security.Cryptography.CryptographicException.ThrowCryptogaphicException(Int32 hr)
   at System.Security.Cryptography.Utils._DecryptKey(SafeKeyHandle hPubKey, Byte[] key, Int32 dwFlags)
   at System.Security.Cryptography.RSACryptoServiceProvider.Decrypt(Byte[] rgb, Boolean fOAEP)
   at TestDecryption.MyDecryptor.DecryptDocIDWithFileCert(String pfxFilePath, Byte[] EncryptedDocID) in C:\Users\developmentUser\Documents\Visual Studio 2008\Projects\TestDecryption\TestDecryption\MyDecryptor.cs:line 71
   at TestDecryption.Program.Main(String[] args) in C:\Users\developmentUser\Documents\Visual Studio 2008\Projects\TestDecryption\TestDecryption\Program.cs:line 37

取决于我通过 makeCert ( -pe )设置的标志

我的新问题是:

SQL-Server 是否使用 RSA 使用 ENCRYPTBYCERT() 加密数据????????????? 还是我在尝试不可行?

是的,好像是用RSA加密的。Michael Coles正在制作一些非常相似的东西,并使用 RsaCryptoServiceProvider 在他的 .NET 应用程序中加密数据。但他使用存储过程来解密数据。我不想解密服务器上的数据。我还需要用 RsaCryptoServiceProvider 解密它。

所以新的标题应该是:我怎样才能摆脱我的错误?


更新: 如何使用 RsaCryptoServiceProvider 和自制证书解密数据?

4

1 回答 1

2

答案:在 SQL 之外创建的证书:

Change Directory to cd "C:\Program Files\MicrosoftSDKs\Windows\v6.1\Bin"
copy makecert.exe and pvk2pfx.exe to temp\createCert\
C:\temp\createCert>makecert -sv PrivateKey.pvk -n "cn=TestCertificate" TestCertificate.cer -b 01/01/2013 -e 01/01/2014 -sky Exchange

创建的 pfx 文件

C:\temp\createCert>pvk2pfx -pvk PrivateKey.pvk -spc TestCertificate.cer -po TestEncryption123
  • 是的,导出私钥
  • 如果可能,在认证路径中包含所有证书
  • 导出所有扩展端属性

设置密码:TestEncryption123

选择 pfx 文件的文件名...

在 SQL Server 上运行:

USE master 
GO
 
CREATE DATABASE TestEncryptionDecryption
GO
 
USE TestEncryptionDecryption
 
CREATE TABLE Encrypt 
(
    intCol int,
    clearTextCol varchar(128)
)
GO
 
INSERT INTO Encrypt (intCol, clearTextCol)
VALUES
    ('1', 'Links'),
    ('2', 'Zwo'),
    ('3', 'Drei'),
    ('4', 'Vier'),
    ('5', 'Fünf'),
    ('6', '5ech5')
GO
 
 
CREATE CERTIFICATE TestCertificate
    FROM FILE = 'C:\temp\createCert\TestCertificate.cer'
    WITH PRIVATE KEY (
        FILE = 'C:\temp\createCert\PrivateKey.pvk',
        ENCRYPTION BY PASSWORD = 'TestEncryption123'
    )
GO
 
SELECT * FROM sys.certificates
 
 
ALTER TABLE Encrypt
ADD 
    encryptedCol varbinary (128)
GO
 
UPDATE Encrypt
SET
    encryptedCol = ENCRYPTBYCERT
    (
        CERT_ID('TestCertificate'),
        clearTextCol
    )
GO
 
 
DECLARE @Passwd nvarchar(128) = 'TestEncryption123'
SELECT intCol,
    clearTextCol,
    encryptedCol,
    CAST
    (
        DECRYPTBYCERT
        (
            CERT_ID('TestCertificate'),
            encryptedCol,
            @Passwd
        ) AS varchar(128)
    ) AS decryptedCol
FROM dbo.Encrypt

GO

制作了我的测试程序:

using System;
using System.Data.SqlClient;
 
namespace Testbeispiel_Encryption
{
    class Program
    {
        static void Main(string[] args)
        {
            //create a new instance of MyDecryptor
            MyDecryptor Decryptor = new MyDecryptor();
            //not recommended to store password as a cleartext (but for development purposes ;) 
            string password = "TestEncryption123";
            System.Security.SecureString securePassword = new System.Security.SecureString();
            //create Secure Password
            foreach (char keyChar in password.ToCharArray())
                securePassword.AppendChar(keyChar);
            //Insert Connection String (local because I am working on the Server)
            string constr = "DATA SOURCE=(local);INITIAL CATALOG=TestEncryptionDecryption;INTEGRATED SECURITY=SSPI;";
            using (SqlConnection con = new SqlConnection(constr))
            {
                //Open the connection to the initial catalog
                con.Open();
                //create SELECT statement
                string cmdstr1 = "SELECT intCol, clearTextCol, encryptedCol FROM dbo.Encrypt";
 
                using (SqlCommand cmd = new SqlCommand(cmdstr1, con))
                {
                    SqlDataReader reader = cmd.ExecuteReader();
                    //now we read data
                    while (reader.Read())
                    {
                        try
                        {
                            Console.Write(reader[0] + "\t" + reader[1] + "\t" + reader[2] + "\t" );
                            Console.WriteLine(Decryptor.DecryptDocIDWithFileCert(@"C:\temp\createCert\TestCertificate.pfx", securePassword, (byte[])reader[2]));
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine(e.ToString());
                            break;
                        }
                    }
                    reader.Close();
                }
                con.Close();
            }
            Console.ReadKey();
        }
    }
}

和我的测试解密器:

using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
namespace Testbeispiel_Encryption
{
    public class MyDecryptor
    {
        public MyDecryptor()
        {
            //TODO Constructor
        }
        ~MyDecryptor()
        {
            //TODO Destructor
        }
 
        ///<summary>
        ///Opens certificate and private Key, Decrypts Data and returns the decrypted DocID
        ///</summary>
        ///<param name="pfxFilePath">The Filepath to the Certificate (.pfx) File!</param>
        ///<param name="password">The password for accessing the pfx file</param>
        ///<param name="EncryptedDocID">the Value of the encrypted Collumn, Encrypted by the Certifiate of the pfx File</param>
        ///<returns>string DecryptedDocID - the decrypted Value of the DocID</returns>
        public string DecryptDocIDWithFileCert(string pfxFilePath, System.Security.SecureString password, byte[] EncryptedDocID)
        {
            //Reverse the encrypted DocID because SQL reverses the byte array on encryption
            //and so does RSACryptoServiceProvider. 
            Array.Reverse(EncryptedDocID);
            string DecryptedDocID = "";
 
            //load certificate from filesystem and open it with provided password
            X509Certificate2 cert = new X509Certificate2(pfxFilePath, password);
            if (cert == null)
            {
                throw new Exception("Certificate " + pfxFilePath + " Does not exist");
            }
 
            //Check if certificate has a private key, otherwhise we are unable to decrypt any message
            //encrypted with the certs public key so we throw an exception
            if (cert.HasPrivateKey)
            {
                RSACryptoServiceProvider RsaCSP = (RSACryptoServiceProvider)cert.PrivateKey;
                //actualy decrypt message with RSACryptoServiceProvider and set OAEP Padding to false
                //Don't know the reason, otherwhise it didn't work correctly 
                byte[] ret = RsaCSP.Decrypt(EncryptedDocID, false);
                if (ret == null)
                {
                    throw new Exception("Decryption with RSA failed");
                }
                //Encode DocID to UTF7 String. UTF8 didn't recognise umlauts
                DecryptedDocID = System.Text.Encoding.UTF7.GetString(ret);
            }
            else
            {
                throw new Exception("Certificate " + pfxFilePath + " has no Private Key; ");
            }
            return DecryptedDocID;
        }
    }
}

最后它起作用了!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

清除数据库

DROP CERTIFICATE TestCertificate
GO
 
DROP TABLE Encrypt
GO
 
USE master
GO
 
DROP DATABASE TestEncryptionDecryption
GO

 

于 2013-07-31T15:12:56.090 回答