我想加密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和自制证书解密数据?
答案 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