BouncyCastle RSA キー ペアの暗号化と SQL2008 データベースへの保存
-
20-08-2019 - |
質問
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 の以前のキー ペアの戻りをこの関数に送信しています。
もう1つの質問は、公開キーを暗号化していないので、これをSQL2008データベースにどの形式で保存する必要があるか、nvarchar(256)などです。
ご協力をいただければ幸いです。
解決
明らかな理由により、デフォルトの (そしておそらくは不注意による) シリアル化は、非常に限られた状況でのみ書き出されるはずの秘密キーでは適切に機能しません。
BouncyCastle は、秘密キーを「シリアル化」するための関連標準である PKCS#8 をサポートしています。PrivateKeyInfo および EncryptedPrivateKeyInfo と呼ばれる ASN.1 構造があります。これらは ASN.1 にあるため、シリアル化/逆シリアル化する標準的な方法があります。名前が示すように、1 つはキーを平文で保存し、もう 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
他のヒント
限りオブジェクトがシリアライズ可能としてマークされているように、バイト配列にオブジェクトを変換する1つの方法は、.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のlibrarysを見る機会を得るでしょうし、必要に応じてより多くのコードを投稿します。
<時間>...私は前にはBouncyCastleを使用したことがありません。いくつかのテストの後、あなたが何かにこれらのオブジェクトを変換する必要がありますので、公開鍵と秘密鍵オブジェクトは、直列化可能でないことが表示されます!
公開鍵と秘密鍵は、様々なBouncyCastle.Math.BigInteger値などのプロパティを公開することが表示されます。 (キーはまた、これらのBigIntegerから構成することができます)。さらに、のBigIntegerはToByteArray()関数を持っており、また、バイト配列から構築することができます。非常に便利です..
あなたはバイト配列に順番に各のBigIntegerにキーとこれらを破ることができることと逆直列化可能なオブジェクトで、これらすべてを格納するための、あなたの方法でも可能であることを知ります。シンプルな構造体やクラス例えばを行うだろう。
[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 BouncyCastle.Crypto.Parameters.RsaKeyParametersへ BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParametersにkeyPair.Private
以下の機能は、以前の宣言に、構造体にこれらを変換します。
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のcontructorsを使用することができます。これらの関数は、このことを実証しています。
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);
うまくいけば、すべてが理にかなっていること!コードサンプルは、あなたの方法であなたを助ける必要があります。
正しいアプローチはピーターズの提案を使用することです。
Iは以下の小さな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を使用しています。サンプルは、キーを暗号化しないことを注。 CreatePrivateKeyInfo
方法は、キーの保護などのパスワードの使用を許可するようにオーバーロードされます。
あなたの質問の後半部分に関しては、キーを格納するために使用されるべきデータ型はVARBINARY(256)になります。
戻るあなたの質問の最初の部分に、あなたは実際にSQL Serverがあなたのための暗号化を扱うことのオプションがあります。あなたがアプリケーションの要件が何であるかの問題だろうこれをしたいだろうが、私はそれはオプションだ場合にはその上に行くつもりかどうか、確かに。
ここではかなり基本的なことだけ対称鍵とトリプルDESを使用します。
まず、データベースには、証明書と非対称キーを保護するために使用されたマスターキーを持っています。マスターキーはトリプル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の...)。