Шифрование пары ключей BouncyCastle RSA и сохранение в базе данных SQL2008 - PullRequest
5 голосов
/ 10 мая 2009

У меня есть функция, которая генерирует пару ключей 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;
}

Очевидно, настройка keyBytes, в которой я конвертирую keyParam.ToString (), неверна, поскольку она только конвертирует имя KeyParameter, а не фактическое значение. Я передаю этой функции предыдущую пару ключей возврата ключей. Частный.

Другой вопрос, поскольку я не шифрую Публичный ключ, в каком формате его следует хранить в базе данных SQL2008, nvarchar (256) или другом?

Любая помощь будет принята с благодарностью.

Ответы [ 4 ]

17 голосов
/ 02 февраля 2010

По понятным причинам сериализация по умолчанию (и, возможно, непреднамеренная) не подходит для закрытых ключей, которые должны записываться только в очень ограниченных ситуациях.

BouncyCastle поддерживает PKCS # 8, который является соответствующим стандартом для "сериализации" закрытых ключей. Существуют структуры ASN.1, которые называются PrivateKeyInfo и EncryptedPrivateKeyInfo. Поскольку они есть в 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.SubjectPublicKeyInfoFactory
11 голосов
/ 15 мая 2009

Пока объект помечен как сериализуемый, одним из способов преобразования объекта в байтовый массив является использование класса BinaryFormatter в .Net.

Вам необходимо добавить это с помощью оператора в ваш файл кода:

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

Надеюсь, все это имеет смысл! Примеры кода должны помочь вам на вашем пути.

6 голосов
/ 28 сентября 2010

Правильный подход - использовать предложение Петерса.

Я включил небольшой пример кода 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);

В примере используется API Bouncy Castle. Обратите внимание , что образец НЕ шифрует ключ. Метод CreatePrivateKeyInfo перегружен, что позволяет использовать пароль в качестве защиты ключа.

1 голос
/ 11 мая 2009

Что касается второй части вашего вопроса, тип данных, который следует использовать для хранения ключа, будет 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 Сервер ...).

...