Importer une clé publique d'un autre endroit à CngKey?

je suis à la recherche d'un moyen multiplateformes pour partager les clés publiques pour la signature ECDSA. J'ai eu une bonne chose allant d'une perspective de performance avec CngKey et la norme. bibliothèques crypto de réseau, mais alors je ne pouvais pas comprendre comment une clé publique de 33 (ou 65) octets (en utilisant secp256r1 / P256) a été transformé en 104 octets par MS.. Par conséquent, je ne pouvais pas supporter la signature et la vérification de plateforme croisée..

j'utilise le BouncyCastle maintenant, mais la Sainte handgranade est lente!

Donc, à la recherche pour des suggestions pour les exigences suivantes:

  1. multiplateforme/langues (le serveur est .NET, mais il est servi via un JSON / Web.Interface de l'API)
    • JavaScript, Ruby,Python, C++ etc..
  2. Pas fou comme lent sur le serveur
  3. pas si douloureusement les gens lents ne peuvent pas l'utiliser sur le client.

le client doit pouvoir signer le message, le serveur doit pouvoir valider la signature avec un clé publique échangée lors de l'inscription au service.

de toute façon, les idées seraient géniales... Merci

14
demandé sur samoz 2014-06-17 00:05:14

3 réponses

J'ai donc trouvé le format D'une clé CN exportée en ECCPublicKeyBlob et ECCPrivateKeyBlob. Cela devrait permettre à d'autres de s'interopérer entre d'autres formats clés et CngKey pour la signature de courbes Elliptcales et autres.

ECCPrivateKeyBlob est formaté (pour P256) comme suit

  • [type de clé (4 octets)][longueur de la clé (4 octets)][clé publique (64 octets)][clé privée (32 octets)]
  • type de clé dans HEX est 45-43-53-32
  • longueur de la clé en HEX est 20-00-00-00
  • la clé publique est le format non compressé moins le premier octet (qui est toujours 04 pour signifier une clé non compressée dans d'autres bibliothèques)

ECCPublicKeyBlob est formaté (P256) comme suit

  • [type de clé (4 octets)][longueur de la clé (4 octets)][clé publique (64 octets)]
  • type de clé dans HEX est 45-43-53-31
  • la longueur de la clé dans L'HEX est 20-00-00-00
  • la clé publique est le format non comprimé moins le Byte de tête (qui est toujours 04 pour signifier une clé non compressée dans d'autres bibliothèques)

donc avec une clé publique non compressée en Hex d'une autre langue, vous pouvez couper le premier octet, ajouter ces 8 octets à l'avant et l'importer en utilisant

CngKey.Import(key,CngKeyBlobFormat.EccPrivateBlob);

Remarque:: le format de BLOB clé est documenté par Microsoft.

le TYPE et la longueur de la clé sont définis dans BCRYPT_ECCKEY_BLOB struct:

{ ulong Magic; ulong cbKey; }

ECC la clé publique de la mémoire format:

BCRYPT_ECCKEY_BLOB
BYTE X[cbKey] // Big-endian.
BYTE Y[cbKey] // Big-endian.

clé privée ECC mémoire format:

BCRYPT_ECCKEY_BLOB
BYTE X[cbKey] // Big-endian.
BYTE Y[cbKey] // Big-endian.
BYTE d[cbKey] // Big-endian.

les valeurs magiques disponibles dans .NET sont en Official Github dotnet/corefx BCrypt/Interop.Les Blobs.

internal enum KeyBlobMagicNumber : int
{
    BCRYPT_ECDH_PUBLIC_P256_MAGIC = 0x314B4345,
    BCRYPT_ECDH_PRIVATE_P256_MAGIC = 0x324B4345,
    BCRYPT_ECDH_PUBLIC_P384_MAGIC = 0x334B4345,
    BCRYPT_ECDH_PRIVATE_P384_MAGIC = 0x344B4345,
    BCRYPT_ECDH_PUBLIC_P521_MAGIC = 0x354B4345,
    BCRYPT_ECDH_PRIVATE_P521_MAGIC = 0x364B4345,
    BCRYPT_ECDSA_PUBLIC_P256_MAGIC = 0x31534345,
    BCRYPT_ECDSA_PRIVATE_P256_MAGIC = 0x32534345,
    BCRYPT_ECDSA_PUBLIC_P384_MAGIC = 0x33534345,
    BCRYPT_ECDSA_PRIVATE_P384_MAGIC = 0x34534345
    BCRYPT_ECDSA_PUBLIC_P521_MAGIC = 0x35534345,
    BCRYPT_ECDSA_PRIVATE_P521_MAGIC = 0x36534345,
    ...
    ...
}
24
répondu Josh Handel 2016-07-08 06:36:13

grâce à vous, j'ai pu importer une clé publique ECDSA_P256 à partir d'un certificat avec ce code:

    private static CngKey ImportCngKeyFromCertificate(X509Certificate2 cert)
    {
        var keyType = new byte[] {0x45, 0x43, 0x53, 0x31};
        var keyLength = new byte[] {0x20, 0x00, 0x00, 0x00};

        var key = cert.PublicKey.EncodedKeyValue.RawData.Skip(1);

        var keyImport = keyType.Concat(keyLength).Concat(key).ToArray();

        var cngKey = CngKey.Import(keyImport, CngKeyBlobFormat.EccPublicBlob);
        return cngKey;
    }

les touches de 65 octets (clé publique seulement) commencent par 0x04 qui doit être enlevé. Alors l'en-tête que vous avez décrit est ajouté.

ensuite, j'ai été en mesure de vérifier une signature comme ça:

var crypto = ECDsaCng(cngKey);
var verify = crypto.VerifyHash(hash, sig);
4
répondu andreas 2015-10-23 18:58:56

j'ai juste pensé que je voudrais dire merci aux deux postes ci-dessus car il m'a énormément aidé. J'ai dû vérifier une signature en utilisant la clé publique RSA en utilisant L'objet RSACng. J'utilisais le RSACryptoServiceProvider avant, mais ce N'est pas compatible FIPS, donc j'ai eu quelques problèmes pour passer à RSACng. Il nécessite également .NET 4.6. Voici comment je l'ai fait fonctionner en utilisant les affiches ci-dessus comme exemple:

                    // This structure is as the header for the CngKey
                    // all should be byte arrays in Big-Endian order
                    //typedef struct _BCRYPT_RSAKEY_BLOB {
                    //  ULONG Magic; 
                    //  ULONG BitLength; 
                    //  ULONG cbPublicExp;
                    //  ULONG cbModulus;
                    //  ULONG cbPrime1;  private key only
                    //  ULONG cbPrime2;  private key only
                    //} BCRYPT_RSAKEY_BLOB;

                    // This is the actual Key Data that is attached to the header
                    //BCRYPT_RSAKEY_BLOB
                    //  PublicExponent[cbPublicExp] 
                    //  Modulus[cbModulus]

                    //first get the public key from the cert (modulus and exponent)
                    // not shown
                    byte[] publicExponent = <your public key exponent>; //Typically equal to from what I've found: {0x01, 0x00, 0x01}
                    byte[] btMod = <your public key modulus>;  //for 128 bytes for 1024 bit key, and 256 bytes for 2048 keys

                    //BCRYPT_RSAPUBLIC_MAGIC = 0x31415352,
                    // flip to big-endian
                    byte[] Magic = new byte[] { 0x52, 0x53, 0x41, 0x31}; 

                    // for BitLendth: convert the length of the key's Modulus as a byte array into bits,
                    // so the size of the key, in bits should be btMod.Length * 8. Convert to a DWord, then flip for Big-Endian 
                    // example 128 bytes = 1024 bits = 0x00000400 = {0x00, 0x00, 0x04, 0x00} = flipped {0x00, 0x04, 0x00, 0x00}
                    // example 256 bytes = 2048 bits = 0x00000800 = {0x00, 0x00, 0x08, 0x00} = flipped {0x00, 0x08, 0x00, 0x00}
                    string sHex = (btMod.Length * 8).ToString("X8");
                    byte[] BitLength = Util.ConvertHexStringToByteArray(sHex);
                    Array.Reverse(BitLength); //flip to Big-Endian

                    // same thing for exponent length (in bytes)
                    sHex = (publicExponent.Length).ToString("X8");
                    byte[] cbPublicExp = Util.ConvertHexStringToByteArray(sHex);
                    Array.Reverse(cbPublicExp);

                    // same thing for modulus length (in bytes)
                    sHex = (btMod.Length).ToString("X8");
                    byte[] cbModulus = Util.ConvertHexStringToByteArray(sHex);
                    Array.Reverse(cbModulus);                      

                    // add the 0 bytes for cbPrime1 and cbPrime2 (always zeros for public keys, these are used for private keys, but need to be zero here)
                    // just make one array with both 4 byte primes as zeros
                    byte[] cbPrimes = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

                    //combine all the parts together into the one big byte array in the order the structure
                    var keyImport = Magic.Concat(BitLength).Concat(cbPublicExp).Concat(cbModulus).Concat(cbPrimes).Concat(publicExponent).Concat(btMod).ToArray();

                    var cngKey = CngKey.Import(keyImport, CngKeyBlobFormat.GenericPublicBlob);

                    // pass the key to the class constructor
                    RSACng rsa = new RSACng(cngKey);

                    //verify: our randomly generated M (message) used to create the signature (not shown), the signature, enum for SHA256, padding
                    verified = rsa.VerifyData(M, signature, HashAlgorithmName.SHA256,RSASignaturePadding.Pkcs1);

Note: le byte de signe pour le module (0x00) peut soit être inclus dans le module ou pas, donc la longueur sera un plus grand si elle est incluse. CNGkey semble s'en occuper de toute façon.

1
répondu jrussID 2016-10-14 18:33:02