加密 BouncyCastle RSA 密钥对并存储在 SQL2008 数据库中

发布于 2024-07-21 03:32:21 字数 1227 浏览 4 评论 0原文

我有一个生成 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 名称,而不是实际值。 我正在向此函数提交之前返回的keys.Private 密钥对。

另一个问题是,由于我没有加密公钥,我应该将其存储在 SQL2008 数据库中,nvarchar(256) 或其他格式?

任何帮助将不胜感激。

I have a function that generates a BouncyCastle RSA key pair. I need to encrypt the private key and then store the encrypted private and public keys into separate SQL2008 database fields.

I am using the following to get the keypair:

private static AsymmetricCipherKeyPair createASymRandomCipher()
{
    RsaKeyPairGenerator r = new RsaKeyPairGenerator();
    r.Init(new KeyGenerationParameters(new SecureRandom(), 1024));
    AsymmetricCipherKeyPair keys = r.GenerateKeyPair();
    return keys; 
}

This is returning the keys fine, but I am not sure how I can then encrypt the private key and subsequently store it in the database.

This is what I am currently using the encrypt the data (incorrectly?):

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

Obviously the keyBytes setting where I am converting keyParam.ToString() is not correct as it only converts the KeyParameter name, not the actual value. I am submitting to this function the previous key pair return of keys.Private.

The other question is as I am not encrypting the Public Key what format should I be storing this in the SQL2008 database, nvarchar(256) or other?

Any help would be greatly appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

轮廓§ 2024-07-28 03:32:22

出于应该明确的原因,默认(可能是无意的)序列化不能很好地与私钥配合使用,私钥只能在非常有限的情况下写出。

BouncyCastle 支持 PKCS#8,这是“序列化”私钥的相关标准。 有称为 PrivateKeyInfo 和 EncryptedPrivateKeyInfo 的 ASN.1 结构。 由于它们位于 ASN.1 中,因此有标准方法可以对它们进行序列化/反序列化。 顾名思义,一种以明文形式存储密钥,另一种根据密码对密钥进行加密。

对于公钥 - 通常不会对其进行加密。 BC支持SubjectPublicKeyInfo的X.509标准格式来序列化它们。

在 C# 版本中,要查看的高级类为:

  • Org.BouncyCastle.Security.PrivateKeyFactory
  • Org.BouncyCastle.Security.PublicKeyFactory
  • Org.BouncyCastle.Pkcs.EncryptedPrivateKeyInfoFactory
  • Org.BouncyCastle.Pkcs.PrivateKeyInfoFactory
  • Org.BouncyCastle.X509。主题公钥信息工厂

For reasons that should be clear, default (and perhaps inadvertent) serialization does not play well with private keys which should only be written out in very limited situations.

BouncyCastle has support for PKCS#8, which is the relevant standard for "serializing" private keys. There are ASN.1 structures called PrivateKeyInfo and EncryptedPrivateKeyInfo. Since they are in ASN.1 there are standard ways to serialize/deserialize them. As the name suggests, one stores the key in plaintext, the other encrypts the key based on a password.

For the public keys - these would not ordinarily be encrypted. BC supports the X.509 standard format of SubjectPublicKeyInfo for serializing them.

In the C# build, the high-level classes to look at would be:

  • Org.BouncyCastle.Security.PrivateKeyFactory
  • Org.BouncyCastle.Security.PublicKeyFactory
  • Org.BouncyCastle.Pkcs.EncryptedPrivateKeyInfoFactory
  • Org.BouncyCastle.Pkcs.PrivateKeyInfoFactory
  • Org.BouncyCastle.X509.SubjectPublicKeyInfoFactory
莳間冲淡了誓言ζ 2024-07-28 03:32:22

只要对象被标记为可序列化,将对象转换为字节数组的一种方法是使用.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;
}

这给了我们一对易于使用的可序列化对象。

AsymmetryCipherKeyPair 将公钥和私钥公开为 AmetryKeyParameter 对象。 要获取更详细的属性,您需要将它们转换为以下内容:

keyPair.Public 到 BouncyCastle.Crypto.Parameters.RsaKeyParameters
keyPair.Private 到 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();

反序列化正好与前面描述的相反。 一旦您反序列化了公共或私有结构,您就可以使用 BouncyCastle 构造函数来重新创建密钥。 这些函数证明了这一点。

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

希望这一切都有意义! 代码示例应该可以帮助您。

As long as the object is marked as serializable, one way to convert an object to a byte array is to use the BinaryFormatter class in .Net.

You will need to add this using statement to your code file:

using System.Runtime.Serialization.Formatters.Binary;

A binary formatter can output your class to a stream. As you intend to convert your object to a byte array, you can use a System.IO.MemoryStream as temporary storage.

MemoryStream memStream = new MemoryStream();

You can then create a new binary formatter.

BinaryFormatter formatter = new BinarryFomatter();

and use this to serialize your object.

formatter.Serialize(memStream, someObject);

To get the bytes you can use:

return memStream.ToArray();

