Microsoft CNG | Как импортировать закрытый ключ ECDSA в кодировке PEM в поставщик хранилищ ключей MS - PullRequest
0 голосов
/ 28 апреля 2018

Я знаю, что MS CNG private имеет этот формат - BCRYPT_ECCKEY_BLOB BYTE X[cbKey] // Big-endian. BYTE Y[cbKey] // Big-endian. BYTE d[cbKey] // Big-endian.

Таким образом пытались импортировать ниже ключевые байты -

byte[] ec256PrivKB =
{
//Magic + CBLength
0x45, 0x43, 0x53, 0x31, 0x20, 0x00, 0x00, 0x00,
//X
0xA7, 0xFB, 0xCD, 0x4D, 0x7E, 0x43, 0x6F, 0x22, 0xBD, 0x74, 0xFA, 0x1F, 0xD7, 0x10, 0xDB, 0x8C, 0xF8, 0x29, 0xC1, 0xEC, 0x5E, 0x15, 0x1E, 0xE2, 0x84, 0x56, 0x3E, 0x54, 0x6E, 0x1D, 0x5C, 0xF6, 
//Y
0x6B, 0x42, 0x21, 0xD1, 0x92, 0xEB, 0x69, 0x66, 0x56, 0xD6, 0xEC, 0x4D, 0x21, 0xB7, 0xDB, 0x3C, 0x94, 0x56, 0x8D, 0x87, 0xEB, 0x1C, 0x11, 0x0F, 0x03, 0x80, 0xF6, 0x10, 0x70, 0x73, 0x7D, 0x1D, 
//D
0x5E, 0xF0, 0x2A, 0x1B, 0x34, 0xE9, 0x2B, 0x96, 0xA4, 0xAE, 0x05, 0x1D, 0x33, 0x53, 0x36, 0x39, 0x7B, 0x1F, 0xF5, 0x24, 0xA4, 0xD6, 0xBD, 0x12, 0x07, 0x3F, 0x43, 0x30, 0x70, 0x32, 0x4E, 0x5D
};

Теперь по телефону

ECDsaCng eCDsa = new ECDsaCng( CngKey.Import(ec256PrivKB, CngKeyBlobFormat.EccPrivateBlob,
                    CngProvider.MicrosoftSoftwareKeyStorageProvider));

Даёт System.Security.Cryptography.CryptographicException: 'The requested operation is not supported. Я не понимаю, почему он дает это исключение?

Кроме того, как импортировать закрытый ключ ecdsa в кодировке base64 в провайдер хранилища ключей MS, т.е. предположим, что у меня есть закрытый ключ ec ниже -

-----BEGIN EC PRIVATE KEY-----
MHcCAQEEICWeuFHssg5i2vJlyMHPUb+DJnylxfbkR8KJPXfYw5ikoAoGCCqGSM49
AwEHoUQDQgAE7A4wVMLQ+orOZYcFv6mLNBbAWfffPwTTw4iroyQDcytYWT+frzl3
RiFXqC1niHgduYtGBZIbwq/48ooyL9HbkA==
-----END EC PRIVATE KEY-----

Теперь, как мне импортировать это в провайдера СПГ?

Редактировать - Также есть ли способ ответить на этот процесс, т.е. Я знаю, как конвертировать из OpenSSL(PEM FORMAT) в MS Format(RAW format) но как сделать обратное значение как преобразовать MS Форматировать ключ ECDSA в OpenSSL Ключ EC в PEM.

1 Ответ

0 голосов
/ 30 апреля 2018

Ваш ключевой блок начинается с BCRYPT_ECDSA_ PUBLIC _P256_MAGIC, но вы хотите BCRYPT_ECDSA_ PRIVATE _P256_MAGIC (измените 0x31 на 0x32).

Исходя из этого, можно предположить, что вы знаете, как выполнить преобразование, но поскольку в заголовке спрашивается, как это сделать в более общем смысле, я продолжу. C # Получить объект CngKey из открытого ключа в текстовом файле имеет простой (если хакерский) ответ на открытые ключи. Закрытые ключи разные, но в основном простые.

Если мы возьмем ключ PEM из вопроса, мы сможем удалить «PEM armor», чтобы получить каплю base-64. Превратив этот шарик base-64 в шестнадцатеричное, мы получим

30 77 02 01 01 04 20 25 9E B8 51 EC B2 0E 62 DA 
F2 65 C8 C1 CF 51 BF 83 26 7C A5 C5 F6 E4 47 C2 
89 3D 77 D8 C3 98 A4 A0 0A 06 08 2A 86 48 CE 3D 
03 01 07 A1 44 03 42 00 04 EC 0E 30 54 C2 D0 FA 
8A CE 65 87 05 BF A9 8B 34 16 C0 59 F7 DF 3F 04 
D3 C3 88 AB A3 24 03 73 2B 58 59 3F 9F AF 39 77 
46 21 57 A8 2D 67 88 78 1D B9 8B 46 05 92 1B C2 
AF F8 F2 8A 32 2F D1 DB 90

