web-dev-qa-db-fra.com

Créer X509Certificate2 à partir d'un fichier PEM dans .NET Core

Je veux créer un objet X509Certificate2 basé sur un fichier PEM. Le problème est la définition de la propriété PrivateKey de X509Certificate2. J'ai lu X509Certificate2.CreateFromCertFile () sur .NET Core puis utilisé

var rsa = new RSACryptoServiceProvider();

rsa.ImportCspBlob(pvk);

pvk est le tableau d'octets de la clé privée (lu à partir de GetBytesFromPEM comme indiqué ici comment obtenir la clé privée du fichier PEM? ), pour définir la clé privée, mais ensuite j'obtiens une

Internal.Cryptography.CryptoThrowHelper + WindowsCryptographicException avec message Mauvaise version du fournisseur.

Comment définir correctement la PrivateKey du X509Certificate2 en fonction de la clé privée du fichier PEM?

Si je regarde Création du X509Certificate2 , ils utilisent

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

ce qui semble être une bonne façon de le faire, mais cela ne fonctionne pas dans .Net Core ...

10
heydy

Si vous venez d'extraire les octets du codage Base64 du fichier de clé privée, vous disposez d'un blob de clé privée PKCS # 1, PKCS # 8 ou crypté PKCS # 8 (selon qu'il indique "BEGIN RSA PRIVATE KEY", " COMMENCER LA CLÉ PRIVÉE "ou" COMMENCER LA CLÉ PRIVÉE CRYPTÉE "). ImportCspBlob veut un format personnalisé pour les données, et c'est pourquoi il se plaint.

Signature numérique en c # sans utiliser BouncyCastle a une explication des voies à suivre. La formule la plus simple/la plus simple consiste à créer un PFX avec le certificat et la clé, et à laisser le X509Certificate2 le constructeur fait son truc.

Si vous suivez la procédure de chargement de l'objet clé directement, la façon dont vous associez une clé privée au certificat consiste à utiliser l'une des nouvelles méthodes d'extension CopyWithPrivateKey . Cela renvoie une nouvelle instance de X509Certificate2 qui connaît la clé privée.

Le setter PrivateKey a été "supprimé" de .NET Core car il a beaucoup d'effets secondaires sur Windows qui sont difficiles à répliquer sur Linux et macOS, en particulier si vous avez récupéré le certificat d'une instance de X509Store.


Ce code est une combinaison de règles trop strictes et trop acceptables pour les vraies règles BER, mais cela devrait lire les fichiers PKCS # 8 valablement codés, sauf s'ils contiennent des attributs.

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