加密BouncyCastle RSA密钥对并存储在SQL2008数据库中
我有一个生成BouncyCastle RSA密钥对的函数。 我需要加密私钥,然后将加密的私钥和公钥存储到单独的SQL2008数据库字段中。
我使用以下内容获取密钥对:
private static AsymmetricCipherKeyPair createASymRandomCipher() { RsaKeyPairGenerator r = new RsaKeyPairGenerator(); r.Init(new KeyGenerationParameters(new SecureRandom(), 1024)); AsymmetricCipherKeyPair keys = r.GenerateKeyPair(); return keys; }
这是正确的返回键,但我不知道如何加密私钥然后将其存储在数据库中。
这是我目前使用的加密数据(错误?):
public static byte[] encBytes2(AsymmetricKeyParameter keyParam, byte[] Key, byte[] IV) { MemoryStream ms = new MemoryStream(); Rijndael rjdAlg = Rijndael.Create(); rjdAlg.Key = Key; rjdAlg.IV = IV; CryptoStream cs = new CryptoStream(ms, rjdAlg.CreateEncryptor(), CryptoStreamMode.Write); byte[] keyBytes = System.Text.Encoding.Unicode.GetBytes(keyParam.ToString()); cs.Write(keyBytes, 0, keyBytes.Length); cs.Close(); byte[] encryptedData = ms.ToArray(); return encryptedData; }
显然,我转换keyParam.ToString()的keyBytes设置不正确,因为它只转换KeyParameter名称,而不是实际值。 我正在向此函数提交以前的密钥对返回密钥。私有。
另一个问题是因为我没有加密公钥我应该在SQL2008数据库,nvarchar(256)或其他中存储什么格式?
任何帮助将不胜感激。
由于应该清楚的原因,默认(也许是无意的)序列化不能很好地与私钥一起使用,私钥只应在非常有限的情况下写出。
BouncyCastle支持PKCS#8,这是“序列化”私钥的相关标准。 有一些名为PrivateKeyInfo和EncryptedPrivateKeyInfo的ASN.1结构。 由于它们在ASN.1中,因此有标准方法可以序列化/反序列化它们。 顾名思义,一个用密钥存储密钥,另一个用密码加密密钥。
对于公钥 – 这些通常不会被加密。 BC支持用于序列化它们的X.509标准格式的SubjectPublicKeyInfo。
在C#build中,要查看的高级类将是:
- Org.BouncyCastle.Security.PrivateKeyFactory
- Org.BouncyCastle.Security.PublicKeyFactory
- Org.BouncyCastle.Pkcs.EncryptedPrivateKeyInfoFactory
- Org.BouncyCastle.Pkcs.PrivateKeyInfoFactory
- Org.BouncyCastle.X509.SubjectPublicKeyInfoFactory
只要对象被标记为可序列化,将对象转换为字节数组的一种方法是使用.Net中的BinaryFormatter类。
您需要将此using语句添加到您的代码文件中:
using System.Runtime.Serialization.Formatters.Binary;
二进制格式化程序可以将您的类输出到流。 当您打算将对象转换为字节数组时,可以使用System.IO.MemoryStream作为临时存储。
MemoryStream memStream = new MemoryStream();
然后,您可以创建新的二进制格式化程序。
BinaryFormatter formatter = new BinarryFomatter();
并使用它来序列化您的对象。
formatter.Serialize(memStream, someObject);
要获取可以使用的字节:
return memStream.ToArray();
要反序列化字节数组,需要将字节写入内存流。
memStream.Write(arrBytes, 0, arrBytes.Length);
返回到流的开头。
memStream.Seek(0, SeekOrigin.Begin);
然后使用格式化程序重新创建对象。
Object obj = (Object)formatter.Deserialize(memStream);
如果您已经在使用加密函数,则应该能够在将创建的字节数组存储到数据库之前非常轻松地对其进行加密。
希望这能帮助您朝着正确的方向前进。 如果幸运的话,BouncyCastle对象将被标记为可序列化,否则您将需要一些额外的代码。 稍后,我将有机会查看BouncyCastle库以便能够对此进行测试,并在必要时发布更多代码。
……我之前从未使用过BouncyCastle。 经过一些测试,看起来公钥和私钥对象都不是可序列化的,所以你需要将这些对象转换成这样的东西!
公钥和私钥似乎将属性公开为各种BouncyCastle.Math.BigInteger值。 (键也可以从这些BigIntegers构建)。 此外,BigIntegers具有ToByteArray()函数,也可以从字节数组构造。 很有用..
知道你可以将每个键分解为BigIntegers,然后将这些键分解为一个字节数组,反之亦然,你可以将所有这些存储在一个可序列化的对象中。 一个简单的结构或类可以做例如
[Serializable] private struct CipherPrivateKey { public byte[] modulus; public byte[] publicExponent; public byte[] privateExponent; public byte[] p; public byte[] q; public byte[] dP; public byte[] dQ; public byte[] qInv; } [Serializable] private struct CipherPublicKey { public bool isPrivate; public byte[] modulus; public byte[] exponent; }
这为我们提供了一对易于使用的可序列化对象。
AsymmetricCipherKeyPair将公钥和私钥公开为AsymmetricKeyParameter对象。 要获得更详细的属性,您需要将这些属性转换为以下内容:
keyPair.Public to BouncyCastle.Crypto.Parameters.RsaKeyParameters keyPair.Private to BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters
以下函数将这些函数转换为先前声明的结构:
private static CipherPublicKey getCipherPublicKey(Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters cPublic) { CipherPublicKey cpub = new CipherPublicKey(); cpub.modulus = cPublic.Modulus.ToByteArray(); cpub.exponent = cPublic.Exponent.ToByteArray(); return cpub; } private static CipherPrivateKey getCipherPrivateKey(Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters cPrivate) { CipherPrivateKey cpri = new CipherPrivateKey(); cpri.dP = cPrivate.DP.ToByteArray(); cpri.dQ = cPrivate.DQ.ToByteArray(); cpri.modulus = cPrivate.Modulus.ToByteArray(); cpri.p = cPrivate.P.ToByteArray(); cpri.privateExponent = cPrivate.Exponent.ToByteArray(); cpri.publicExponent = cPrivate.PublicExponent.ToByteArray(); cpri.q = cPrivate.Q.ToByteArray(); cpri.qInv = cPrivate.QInv.ToByteArray(); return cpri; }
使用前面提到的二进制格式化器,我们可以将刚创建的可序列化对象转换为字节数组。
CipherPublicKey cpub = getCipherPublicKey((Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters)keypair.Public); MemoryStream memStream = new MemoryStream(); BinaryFormatter formatter = new BinarryFomatter(); formatter.Serialize(memStream, cpub); return memStream.ToArray();
如前所述,Desierializing就是反向。 一旦你有反序列化的公共或私有结构,你可以使用BouncyCastle结构重新创建密钥。 这些functioncertificate了这一点
private static Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters recreateASymCipherPublicKey(CipherPublicKey cPublicKey) { Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters key; key = new Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters( cPublicKey.isPrivate, createBigInteger(cPublicKey.modulus), createBigInteger(cPublicKey.exponent)); return key; } private static Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters recreateASymCipherPrivateKey(CipherPrivateKey cPrivateKey) { Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters key; key = new Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters( createBigInteger(cPrivateKey.modulus), createBigInteger(cPrivateKey.publicExponent), createBigInteger(cPrivateKey.privateExponent), createBigInteger(cPrivateKey.p), createBigInteger(cPrivateKey.q), createBigInteger(cPrivateKey.dP), createBigInteger(cPrivateKey.dQ), createBigInteger(cPrivateKey.qInv)); return key; }
如果您因任何原因需要重新创建原始密钥对:
AsymmetricKeyParameter publ = (AsymmetricKeyParameter)recreateASymCipherPublicKey(cKeyPair.publicKey); AsymmetricKeyParameter priv = (AsymmetricKeyParameter)recreateASymCipherPrivateKey(cKeyPair.privateKey); AsymmetricCipherKeyPair keyPair = new AsymmetricCipherKeyPair(publ, priv);
希望一切都有意义! 代码示例应该可以帮助您。
正确的方法是使用Peters的建议。
我在下面添加了一个小的C#代码示例:
var keyPair = GetKeypair(); PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private); byte[] serializedKey = privateKeyInfo.ToAsn1Object().GetDerEncoded(); AsymmetricKeyParameter deserializedKey1 = PrivateKeyFactory.CreateKey(serializedKey); Assert.AreEqual(keyPair.Private, deserializedKey1); AsymmetricKeyParameter deserializedKey2 = PrivateKeyFactory.CreateKey(privateKeyInfo); Assert.AreEqual(keyPair.Private, deserializedKey2);
该示例使用Bouncy Castle API。 请注意 ,该示例不会加密密钥。 CreatePrivateKeyInfo
方法被重载以允许使用密码作为密钥的保护。
关于问题的第二部分,应该用于存储密钥的数据类型是VARBINARY(256)。
回到问题的第一部分,您实际上可以选择让SQL Server为您处理加密。 当然,您是否愿意这样做将取决于您的应用要求是什么,但是如果它是一个选项,我会仔细检查它。
我们在这里非常基础,只需使用对称密钥和Triple-DES。
首先,数据库有一个主密钥,用于保护证书和非对称密钥。 主密钥使用Triple-DES加密。
CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'supersecretpassword'
SQL Server 2005/2008可以生成自己的X.509证书,用于保护用于加密实际数据的密钥。
CREATE CERTIFICATE ExampleCertificate WITH SUBJECT = 'thisisjustsomemetadata'
加密对称密钥(证书,密码,其他密钥)以及许多支持的算法有很多选项。 但是对于这个例子,我们将使用我们的证书。
CREATE SYMMETRIC KEY ExampleKey WITH ALGORITHM = TRIPLE_DES ENCRYPTION BY CERTIFICATE EncryptTestCert
密钥需要使用与其加密相同的方法进行解密。 在我们的例子中,这将是我们创建的证书。
DECLARE @Value VARCHAR(50) SET @Value = 'supersecretdata!' OPEN SYMMETRIC KEY ExampleKey DECRYPTION BY CERTIFICATE ExampleCertificate UPDATE SomeTable SET SomeColumn = ENCRYPTBYKEY(KEY_GUID('ExampleKey'), @Value)
解密也同样简单。
OPEN SYMMETRIC KEY ExampleKey DECRYPTION BY CERTIFICATE ExampleCertificate SELECT CONVERT(VARCHAR(50),DECRYPTBYKEY(SomeColumn)) AS DecryptedData FROM SomeTable
希望这能解决您的问题,或者至少让您接触到其他解决方案(尽管在C#应用程序中有过加密经验的人可能会在上面的代码中发现错误)。 如果您的要求需要数据甚至无法通过纯文本连接到SQL Server,显然这是不行的(好吧,您实际上可以创建到SQL Server的SSL连接……)。