Шифрование пары ключей BouncyCastle RSA и сохранение в базе данных SQL2008

StackOverflow https://stackoverflow.com/questions/844997

Вопрос

У меня есть функция, которая генерирует пару ключей 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() неверен, поскольку он преобразует только имя ключевого параметра, а не фактическое значение.Я отправляю этой функции возврат предыдущей пары ключей keys.Частное.

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

Мы были бы очень признательны за любую помощь.

Это было полезно?

Решение

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

BouncyCastle поддерживает PKCS # 8, который является соответствующим стандартом для "сериализации" закрытых ключей.Существуют структуры ASN.1, называемые PrivateKeyInfo и EncryptedPrivateKeyInfo.Поскольку они находятся в ASN.1, существуют стандартные способы их сериализации / десериализации.Как следует из названия, один хранит ключ в виде открытого текста, другой шифрует ключ на основе пароля.

Что касается открытых ключей - обычно они не шифруются.BC поддерживает стандартный формат X.509 SubjectPublicKeyInfo для их сериализации.

В сборке C # высокоуровневыми классами для рассмотрения будут:

  • Орг.Надувной замок.Безопасность.Частная фабрика ключей
  • Орг.Надувной замок.Безопасность.Публичная фабрика
  • Org.BouncyCastle.Pkcs.Зашифрованный личный ключ на фабрике
  • Org.BouncyCastle.Pkcs.PrivateKeyInfoFactory
  • Org.BouncyCastle.X509.Subjectpublickeyинформационная фабрика

Другие советы

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

Вам нужно будет добавить этот оператор 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.Чтобы получить доступ к более подробным свойствам, вам нужно будет преобразовать их в следующие:

Ключевая пара.Общедоступная для BouncyCastle.Криптография.Параметры.RsaKeyParameters Ключевая пара.Приватная для BouncyCastle.Криптография.Параметры.rsaprivatecrtkeyпараметры

Следующие функции преобразуют их в структуры, объявленные ранее:

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top