Импорт открытого ключа для CngKey?
Я ищу кросс-платформенный способ обмена открытыми ключами для подписания ECDSA. У меня было большое дело с точки зрения производительности с CngKey и стандартными криптографическими библиотеками .NET, но тогда я не мог понять, как 33 (или 65) байтовый открытый ключ (используя secp256r1 / P256) превращался в 104 байта MS.. Следовательно, я не мог поддерживать кросс-платформенную подпись и проверку..
Я использую BouncyCastle сейчас, но holy handgranade это медленно!
Так, ищем предложения по следующим требованиям:
- кросс-платформенные / языки (сервер .NET, но это подается через JSON / Web.API-интерфейс )
- JavaScript, Ruby, Python, C++ и т. д..
- не сумасшедший, как медленно на сервере
- Не так болезненно медленно, люди не могут использовать его на клиенте.
клиент должен иметь возможность подписать сообщение, сервер должен иметь возможность проверить подпись с открытым ключом, который был обменен при регистрации на сервис.
в любом случае, идеи были бы потрясающими... Спасибо
3 ответов
Итак, я выяснил формат CngKey, экспортируемого в ECCPublicKeyBlob и ECCPrivateKeyBlob. Это должно позволить другим, чтобы взаимодействие между другими основными форматами и CngKey для Elliptcal подписания кривая и такие.
ECCPrivateKeyBlob отформатирован (для P256) следующим образом
- [тип ключа (4 байта)] [длина ключа (4 байта)] [открытый ключ (64 байта)] [закрытый ключ (32 байта)]
- тип ключа в HEX 45-43-53-32
- длина ключа в HEX 20-00-00-00
- открытый ключ-это несжатый формат минус ведущий байт (который всегда 04 для обозначения несжатого ключа в других библиотеках)
ECCPublicKeyBlob отформатирован (для P256) следующим образом
- [тип ключа (4 байта)] [длина ключа (4 байта)] [открытый ключ (64 байта)]
- тип ключа в HEX 45-43-53-31
- длина ключа в HEX 20-00-00-00
- открытый ключ - это несжатый формат минус ведущий байт (который всегда 04 для обозначения несжатого ключа в других библиотеках)
таким образом, учитывая несжатый открытый ключ в Hex с другого языка, вы можете обрезать первый байт, добавить эти 8 байтов на фронт и импортировать его с помощью
CngKey.Import(key,CngKeyBlobFormat.EccPrivateBlob);
Примечание: формат blob ключа задокументирован корпорацией Майкрософт.
тип ключа и длина ключа определяются в BCRYPT_ECCKEY_BLOB структура:
{ ulong Magic; ulong cbKey; }
ECC формат памяти открытого ключа:
BCRYPT_ECCKEY_BLOB
BYTE X[cbKey] // Big-endian.
BYTE Y[cbKey] // Big-endian.
формат памяти закрытого ключа ECC:
BCRYPT_ECCKEY_BLOB
BYTE X[cbKey] // Big-endian.
BYTE Y[cbKey] // Big-endian.
BYTE d[cbKey] // Big-endian.
магические значения .Сетка в официальный сайт Microsoft GitHub dotnet / corefx BCrypt / Interop.Капли.
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,
...
...
}
благодаря вам я смог импортировать открытый ключ ECDSA_P256 из сертификата с этим кодом:
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;
}
65 байтовых ключей (только открытый ключ) начинаются с 0x04
который необходимо удалить. Затем добавляется описанный вами заголовок.
тогда я смог проверить такую подпись:
var crypto = ECDsaCng(cngKey);
var verify = crypto.VerifyHash(hash, sig);
Я просто подумал, что скажу спасибо обоим вышеперечисленным постам, поскольку это очень помогло мне. Мне пришлось проверить подпись с помощью открытого ключа RSA, используя объект RSACng. Раньше я использовал RSACryptoServiceProvider, но это не соответствует FIPS, поэтому у меня возникли проблемы с переключением на RSACng. Он также требует .Чистая 4.6. Вот как я заставил его работать, используя вышеуказанные плакаты в качестве примера:
// 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);
Примечание: байт знака для модуля (0x00) можно или включить в модуль или нет, поэтому длина будет на один больше, если она включена. CNGkey, похоже, справляется с этим в любом случае.