Если мы откроем https://www.secg.org/sec1-v2.pdf раздел C.4, то увидим

ECPrivateKey ::= SEQUENCE {
  version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
  privateKey OCTET STRING,
  parameters [0] ECDomainParameters {{ SECGCurveNames }} OPTIONAL,
  publicKey [1] BIT STRING OPTIONAL
}

С помощью спецификаций ASN.1 ( ITU-T X.680 ) и BER / DER ( ITU-T X.690 ) мы можем смешать этот шестнадцатеричный дамп и эту структуру :

// (constructed) SEQUENCE, 119 content bytes
30 77
   // INTEGER, 1 content byte, value 0x01.
   02 01 01
   // OCTET STRING (byte[]), 32 bytes, value 25 9E ... 98 A4
   04 20
      25 9E B8 51 EC B2 0E 62 DA F2 65 C8 C1 CF 51 BF 
      83 26 7C A5 C5 F6 E4 47 C2 89 3D 77 D8 C3 98 A4
   // (constructed) CONTEXT-SPECIFIC 0, 10 content bytes
   A0 0A
      // OBJECT IDENTIFIER, 8 content bytes, 1.2.840.10045.3.1.7
      06 08 2A 86 48 CE 3D 03 01 07
   // (constructed) CONTEXT-SPECIFIC 1, 68 content bytes
   A1 44
      // BIT STRING, 66 content bytes, 0 unused bits, value 04 EC .. DB 90
      03 42
         00
         04 EC 0E 30 54 C2 D0 FA 8A CE 65 87 05 BF A9 8B 
         34 16 C0 59 F7 DF 3F 04 D3 C3 88 AB A3 24 03 73 
         2B 58 59 3F 9F AF 39 77 46 21 57 A8 2D 67 88 78 
         1D B9 8B 46 05 92 1B C2 AF F8 F2 8A 32 2F D1 DB 
         90

Выполнение поиска для 1.2.840.10045.3.1.7 указывает на то, что (неудивительно) это эллиптическая кривая secp256r1 / NIST P-256.

Содержимое строки BIT STRING начинается с 04, что указывает на то, что это несжатая точка EC, первая половина того, что остается, является координатой X, а вторая половина - координатой Y. Приятно, что это соответствует ширине OCTET STRING для закрытого ключа, что означает, что структура действительна.

Вы заметите, что в структуре ASN.1 указано, что идентификатор кривой и открытый ключ технически являются необязательными. На практике они записаны, и это хорошо, так как мы собираемся на это рассчитывать.

Преобразование капли с закрытым ключом ЕС в каплю СПГ

// structure opening up to the private key (D) value.
private static readonly byte[] s_secp256R1Prefix =
    { 0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20 };

// After D through the 0x04 identifying the public key is uncompressed
private static readonly byte[] s_secp256R1Infix =
{
    0xA0, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE,
    0x3D, 0x03, 0x01, 0x07, 0xA1, 0x44, 0x03, 0x42,
    0x00, 0x04
};

private static readonly byte[] s_secp256R1PrivateCngPrefix =
    { 0x45, 0x43, 0x53, 0x32, 0x20, 0x00, 0x00, 0x00 };

private static CngKey DecodeP256ECPrivateKeyBase64(string base64)
{
    byte[] derBlob = Convert.FromBase64String(base64);

    if (derBlob.Length == 121 &&
        derBlob.Take(s_secp256R1Prefix.Length).SequenceEqual(s_secp256R1Prefix) &&
        derBlob.Skip(0x20 + s_secp256R1Prefix.Length).Take(s_secp256R1Infix.Length).
            SequenceEqual(s_secp256R1Infix))
    {
        byte[] cngBlob = new byte[2 * sizeof(uint) + 3 * 0x20];
        int offset = 0;

        // Header (BCRYPT_ECDSA_PRIVATE_P256_MAGIC and 0x00000020)
        Buffer.BlockCopy(
            s_secp256R1PrivateCngPrefix,
            0,
            cngBlob,
            offset,
            s_secp256R1PrivateCngPrefix.Length);

        offset += s_secp256R1PrivateCngPrefix.Length;

        // X and Y
        Buffer.BlockCopy(
            derBlob,
            s_secp256R1Prefix.Length + 0x20 + s_secp256R1Infix.Length,
            cngBlob,
            offset,
            2 * 0x20);

        offset += 2 * 0x20;

        Buffer.BlockCopy(
            derBlob,
            s_secp256R1Prefix.Length,
            cngBlob,
            offset,
            0x20);

        offset += 0x20;
        Debug.Assert(offset == cngBlob.Length);

        return CngKey.Import(cngBlob, CngKeyBlobFormat.EccPrivateBlob);
    }

    throw new InvalidOperationException();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...