Question

J'ai une fonction qui génère une paire de clés BouncyCastle RSA. Je dois chiffrer la clé privée, puis stocker les clés privée et publique chiffrées dans des champs de base de données SQL2008 distincts.

J'utilise les éléments suivants pour obtenir la paire de clés:

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

Cela retourne bien les clés, mais je ne sais pas comment je peux ensuite chiffrer la clé privée et la stocker ensuite dans la base de données.

C’est ce que j’utilise actuellement pour chiffrer les données (incorrectement?):

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

Évidemment, le paramètre keyBytes où je convertis keyParam.ToString () n’est pas correct car il ne fait que convertir le nom KeyParameter, pas la valeur réelle. Je soumets à cette fonction le retour de la paire de clés précédente.Privé.

L’autre question est de ne pas chiffrer la clé publique. Quel format dois-je stocker dans la base de données SQL2008, nvarchar (256) ou autre?

Toute aide serait grandement appréciée.

Était-ce utile?

La solution

Pour des raisons qui devraient être claires, la sérialisation par défaut (et peut-être par inadvertance) ne fonctionne pas bien avec les clés privées qui ne doivent être écrites que dans des situations très limitées.

BouncyCastle prend en charge PKCS # 8, qui est le standard approprié pour & "; sérialiser &"; clés privées. Il existe des structures ASN.1 appelées PrivateKeyInfo et EncryptedPrivateKeyInfo. Comme ils sont en ASN.1, il existe des méthodes standard pour les sérialiser / désérialiser. Comme son nom l'indique, l'un stocke la clé en texte brut, l'autre le chiffre en fonction d'un mot de passe.

Pour les clés publiques, elles ne seraient généralement pas chiffrées. BC prend en charge le format standard X.509 de SubjectPublicKeyInfo pour leur sérialisation.

Dans la construction C #, les classes de haut niveau à examiner seraient:

  • Org.BouncyCastle.Security.PrivateKeyFactory
  • Org.BouncyCastle.Security.PublicKeyFactory
  • Org.BouncyCastle.Pkcs.EncryptedPrivateKeyInfoFactory
  • Org.BouncyCastle.Pkcs.PrivateKeyInfoFactory
  • Org.BouncyCastle.X509.SubjectPublicKeyInfoFactory

Autres conseils

Tant que l'objet est marqué comme sérialisable, un moyen de convertir un objet en tableau d'octets consiste à utiliser la classe BinaryFormatter en .Net.

Vous devrez ajouter cette instruction using à votre fichier de code:

using System.Runtime.Serialization.Formatters.Binary;

Un formateur binaire peut exporter votre classe dans un flux. Lorsque vous envisagez de convertir votre objet en tableau d'octets, vous pouvez utiliser System.IO.MemoryStream comme stockage temporaire.

MemoryStream memStream = new MemoryStream();

Vous pouvez ensuite créer un nouveau formateur binaire.

BinaryFormatter formatter = new BinarryFomatter();

et utilisez-le pour sérialiser votre objet.

formatter.Serialize(memStream, someObject);

Pour obtenir les octets, vous pouvez utiliser:

return memStream.ToArray();

Pour désérialiser le tableau d'octets, vous devez écrire les octets dans un flux de mémoire.

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

Retour au début du flux.

memStream.Seek(0, SeekOrigin.Begin);

Utilisez ensuite le formateur pour recréer l'objet.

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

Si vous utilisez déjà des fonctions de chiffrement, vous devriez pouvoir chiffrer assez facilement le tableau d'octets créé avant de le stocker dans la base de données.

J'espère que cela vous aidera dans la bonne direction. Si vous êtes chanceux, les objets BouncyCastle seront marqués comme sérialisables, sinon vous aurez besoin de code supplémentaire. Plus tard, je pourrai consulter les bibliothèques de BouncyCastle pour pouvoir le tester et posterai plus de code si nécessaire.

