Search code examples
c#x509pemed25519pkcs#8

Get public and private key from PEM ed25519 in C#


I have private key generated by: openssl genpkey -algorithm ed25519 -out private.pem and it looks like this:

-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEINTZWUEn/Jt6TV9OxGxjD+6CtqKB3MtcJdFAzFUg3fk/
-----END PRIVATE KEY-----

I also have a public key generated by: openssl pkey -in private.pem -out public.pem and it looks like this:

-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAPBDjfKgiUSIjVLrvsR+pxw5i9unTpr8S5BL04T13r6w=
-----END PUBLIC KEY-----

And now I don't know, how can I get public and private keys from these PEM's in C#? In my previous question: Generating public ed25519 key with OpenSSL I found out that the public key is in X509/SPKI format and the private key is in PKCS#8 format. But how to get right keys from these formats in C#? I tried with the X509Certificate2 class but got nothing.


Solution

  • Ed25519 can be implemented on .NET with e.g. BouncyCastle and runs on both .NET Framework and .NET Core.

    BouncyCastle also provides a way to directly import PEM keys using the PemReader class.

    Example:

    using Org.BouncyCastle.Crypto;
    using Org.BouncyCastle.Crypto.Parameters;
    using Org.BouncyCastle.Crypto.Signers;
    using Org.BouncyCastle.OpenSsl;
    using System;
    using System.IO;
    using System.Text;
    
    ...
    
    //
    // Signing
    //
    
    // Import private key
    string ed25519pkcs8 = @"-----BEGIN PRIVATE KEY-----
                            MC4CAQAwBQYDK2VwBCIEIAYIsKL0xkTkAXDhUN6eDheqODEOGyFZ04jsgFNCFxZf
                            -----END PRIVATE KEY-----";
    PemReader pemReaderPrivate = new PemReader(new StringReader(ed25519pkcs8));
    Ed25519PrivateKeyParameters ed25519pkcs8Parameters = (Ed25519PrivateKeyParameters)pemReaderPrivate.ReadObject();
    
    // Sign
    byte[] dataToSign = Encoding.UTF8.GetBytes("The quick brown fox jumps over the lazy dog");
    ISigner signer = new Ed25519Signer();
    signer.Init(true, ed25519pkcs8Parameters);
    signer.BlockUpdate(dataToSign, 0, dataToSign.Length);
    byte[] signature = signer.GenerateSignature();
    Console.WriteLine("Signature: " + Convert.ToBase64String(signature)); // Signature: MTAK9rOibXN1RBOP3O6cRf7Dut1wS6pdz9xM11NIMjg/G0vEusn0piL1iTUcVZvfPNr4PHZSsjp6qX9HkCKRCw==
    
    //
    // Verifying
    //
    
    // Import public key
    string ed25519x509 = @"-----BEGIN PUBLIC KEY-----
                          MCowBQYDK2VwAyEA3mcwgf2DrWLR3mQ6l2d59bGU6qUStwQrln2+rKlKxoA=
                          -----END PUBLIC KEY-----";
    PemReader pemReaderPublic = new PemReader(new StringReader(ed25519x509));
    Ed25519PublicKeyParameters ed25519x509Parameters = (Ed25519PublicKeyParameters)pemReaderPublic.ReadObject();
    
    // Verify
    ISigner verifier = new Ed25519Signer();
    verifier.Init(false, ed25519x509Parameters);
    verifier.BlockUpdate(dataToSign, 0, dataToSign.Length);
    bool verified = verifier.VerifySignature(signature);
    Console.WriteLine("Verification: " + verified); // Verification: True
    

    with the output:

    Signature: MTAK9rOibXN1RBOP3O6cRf7Dut1wS6pdz9xM11NIMjg/G0vEusn0piL1iTUcVZvfPNr4PHZSsjp6qX9HkCKRCw==
    Verification: True