在.Net中解密加密。我是如何做到的

时间:2013-07-24 15:03:10

标签: c# .net sql security cryptography

我想加密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'

我收到一条错误消息,指出私钥是通过主密码加密的。

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

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

我的新问题是:

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

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

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


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

1 个答案:

答案 0 :(得分: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
  
      
  • 是,导出私钥
  •   
  • 如果可能,请在证书路径中包含所有证书
  •   
  • 导出所有extendend属性
  •   

设置密码: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();
        }
    }
}

和我的Testdecryptor:

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;
        }
    }
}

AND FINALY IT WORKS !!!!!!!!!!!!!!!!!!!!!!!!!!!!!

清除数据库

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