... Je n'ai jamais utilisé BouncyCastle auparavant. Après quelques tests, il apparaît que les objets de clé publique et privée ne sont pas sérialisables. Vous devrez donc convertir ces objets en quelque chose de nouveau!

Il semble que les clés publique et privée exposent les propriétés sous différentes valeurs BouncyCastle.Math.BigInteger. (Les clés peuvent également être construites à partir de ces BigIntegers). En outre, les BigIntegers ont une fonction ToByteArray () et peuvent également être construits à partir d'un tableau d'octets. Très utile ..

Sachant que vous pouvez diviser chaque clé en BigIntegers et que ceux-ci se transforment ensuite en un tableau d'octets et que l'inverse est également possible, vous pouvez les stocker dans un objet sérialisable. Une structure simple ou une classe ferait par exemple.

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

Cela nous donne une paire d’objets sérialisables faciles à utiliser.

AsymmetricCipherKeyPair expose les clés publique et privée en tant qu’objets AsymmetricKeyParameter. Pour obtenir les propriétés plus détaillées, vous devrez les convertir comme suit:

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

Les fonctions suivantes les convertiront en structures déclarées précédemment:

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

En utilisant le formateur binaire mentionné précédemment, nous pouvons convertir les objets sérialisables que nous venons de créer en un tableau d'octets.

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

La désierialisation n’est alors que l’inverse décrit précédemment. Une fois que vous avez désérialisé les structures publiques ou privées, vous pouvez utiliser les constructeurs BouncyCastle pour recréer les clés. Ces fonctions le démontrent.

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

Si vous devez recréer la paire de clés d'origine pour une raison quelconque:

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

Espérons que tout a du sens! Les exemples de code devraient vous aider dans votre cheminement.

La bonne approche consiste à utiliser la suggestion de Peters.

J'ai inclus un petit exemple de code C # ci-dessous:

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

L'exemple utilise l'API Bouncy Castle. Notez que l'exemple NE chiffre PAS la clé. La méthode CreatePrivateKeyInfo est surchargée pour permettre l’utilisation d’un mot de passe comme protection de la clé.

En ce qui concerne la deuxième partie de votre question, le type de données à utiliser pour stocker la clé serait VARBINARY (256).

Retour à la première partie de votre question, vous avez en fait la possibilité de laisser SQL Server gérer le cryptage pour vous. Certes, que vous souhaitiez ou non procéder, cela dépend des exigences de votre demande, mais je vais y revenir au cas où ce serait une option.

Nous serons assez basiques ici et nous n’utiliserons que des clés symétriques et Triple-DES.

Tout d’abord, la base de données possède une clé principale utilisée pour protéger les certificats et les clés asymétriques. La clé principale est cryptée avec Triple-DES.

CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'supersecretpassword'  

SQL Server 2005/2008 peut générer ses propres certificats X.509 utilisés pour protéger les clés utilisées pour chiffrer les données réelles.

CREATE CERTIFICATE ExampleCertificate 
     WITH SUBJECT = 'thisisjustsomemetadata'

Il existe de nombreuses options pour chiffrer les clés symétriques (certificats, mots de passe, autres clés), ainsi que de nombreux algorithmes pris en charge. Mais pour cet exemple, nous utiliserons notre certificat.

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

La clé doit être déchiffrée à l'aide de la même méthode que celle avec laquelle elle a été chiffrée. Dans notre cas, il s’agit du certificat que nous avons créé.

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

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

Le décryptage est tout aussi simple.

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

Espérons que cela résoudra votre problème, ou du moins vous ouvrira la voie à des solutions alternatives (bien qu'une personne expérimentée dans le cryptage d'applications C # puisse probablement trouver la faute dans votre code ci-dessus). Si vous avez des exigences imposant que les données ne puissent même pas être transmises en texte brut à SQL Server, ceci est évidemment une solution de rechange (eh bien, vous pouvez créer réellement des connexions SSL avec SQL Server ...).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top