To deserialize the byte array you need to write the bytes to a memory stream.

memStream.Write(arrBytes, 0, arrBytes.Length);

Return to the beginning of the stream.

memStream.Seek(0, SeekOrigin.Begin);

Then use the formatter to recreate the object.

Object obj = (Object)formatter.Deserialize(memStream);

If you are already using encryption functions you should be able to encrypt the created byte array quite easily before storing it in the database.

Hopefully that will help you in the right direction. If you are lucky, the BouncyCastle objects will be marked as serializable, if not you will need some extra code. Later, I will get a chance to look at the BouncyCastle librarys to be able to test this and will post more code if necessary.


... I have never used BouncyCastle before. After some testing, it appears that the public and private key objects are not serializable, so you will need to convert these objects into something that is!

It appears that the public and private keys expose properties as various BouncyCastle.Math.BigInteger values. (The keys can also be constructed from these BigIntegers). Further, BigIntegers have a ToByteArray() function and can also be constructed from a byte array. Very useful..

Knowing that you can break each key into BigIntegers and these in turn to a byte array and that the reverse is also possible, you a way to store all these in a serializable object. A simple struct or class would do e.g.

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

This gives us a pair of easy to use serializable objects.

The AsymmetricCipherKeyPair exposes the Public and Private keys as AsymmetricKeyParameter objects. To get at the more detailed properties you will need to cast these to the following:

keyPair.Public to BouncyCastle.Crypto.Parameters.RsaKeyParameters
keyPair.Private to BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters

The following functions will convert these to the structs to declared earlier:

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

Using the binary formatter mentioned earlier, we can convert the serializable objects we have just created to a byte array.

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 is then just the inverse as described earlier. Once you have either the public or private structs deserialized you can use the BouncyCastle contructors to recreate the keys. These functions demonstrate this.

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

If you need to recreate the original key pair for any reason:

AsymmetricKeyParameter publ = (AsymmetricKeyParameter)recreateASymCipherPublicKey(cKeyPair.publicKey);
AsymmetricKeyParameter priv = (AsymmetricKeyParameter)recreateASymCipherPrivateKey(cKeyPair.privateKey);
AsymmetricCipherKeyPair keyPair = new AsymmetricCipherKeyPair(publ, priv);

Hopefully that all makes sense! The code samples should help you on your way.

比忠 2024-07-28 03:32:22

正确的做法是采用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 方法已重载,以允许使用密码来保护密钥。

The correct approach is to use Peters' suggestion.

I have included a small C# code sample below :

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

The sample uses the Bouncy Castle API. Note that the sample does NOT encrypt the key. The CreatePrivateKeyInfo method is overloaded to allow the use of a password as protection of the key.

对你再特殊 2024-07-28 03:32:22

关于问题的第二部分,用于存储密钥的数据类型是 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,显然这是不行的(嗯,您可以实际上创建 SSL 连接到SQL 服务器...)。

Regarding the second part of your question, the data type that should be used for storing the key would be VARBINARY(256).

Back to the first part of your question, you actually have the option of having SQL Server handle the encryption for you. Granted, whether or not you would want to do this would be a matter of what your application requirements are, but I'll go over it in case it's an option.

We'll be pretty basic here and just use symmetric keys and Triple-DES.

First, the database has a master key which is used to protect certificates and asymmetric keys. The master key is encrypted with Triple-DES.

CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'supersecretpassword'  

SQL Server 2005/2008 can generate their own X.509 certificates used to protect the keys used to encrypt the actual data.

CREATE CERTIFICATE ExampleCertificate 
     WITH SUBJECT = 'thisisjustsomemetadata'

There are a lot of options for encrypting symmetric keys (certificates, passwords, other keys), as well as many supported algorithms. But for this example, we'll use our certificate.

CREATE SYMMETRIC KEY ExampleKey
     WITH ALGORITHM = TRIPLE_DES  
     ENCRYPTION BY CERTIFICATE EncryptTestCert 

The key needs to be decrypted using the same method with which it was encrypted. In our case, this would be the certificate we created.

 DECLARE @Value VARCHAR(50)
 SET @Value = 'supersecretdata!'

 OPEN SYMMETRIC KEY ExampleKey DECRYPTION BY CERTIFICATE ExampleCertificate  
     UPDATE SomeTable  
     SET SomeColumn = ENCRYPTBYKEY(KEY_GUID('ExampleKey'), @Value)

Decryption is just as straightforward.

OPEN SYMMETRIC KEY ExampleKey DECRYPTION BY CERTIFICATE ExampleCertificate  
     SELECT CONVERT(VARCHAR(50),DECRYPTBYKEY(SomeColumn)) AS DecryptedData 
     FROM SomeTable 

Hopefully this solved your problem, or at least opened you up to alternative solutions (though someone who's had experience doing encryption in C# apps could probably find the fault in your above code). If you have requirements that necessitate that the data can't even go over the wire to the SQL Server in plain-text, obviously this is a no-go (well, you can actually create SSL connections to SQL Server...).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文