Search code examples
c#.net.net-corehttpclientx509certificate2

Unable to add key file to X509Certificate2


Environment: VS 2019, Core 3.1, C# 8.0

I'm getting the following error while trying to add a .cer and .key file to my httpClientHandler:

    {"ASN1 corrupted data."}
        Data: {System.Collections.ListDictionaryInternal}
        HResult: -2146233087
        HelpLink: null
        InnerException: null
        Message: "ASN1 corrupted data."
        Source: "System.Security.Cryptography.Algorithms"
        StackTrace: "   at System.Security.Cryptography.Asn1.AsnReader.CheckExpectedTag(Asn1Tag tag, Asn1Tag expectedTag, UniversalTagNumber tagNumber)\r\n   at System.Security.Cryptography.Asn1.AsnReader.ReadSequence(Asn1Tag expectedTag)\r\n   at System.Security.Cryptography.Asn1.RSAPrivateKeyAsn.Decode(AsnReader reader, Asn1Tag expectedTag, RSAPrivateKeyAsn& decoded)\r\n   at System.Security.Cryptography.Asn1.RSAPrivateKeyAsn.Decode(Asn1Tag expectedTag, ReadOnlyMemory`1 encoded, AsnEncodingRules ruleSet)\r\n   at System.Security.Cryptography.Asn1.RSAPrivateKeyAsn.Decode(ReadOnlyMemory`1 encoded, AsnEncodingRules ruleSet)\r\n   at System.Security.Cryptography.RSAKeyFormatHelper.FromPkcs1PrivateKey(ReadOnlyMemory`1 keyData, AlgorithmIdentifierAsn& algId, RSAParameters& ret)\r\n   at System.Security.Cryptography.RSA.ImportRSAPrivateKey(ReadOnlySpan`1 source, Int32& bytesRead)\r\n   at BnyMellon.Program.CreateFromCertFile(String cerFile, String keyFile) in C:\\Users\\bbernzweig.AD\\source\\repos\\HttpClientExample\\
    BnyMellon\\Program.cs:line 150"
        TargetSite: {Void CheckExpectedTag(System.Security.Cryptography.Asn1.Asn1Tag, System.Security.Cryptography.Asn1.Asn1Tag, System.Security.Cryptography.Asn1.UniversalTagNumber)}

Error is raised here on line rsa.ImportRSAPrivateKey(privateKeyBytes, out _);:

private static X509Certificate2 CreateFromCertFile(string cerFile, string keyFile)
{
    try
    {
        var cert = new X509Certificate2 (cerFile);
        var privateKeyBytes = LoadPrivateKeyBytes(keyFile);

        using var rsa = RSA.Create();
        rsa.ImportRSAPrivateKey(privateKeyBytes, out _);
        var certWithKey = cert.CopyWithPrivateKey(rsa);

        cert.Dispose();
        return certWithKey;
    }
    catch(Exception e)
    {
        Console.WriteLine(e);
    }

    return null;
}

Called from:

var clientCertificate = new X509Certificate2();
clientCertificate = CreateFromCertFile(certificateFile, keyFile);  
httpClientHandler.ClientCertificates.Add(clientCertificate);

Note: I'm able to make the request using both of these files via curl and Postman without any problem.

I'm trying to attaching both files to the request so not tied to this specific approach. If there is a better way I'm interested in hearing about it.


Solution

  • Super late to this, and faced the same problem ASN1 corrupted data and managed to resolve my problem from both your question and the question answered by @bartonjs

    The advice on Create X509Certificate2 from Cert and Key, without making a PFX file question is

    using (RSA rsa = RSA.Create())
    {
        rsa.ImportRSAPrivateKey(binaryEncoding, out _);
        // do stuff with the key now
    }
    

    The clue for me was binaryEncoding, the answer is commented as part of the same question is...

    if you had a PEM you need to "de-PEM" it, by extracting the contents between the BEGIN and END delimiters and running it through Convert.FromBase64String in order to get binaryEncoding

    So based on your code... the following imports the PEM file without issue.

            private static byte[] LoadPrivateKeyBytes(string keyFile)
            {
                // remove these lines
                // -----BEGIN RSA PRIVATE KEY-----
                // -----END RSA PRIVATE KEY-----
                var pemFileData = File.ReadAllLines(keyFile).Where(x => !x.StartsWith("-"));
    
                // Join it all together, convert from base64
                var binaryEncoding = Convert.FromBase64String(string.Join(null, pemFileData));
    
                // this is the private key byte data
                return binaryEncoding;
            }
    
            private static X509Certificate2 CreateFromCertFile(string cerFile, string keyFile)
            {
                try
                {
                    var cert = new X509Certificate2(cerFile);
                    var privateKeyBytes = LoadPrivateKeyBytes(keyFile);
    
                    using var rsa = RSA.Create();
                    rsa.ImportRSAPrivateKey(privateKeyBytes, out _);
                    var certWithKey = cert.CopyWithPrivateKey(rsa);
    
                    cert.Dispose();
                    return certWithKey;
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
    
    #pragma warning disable CS8603 // Possible null reference return.
                return null;
    #pragma warning restore CS8603 // Possible null reference return.
            }