Search code examples
c#.netcryptographyx509certificatex509certificate2

Extract RSA Public key from x509 C#


I am trying to extract the RSA public key PKCS#1 from X509Certificate base64. to generate 8 tag for zatca phase 2 but output of my code is incorrect any one can help me with it. what i'm missing here ?

I have used following code:-

string X509CertificateBase64 = "MIID6jCCA5CgAwIBAgITbwAAgbuRbo5tpQ+QjgABAACBuzAKBggqhkjOPQQDAjBjMRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxEzARBgoJkiaJk/IsZAEZFgNnb3YxFzAVBgoJkiaJk/IsZAEZFgdleHRnYXp0MRwwGgYDVQQDExNUU1pFSU5WT0lDRS1TdWJDQS0xMB4XDTIyMTEwOTA4MDcyMloXDTI0MTEwODA4MDcyMlowTjELMAkGA1UEBhMCU0ExEzARBgNVBAoTCjM5OTk5OTk5OTkxDDAKBgNVBAsTA1RTVDEcMBoGA1UEAxMTVFNULTM5OTk5OTk5OTkwMDAwMzBWMBAGByqGSM49AgEGBSuBBAAKA0IABGGDDKDmhWAITDv7LXqLX2cmr6+qddUkpcLCvWs5rC2O29W/hS4ajAK4Qdnahym6MaijX75Cg3j4aao7ouYXJ9GjggI5MIICNTCBmgYDVR0RBIGSMIGPpIGMMIGJMTswOQYDVQQEDDIxLVRTVHwyLVRTVHwzLTlmMDkyMjM4LTFkOTctNDcxOC1iNDQxLWNiYzMwMTMyMWIwYTEfMB0GCgmSJomT8ixkAQEMDzM5OTk5OTk5OTkwMDAwMzENMAsGA1UEDAwEMTEwMDEMMAoGA1UEGgwDVFNUMQwwCgYDVQQPDANUU1QwHQYDVR0OBBYEFDuWYlOzWpFN3no1WtyNktQdrA8JMB8GA1UdIwQYMBaAFHZgjPsGoKxnVzWdz5qspyuZNbUvME4GA1UdHwRHMEUwQ6BBoD+GPWh0dHA6Ly90c3RjcmwuemF0Y2EuZ292LnNhL0NlcnRFbnJvbGwvVFNaRUlOVk9JQ0UtU3ViQ0EtMS5jcmwwga0GCCsGAQUFBwEBBIGgMIGdMG4GCCsGAQUFBzABhmJodHRwOi8vdHN0Y3JsLnphdGNhLmdvdi5zYS9DZXJ0RW5yb2xsL1RTWkVpbnZvaWNlU0NBMS5leHRnYXp0Lmdvdi5sb2NhbF9UU1pFSU5WT0lDRS1TdWJDQS0xKDEpLmNydDArBggrBgEFBQcwAYYfaHR0cDovL3RzdGNybC56YXRjYS5nb3Yuc2Evb2NzcDAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMDMCcGCSsGAQQBgjcVCgQaMBgwCgYIKwYBBQUHAwIwCgYIKwYBBQUHAwMwCgYIKoZIzj0EAwIDSAAwRQIgeWUEjxXaW4s8XilH/abzbDJhHHjO3uLaD87YqioA89YCIQDNltfAU98b8FnTD7M8NYIk8cqi7OnPu7h85v5V1Bt3Hg==";
    
         public string ExtractFromX509Certificate(string certificateStr)
         {
             byte[] data = Convert.FromBase64String(certificateStr);
             var x509 = new X509Certificate2(data);
             
             return Convert.ToBase64String(x509.PublicKey.EncodedKeyValue.RawData);
         }

public string Test1(){
   return   ExtractFromX509Certificate(X509CertificateBase64);

}

output="BGGDDKDmhWAITDv7LXqLX2cmr6+qddUkpcLCvWs5rC2O29W/hS4ajAK4Qdnahym6MaijX75Cg3j4aao7ouYXJ9E=";

but actual output should be = "MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEYYMMoOaFYAhMO/steotfZyavr6p11SSlwsK9azmsLY7b1b+FLhqMArhB2dqHKboxqKNfvkKDePhpqjui5hcn0Q==";

I have updated my Question Currently using .net core 2.2


