Создайте X509Certificate2 из файла PEM в .NET Core - PullRequest
0 голосов
/ 08 мая 2018

Я хочу создать объект X509Certificate2 на основе файла PEM. Проблема заключается в установке свойства PrivateKey X509Certificate2. Я прочитал X509Certificate2.CreateFromCertFile () в .NET Core и затем использовал

var rsa = new RSACryptoServiceProvider();

rsa.ImportCspBlob(pvk);

Где pvk - это байтовый массив закрытого ключа (читается из GetBytesFromPEM, как показано здесь как получить закрытый ключ из файла PEM? ), чтобы установить закрытый ключ, но затем я получаю

Internal.Cryptography.CryptoThrowHelper + WindowsCryptographicException с сообщением Плохая версия поставщика.

Как правильно установить PrivateKey X509Certificate2 на основе закрытого ключа в файле PEM?

Если я посмотрю на Создание сертификата X5092 , они используют

 RSACryptoServiceProvider prov = Crypto.DecodeRsaPrivateKey(keyBuffer);
 certificate.PrivateKey = prov;

, который выглядит как отличный способ сделать это, но это не работает в .Net Core ...

1 Ответ

0 голосов
/ 08 мая 2018

Если вы только что извлекли байты из кодировки Base64 файла закрытого ключа, у вас есть PKCS # 1, PKCS # 8 или зашифрованный BLOB-объект закрытого ключа PKCS # 8 (в зависимости от того, было ли указано «BEGIN RSA PRIVATE KEY») "," НАЧАТЬ ЧАСТНЫЙ КЛЮЧ "или" НАЧАТЬ ЗАПИСАННЫЙ ЧАСТНЫЙ КЛЮЧ "). ImportCspBlob хочет иметь собственный формат данных, и поэтому он жалуется.

Цифровая подпись в c # без использования BouncyCastle содержит объяснение дальнейших действий. Самый простой / самый шаблонный способ - просто создать PFX с сертификатом и ключом, и пусть конструктор X509Certificate2 сделает свое дело.

Если вы идете по пути загрузки ключевого объекта напрямую, то способ, которым вы бы соединили закрытый ключ с сертификатом, - это использовать один из новых методов расширения CopyWithPrivateKey. Это возвращает новый экземпляр X509Certificate2, который знает о закрытом ключе.

Сеттер PrivateKey был «удален» из .NET Core, поскольку он имеет много побочных эффектов для Windows, которые трудно воспроизвести в Linux и macOS, особенно если вы получили сертификат из экземпляра X509Store.


Этот код представляет собой сочетание чрезмерно строгого и чрезмерно приемлемого для настоящих правил BER, но при этом должны считываться правильно закодированные файлы PKCS # 8, если они не содержат атрибутов.

private static readonly byte[] s_derIntegerZero = { 0x02, 0x01, 0x00 };

private static readonly byte[] s_rsaAlgorithmId =
{
    0x30, 0x0D,
    0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
    0x05, 0x00,
};

private static int ReadLength(byte[] data, ref int offset)
{
    byte lengthOrLengthLength = data[offset++];

    if (lengthOrLengthLength < 0x80)
    {
        return lengthOrLengthLength;
    }

    int lengthLength = lengthOrLengthLength & 0x7F;
    int length = 0;

    for (int i = 0; i < lengthLength; i++)
    {
        if (length > ushort.MaxValue)
        {
            throw new InvalidOperationException("This seems way too big.");
        }

        length <<= 8;
        length |= data[offset++];
    }

    return length;
}

private static byte[] ReadUnsignedInteger(byte[] data, ref int offset, int targetSize = 0)
{
    if (data[offset++] != 0x02)
    {
        throw new InvalidOperationException("Invalid encoding");
    }

    int length = ReadLength(data, ref offset);

    // Encoding rules say 0 is encoded as the one byte value 0x00.
    // Since we expect unsigned, throw if the high bit is set.
    if (length < 1 || data[offset] >= 0x80)
    {
        throw new InvalidOperationException("Invalid encoding");
    }

    byte[] ret;

    if (length == 1)
    {
        ret = new byte[length];
        ret[0] = data[offset++];
        return ret;
    }

    if (data[offset] == 0)
    {
        offset++;
        length--;
    }

    if (targetSize != 0)
    {
        if (length > targetSize)
        {
            throw new InvalidOperationException("Bad key parameters");
        }

        ret = new byte[targetSize];
    }
    else
    {
        ret = new byte[length];
    }

    Buffer.BlockCopy(data, offset, ret, ret.Length - length, length);
    offset += length;
    return ret;
}

private static void EatFullPayloadTag(byte[] data, ref int offset, byte tagValue)
{
    if (data[offset++] != tagValue)
    {
        throw new InvalidOperationException("Invalid encoding");
    }

    int length = ReadLength(data, ref offset);

    if (data.Length - offset != length)
    {
        throw new InvalidOperationException("Data does not represent precisely one value");
    }
}

private static void EatMatch(byte[] data, ref int offset, byte[] toMatch)
{
    if (data.Length - offset > toMatch.Length)
    {
        if (data.Skip(offset).Take(toMatch.Length).SequenceEqual(toMatch))
        {
            offset += toMatch.Length;
            return;
        }
    }

    throw new InvalidOperationException("Bad data.");
}

private static RSA DecodeRSAPkcs8(byte[] pkcs8Bytes)
{
    int offset = 0;

    // PrivateKeyInfo SEQUENCE
    EatFullPayloadTag(pkcs8Bytes, ref offset, 0x30);
    // PKCS#8 PrivateKeyInfo.version == 0
    EatMatch(pkcs8Bytes, ref offset, s_derIntegerZero);
    // rsaEncryption AlgorithmIdentifier value
    EatMatch(pkcs8Bytes, ref offset, s_rsaAlgorithmId);
    // PrivateKeyInfo.privateKey OCTET STRING
    EatFullPayloadTag(pkcs8Bytes, ref offset, 0x04);
    // RSAPrivateKey SEQUENCE
    EatFullPayloadTag(pkcs8Bytes, ref offset, 0x30);
    // RSAPrivateKey.version == 0
    EatMatch(pkcs8Bytes, ref offset, s_derIntegerZero);

    RSAParameters rsaParameters = new RSAParameters();
    rsaParameters.Modulus = ReadUnsignedInteger(pkcs8Bytes, ref offset);
    rsaParameters.Exponent = ReadUnsignedInteger(pkcs8Bytes, ref offset);
    rsaParameters.D = ReadUnsignedInteger(pkcs8Bytes, ref offset, rsaParameters.Modulus.Length);
    int halfModulus = (rsaParameters.Modulus.Length + 1) / 2;
    rsaParameters.P = ReadUnsignedInteger(pkcs8Bytes, ref offset, halfModulus);
    rsaParameters.Q = ReadUnsignedInteger(pkcs8Bytes, ref offset, halfModulus);
    rsaParameters.DP = ReadUnsignedInteger(pkcs8Bytes, ref offset, halfModulus);
    rsaParameters.DQ = ReadUnsignedInteger(pkcs8Bytes, ref offset, halfModulus);
    rsaParameters.InverseQ = ReadUnsignedInteger(pkcs8Bytes, ref offset, halfModulus);

    if (offset != pkcs8Bytes.Length)
    {
        throw new InvalidOperationException("Something didn't add up");
    }

    RSA rsa = RSA.Create();
    rsa.ImportParameters(rsaParameters);
    return rsa;
}
...