Solution

  • From the certificate it can be seen that the key is an public EC key for secp256k1 (and not a public RSA key).

    x509.PublicKey.EncodedKeyValue.RawData returns the public key in uncompressed format: 0x04 + <32 bytes x coordinate> + <32 bytes y coordinate>.

    MFYw... specifies the public EC key in X.509/SPKI format. An export in this format is possible e.g. with x509.GetECDsaPublicKey().ExportSubjectPublicKeyInfo(). This returns the key in DER encoding. If the DER encoding is Base64 encoded, the expected result MFYw... is returned.

    using System;
    using System.Security.Cryptography.X509Certificates;
    
    ...
    
    string X509CertificateBase64 = "MIID6jCCA5CgAwIBAgITbwAAgbuRbo5tpQ+QjgABAACBuzAKBggqhkjOPQQDAjBjMRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxEzARBgoJkiaJk/IsZAEZFgNnb3YxFzAVBgoJkiaJk/IsZAEZFgdleHRnYXp0MRwwGgYDVQQDExNUU1pFSU5WT0lDRS1TdWJDQS0xMB4XDTIyMTEwOTA4MDcyMloXDTI0MTEwODA4MDcyMlowTjELMAkGA1UEBhMCU0ExEzARBgNVBAoTCjM5OTk5OTk5OTkxDDAKBgNVBAsTA1RTVDEcMBoGA1UEAxMTVFNULTM5OTk5OTk5OTkwMDAwMzBWMBAGByqGSM49AgEGBSuBBAAKA0IABGGDDKDmhWAITDv7LXqLX2cmr6+qddUkpcLCvWs5rC2O29W/hS4ajAK4Qdnahym6MaijX75Cg3j4aao7ouYXJ9GjggI5MIICNTCBmgYDVR0RBIGSMIGPpIGMMIGJMTswOQYDVQQEDDIxLVRTVHwyLVRTVHwzLTlmMDkyMjM4LTFkOTctNDcxOC1iNDQxLWNiYzMwMTMyMWIwYTEfMB0GCgmSJomT8ixkAQEMDzM5OTk5OTk5OTkwMDAwMzENMAsGA1UEDAwEMTEwMDEMMAoGA1UEGgwDVFNUMQwwCgYDVQQPDANUU1QwHQYDVR0OBBYEFDuWYlOzWpFN3no1WtyNktQdrA8JMB8GA1UdIwQYMBaAFHZgjPsGoKxnVzWdz5qspyuZNbUvME4GA1UdHwRHMEUwQ6BBoD+GPWh0dHA6Ly90c3RjcmwuemF0Y2EuZ292LnNhL0NlcnRFbnJvbGwvVFNaRUlOVk9JQ0UtU3ViQ0EtMS5jcmwwga0GCCsGAQUFBwEBBIGgMIGdMG4GCCsGAQUFBzABhmJodHRwOi8vdHN0Y3JsLnphdGNhLmdvdi5zYS9DZXJ0RW5yb2xsL1RTWkVpbnZvaWNlU0NBMS5leHRnYXp0Lmdvdi5sb2NhbF9UU1pFSU5WT0lDRS1TdWJDQS0xKDEpLmNydDArBggrBgEFBQcwAYYfaHR0cDovL3RzdGNybC56YXRjYS5nb3Yuc2Evb2NzcDAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMDMCcGCSsGAQQBgjcVCgQaMBgwCgYIKwYBBQUHAwIwCgYIKwYBBQUHAwMwCgYIKoZIzj0EAwIDSAAwRQIgeWUEjxXaW4s8XilH/abzbDJhHHjO3uLaD87YqioA89YCIQDNltfAU98b8FnTD7M8NYIk8cqi7OnPu7h85v5V1Bt3Hg==";
    byte[] x509CertificateRaw = Convert.FromBase64String(X509CertificateBase64);
    X509Certificate2 x509Certificate = new X509Certificate2(x509CertificateRaw);
    
    Console.WriteLine(Convert.ToHexString(x509Certificate.PublicKey.EncodedKeyValue.RawData)); // 0461830CA0E68560084C3BFB2D7A8B5F6726AFAFAA75D524A5C2C2BD6B39AC2D8EDBD5BF852E1A8C02B841D9DA8729BA31A8A35FBE428378F869AA3BA2E61727D1
    Console.WriteLine(Convert.ToBase64String(x509Certificate.GetECDsaPublicKey().ExportSubjectPublicKeyInfo())); // MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEYYMMoOaFYAhMO/steotfZyavr6p11SSlwsK9azmsLY7b1b+FLhqMArhB2dqHKboxqKNfvkKDePhpqjui5hcn0Q==
    

    Edit: Regarding your comment that you are using .NET Core 2.2:

    In .NET Core 2.2 (as well as in .NET Framework) there is little or no support for the X.509/SPKI format. Here it is most convenient to use C#/BouncyCastle.
    There are several ways to convert the public key to the X.509/SPKI format using C#/BouncyCastle. Since in your use case the public key is to be extracted from a certificate and the certificate already contains the public key in X.509/SPKI format (see RFC 5280, 4.1. Basic Certificate Fields), it makes sense to export it directly with BouncyCastle's ASN.1 parser:

    using System;
    using Org.BouncyCastle.Asn1;
    ...
    string X509CertificateBase64 = "MIID6jCCA5CgAwIBAgITbwAAgbuRbo5tpQ+QjgABAACBuzAKBggqhkjOPQQDAjBjMRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxEzARBgoJkiaJk/IsZAEZFgNnb3YxFzAVBgoJkiaJk/IsZAEZFgdleHRnYXp0MRwwGgYDVQQDExNUU1pFSU5WT0lDRS1TdWJDQS0xMB4XDTIyMTEwOTA4MDcyMloXDTI0MTEwODA4MDcyMlowTjELMAkGA1UEBhMCU0ExEzARBgNVBAoTCjM5OTk5OTk5OTkxDDAKBgNVBAsTA1RTVDEcMBoGA1UEAxMTVFNULTM5OTk5OTk5OTkwMDAwMzBWMBAGByqGSM49AgEGBSuBBAAKA0IABGGDDKDmhWAITDv7LXqLX2cmr6+qddUkpcLCvWs5rC2O29W/hS4ajAK4Qdnahym6MaijX75Cg3j4aao7ouYXJ9GjggI5MIICNTCBmgYDVR0RBIGSMIGPpIGMMIGJMTswOQYDVQQEDDIxLVRTVHwyLVRTVHwzLTlmMDkyMjM4LTFkOTctNDcxOC1iNDQxLWNiYzMwMTMyMWIwYTEfMB0GCgmSJomT8ixkAQEMDzM5OTk5OTk5OTkwMDAwMzENMAsGA1UEDAwEMTEwMDEMMAoGA1UEGgwDVFNUMQwwCgYDVQQPDANUU1QwHQYDVR0OBBYEFDuWYlOzWpFN3no1WtyNktQdrA8JMB8GA1UdIwQYMBaAFHZgjPsGoKxnVzWdz5qspyuZNbUvME4GA1UdHwRHMEUwQ6BBoD+GPWh0dHA6Ly90c3RjcmwuemF0Y2EuZ292LnNhL0NlcnRFbnJvbGwvVFNaRUlOVk9JQ0UtU3ViQ0EtMS5jcmwwga0GCCsGAQUFBwEBBIGgMIGdMG4GCCsGAQUFBzABhmJodHRwOi8vdHN0Y3JsLnphdGNhLmdvdi5zYS9DZXJ0RW5yb2xsL1RTWkVpbnZvaWNlU0NBMS5leHRnYXp0Lmdvdi5sb2NhbF9UU1pFSU5WT0lDRS1TdWJDQS0xKDEpLmNydDArBggrBgEFBQcwAYYfaHR0cDovL3RzdGNybC56YXRjYS5nb3Yuc2Evb2NzcDAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMDMCcGCSsGAQQBgjcVCgQaMBgwCgYIKwYBBQUHAwIwCgYIKwYBBQUHAwMwCgYIKoZIzj0EAwIDSAAwRQIgeWUEjxXaW4s8XilH/abzbDJhHHjO3uLaD87YqioA89YCIQDNltfAU98b8FnTD7M8NYIk8cqi7OnPu7h85v5V1Bt3Hg==";
    byte[] x509CertificateRaw = Convert.FromBase64String(X509CertificateBase64);
    Asn1InputStream asn1InputStream = new Asn1InputStream(x509CertificateRaw);
    Asn1Sequence asn1SequenceRoot = Asn1Sequence.GetInstance(asn1InputStream.ReadObject());
    Asn1Sequence asn1SequenceTbsCertificate = Asn1Sequence.GetInstance(asn1SequenceRoot[0]); // TBSCertificate
    Asn1Sequence asn1SequenceSpki = Asn1Sequence.GetInstance(asn1SequenceTbsCertificate[6]); // SubjectPublicKeyInfo
    Console.WriteLine(Convert.ToBase64String(asn1SequenceSpki.GetDerEncoded())); // MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEYYMMoOaFYAhMO/steotfZyavr6p11SSlwsK9azmsLY7b1b+FLhqMArhB2dqHKboxqKNfvkKDePhpqjui5hcn0Q==