How to get RSACryptoServiceProvider public and private key only in c#












0















I am running below code to get public and private key only, but it seems it outputs the whole XML format. I only need to output the keys as shown in Public and Private Key demo



        static RSACryptoServiceProvider rsa;
private RSAParameters _privateKey;
private RSAParameters _publicKey;
public RSACrypto()
{
rsa = new RSACryptoServiceProvider(2048);
_privateKey = rsa.ExportParameters(true);
_publicKey = rsa.ExportParameters(false);

}
public string GetPublicKeyString()
{
var sw = new StringWriter();
var xs = new XmlSerializer(typeof(RSAParameters));
xs.Serialize(sw, _publicKey);
return sw.ToString();
}
public string GetPrivateKeyString()
{
var sw = new StringWriter();
var xs = new XmlSerializer(typeof(RSAParameters));
xs.Serialize(sw, _privateKey);
return sw.ToString();
}









share|improve this question























  • See the second answer of this question. stackoverflow.com/questions/14047532/…

    – NthDeveloper
    Jan 1 at 20:47











  • @NthDeveloper: That answer has nothing to do with this question.

    – James K Polk
    Jan 1 at 21:49






  • 1





    You can use the Bouncycastle C# library which is the easiest solution or you can try to hand code the generation of ASN.1 objects yourself which is doable but much harder.

    – James K Polk
    Jan 1 at 21:52











  • In .NET Core 3.0 (preview 1) you can get the binary data with rsa.ExportSubjectPublicKeyInfo() and rsa.ExportRSAPrivateKey(), I’ll post an answer tomorrow.

    – bartonjs
    Jan 2 at 3:49













  • @JamesKPolk thank you, I used BouncyCastle C# library, please write it as an answer to close this thread.

    – ARH
    Jan 2 at 10:48
















0















I am running below code to get public and private key only, but it seems it outputs the whole XML format. I only need to output the keys as shown in Public and Private Key demo



        static RSACryptoServiceProvider rsa;
private RSAParameters _privateKey;
private RSAParameters _publicKey;
public RSACrypto()
{
rsa = new RSACryptoServiceProvider(2048);
_privateKey = rsa.ExportParameters(true);
_publicKey = rsa.ExportParameters(false);

}
public string GetPublicKeyString()
{
var sw = new StringWriter();
var xs = new XmlSerializer(typeof(RSAParameters));
xs.Serialize(sw, _publicKey);
return sw.ToString();
}
public string GetPrivateKeyString()
{
var sw = new StringWriter();
var xs = new XmlSerializer(typeof(RSAParameters));
xs.Serialize(sw, _privateKey);
return sw.ToString();
}









share|improve this question























  • See the second answer of this question. stackoverflow.com/questions/14047532/…

    – NthDeveloper
    Jan 1 at 20:47











  • @NthDeveloper: That answer has nothing to do with this question.

    – James K Polk
    Jan 1 at 21:49






  • 1





    You can use the Bouncycastle C# library which is the easiest solution or you can try to hand code the generation of ASN.1 objects yourself which is doable but much harder.

    – James K Polk
    Jan 1 at 21:52











  • In .NET Core 3.0 (preview 1) you can get the binary data with rsa.ExportSubjectPublicKeyInfo() and rsa.ExportRSAPrivateKey(), I’ll post an answer tomorrow.

    – bartonjs
    Jan 2 at 3:49













  • @JamesKPolk thank you, I used BouncyCastle C# library, please write it as an answer to close this thread.

    – ARH
    Jan 2 at 10:48














0












0








0








I am running below code to get public and private key only, but it seems it outputs the whole XML format. I only need to output the keys as shown in Public and Private Key demo



        static RSACryptoServiceProvider rsa;
private RSAParameters _privateKey;
private RSAParameters _publicKey;
public RSACrypto()
{
rsa = new RSACryptoServiceProvider(2048);
_privateKey = rsa.ExportParameters(true);
_publicKey = rsa.ExportParameters(false);

}
public string GetPublicKeyString()
{
var sw = new StringWriter();
var xs = new XmlSerializer(typeof(RSAParameters));
xs.Serialize(sw, _publicKey);
return sw.ToString();
}
public string GetPrivateKeyString()
{
var sw = new StringWriter();
var xs = new XmlSerializer(typeof(RSAParameters));
xs.Serialize(sw, _privateKey);
return sw.ToString();
}









share|improve this question














I am running below code to get public and private key only, but it seems it outputs the whole XML format. I only need to output the keys as shown in Public and Private Key demo



        static RSACryptoServiceProvider rsa;
private RSAParameters _privateKey;
private RSAParameters _publicKey;
public RSACrypto()
{
rsa = new RSACryptoServiceProvider(2048);
_privateKey = rsa.ExportParameters(true);
_publicKey = rsa.ExportParameters(false);

}
public string GetPublicKeyString()
{
var sw = new StringWriter();
var xs = new XmlSerializer(typeof(RSAParameters));
xs.Serialize(sw, _publicKey);
return sw.ToString();
}
public string GetPrivateKeyString()
{
var sw = new StringWriter();
var xs = new XmlSerializer(typeof(RSAParameters));
xs.Serialize(sw, _privateKey);
return sw.ToString();
}






c# asp.net rsa public-key-encryption






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Jan 1 at 18:28









ARHARH

5332932




5332932













  • See the second answer of this question. stackoverflow.com/questions/14047532/…

    – NthDeveloper
    Jan 1 at 20:47











  • @NthDeveloper: That answer has nothing to do with this question.

    – James K Polk
    Jan 1 at 21:49






  • 1





    You can use the Bouncycastle C# library which is the easiest solution or you can try to hand code the generation of ASN.1 objects yourself which is doable but much harder.

    – James K Polk
    Jan 1 at 21:52











  • In .NET Core 3.0 (preview 1) you can get the binary data with rsa.ExportSubjectPublicKeyInfo() and rsa.ExportRSAPrivateKey(), I’ll post an answer tomorrow.

    – bartonjs
    Jan 2 at 3:49













  • @JamesKPolk thank you, I used BouncyCastle C# library, please write it as an answer to close this thread.

    – ARH
    Jan 2 at 10:48



















  • See the second answer of this question. stackoverflow.com/questions/14047532/…

    – NthDeveloper
    Jan 1 at 20:47











  • @NthDeveloper: That answer has nothing to do with this question.

    – James K Polk
    Jan 1 at 21:49






  • 1





    You can use the Bouncycastle C# library which is the easiest solution or you can try to hand code the generation of ASN.1 objects yourself which is doable but much harder.

    – James K Polk
    Jan 1 at 21:52











  • In .NET Core 3.0 (preview 1) you can get the binary data with rsa.ExportSubjectPublicKeyInfo() and rsa.ExportRSAPrivateKey(), I’ll post an answer tomorrow.

    – bartonjs
    Jan 2 at 3:49













  • @JamesKPolk thank you, I used BouncyCastle C# library, please write it as an answer to close this thread.

    – ARH
    Jan 2 at 10:48

















See the second answer of this question. stackoverflow.com/questions/14047532/…

– NthDeveloper
Jan 1 at 20:47





See the second answer of this question. stackoverflow.com/questions/14047532/…

– NthDeveloper
Jan 1 at 20:47













@NthDeveloper: That answer has nothing to do with this question.

– James K Polk
Jan 1 at 21:49





@NthDeveloper: That answer has nothing to do with this question.

– James K Polk
Jan 1 at 21:49




1




1





You can use the Bouncycastle C# library which is the easiest solution or you can try to hand code the generation of ASN.1 objects yourself which is doable but much harder.

– James K Polk
Jan 1 at 21:52





You can use the Bouncycastle C# library which is the easiest solution or you can try to hand code the generation of ASN.1 objects yourself which is doable but much harder.

– James K Polk
Jan 1 at 21:52













In .NET Core 3.0 (preview 1) you can get the binary data with rsa.ExportSubjectPublicKeyInfo() and rsa.ExportRSAPrivateKey(), I’ll post an answer tomorrow.

– bartonjs
Jan 2 at 3:49







In .NET Core 3.0 (preview 1) you can get the binary data with rsa.ExportSubjectPublicKeyInfo() and rsa.ExportRSAPrivateKey(), I’ll post an answer tomorrow.

– bartonjs
Jan 2 at 3:49















@JamesKPolk thank you, I used BouncyCastle C# library, please write it as an answer to close this thread.

– ARH
Jan 2 at 10:48





@JamesKPolk thank you, I used BouncyCastle C# library, please write it as an answer to close this thread.

– ARH
Jan 2 at 10:48












2 Answers
2






active

oldest

votes


















0














The Bouncycastle C# library has some helper classes that can make this relatively easy. It is not well documented unfortunately. Here is an example:



using System;
using System.IO;
using System.Security.Cryptography;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;

namespace ExportToStandardFormats
{
class MainClass
{

public static void Main(string args)
{
var rsa = new RSACryptoServiceProvider(2048);
var rsaKeyPair = DotNetUtilities.GetRsaKeyPair(rsa);
var writer = new StringWriter();
var pemWriter = new PemWriter(writer);
pemWriter.WriteObject(rsaKeyPair.Public);
pemWriter.WriteObject(rsaKeyPair.Private);
Console.WriteLine(writer);
}
}
}





share|improve this answer































    1














    Starting in .NET Core 3.0, this is (largely) built-in.



    Writing SubjectPublicKeyInfo and RSAPrivateKey



    .NET Core 3.0 built-in API



    The output of the builtin API is the binary representation, to make them PEM you need to output the header, footer, and base64:



    private static string MakePem(byte ber, string header)
    {
    StringBuilder builder = new StringBuilder("-----BEGIN ");
    builder.Append(header);
    builder.AppendLine("-----");

    string base64 = Convert.ToBase64String(ber);
    int offset = 0;
    const int LineLength = 64;

    while (offset < base64.Length)
    {
    int lineEnd = Math.Min(offset + LineLength, base64.Length);
    builder.AppendLine(base64.Substring(offset, lineEnd - offset));
    offset = lineEnd;
    }

    builder.Append("-----END ");
    builder.Append(header);
    builder.AppendLine("-----");
    return builder.ToString();
    }


    So to produce the strings:



    string publicKey = MakePem(rsa.ExportSubjectPublicKeyInfo(), "PUBLIC KEY");
    string privateKey = MakePem(rsa.ExportRSAPrivateKey(), "RSA PRIVATE KEY");


    Semi-manually



    If you can't use .NET Core 3.0, but you can use pre-release NuGet packages, you can make use of the prototype ASN.1 writer package (which is the same code that's used internally in .NET Core 3.0; it's just that the API surface isn't finalized).



    To make the public key:



    private static string ToSubjectPublicKeyInfo(RSA rsa)
    {
    RSAParameters rsaParameters = rsa.ExportParameters(false);

    AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
    writer.PushSequence();

    writer.PushSequence();
    writer.WriteObjectIdentifier("1.2.840.113549.1.1.1");
    writer.WriteNull();
    writer.PopSequence();

    AsnWriter innerWriter = new AsnWriter(AsnEncodingRules.DER);

    innerWriter.PushSequence();
    WriteRSAParameter(innerWriter, rsaParameters.Modulus);
    WriteRSAParameter(innerWriter, rsaParameters.Exponent);
    innerWriter.PopSequence();

    writer.WriteBitString(innerWriter.Encode());

    writer.PopSequence();
    return MakePem(writer.Encode(), "PUBLIC KEY");
    }


    And to make the private key:



    private static string ToRSAPrivateKey(RSA rsa)
    {
    RSAParameters rsaParameters = rsa.ExportParameters(true);

    AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
    writer.PushSequence();

    writer.WriteInteger(0);
    WriteRSAParameter(writer, rsaParameters.Modulus);
    WriteRSAParameter(writer, rsaParameters.Exponent);
    WriteRSAParameter(writer, rsaParameters.D);
    WriteRSAParameter(writer, rsaParameters.P);
    WriteRSAParameter(writer, rsaParameters.Q);
    WriteRSAParameter(writer, rsaParameters.DP);
    WriteRSAParameter(writer, rsaParameters.DQ);
    WriteRSAParameter(writer, rsaParameters.InverseQ);

    writer.PopSequence();
    return MakePem(writer.Encode(), "RSA PRIVATE KEY");
    }


    Reading them back



    .NET Core 3.0 built-in API



    Except that .NET Core 3.0 doesn't understand PEM encoding, so you have to do PEM->binary yourself:



    private const string RsaPrivateKey = "RSA PRIVATE KEY";
    private const string SubjectPublicKeyInfo = "PUBLIC KEY";

    private static byte PemToBer(string pem, string header)
    {
    // Technically these should include a newline at the end,
    // and either newline-or-beginning-of-data at the beginning.
    string begin = $"-----BEGIN {header}-----";
    string end = $"-----END {header}-----";

    int beginIdx = pem.IndexOf(begin);
    int base64Start = beginIdx + begin.Length;
    int endIdx = pem.IndexOf(end, base64Start);

    return Convert.FromBase64String(pem.Substring(base64Start, endIdx - base64Start));
    }


    Once that's done you can now load the keys:



    using (RSA rsa = RSA.Create())
    {
    rsa.ImportRSAPrivateKey(PemToBer(pemPrivateKey, RsaPrivateKey), out _);

    ...
    }

    using (RSA rsa = RSA.Create())
    {
    rsa.ImportSubjectPublicKeyInfo(PemToBer(pemPublicKey, SubjectPublicKeyInfo), out _);

    ...
    }


    Semi-manually



    If you can't use .NET Core 3.0, but you can use pre-release NuGet packages, you can make use of the prototype ASN.1 reader package (which is the same code that's used internally in .NET Core 3.0; it's just that the API surface isn't finalized).



    For the public key:



    private static RSA FromSubjectPublicKeyInfo(string pem)
    {
    AsnReader reader = new AsnReader(PemToBer(pem, SubjectPublicKeyInfo), AsnEncodingRules.DER);
    AsnReader spki = reader.ReadSequence();
    reader.ThrowIfNotEmpty();

    AsnReader algorithmId = spki.ReadSequence();

    if (algorithmId.ReadObjectIdentifierAsString() != "1.2.840.113549.1.1.1")
    {
    throw new InvalidOperationException();
    }

    algorithmId.ReadNull();
    algorithmId.ThrowIfNotEmpty();

    AsnReader rsaPublicKey = spki.ReadSequence();

    RSAParameters rsaParameters = new RSAParameters
    {
    Modulus = ReadNormalizedInteger(rsaPublicKey),
    Exponent = ReadNormalizedInteger(rsaPublicKey),
    };

    rsaPublicKey.ThrowIfNotEmpty();

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

    private static byte ReadNormalizedInteger(AsnReader reader)
    {
    ReadOnlyMemory<byte> memory = reader.ReadIntegerBytes();
    ReadOnlySpan<byte> span = memory.Span;

    if (span[0] == 0)
    {
    span = span.Slice(1);
    }

    return span.ToArray();
    }


    And because the private key values have to have the correct size arrays, the private key one is just a little trickier:



    private static RSA FromRSAPrivateKey(string pem)
    {
    AsnReader reader = new AsnReader(PemToBer(pem, RsaPrivateKey), AsnEncodingRules.DER);
    AsnReader rsaPrivateKey = reader.ReadSequence();
    reader.ThrowIfNotEmpty();

    if (!rsaPrivateKey.TryReadInt32(out int version) || version != 0)
    {
    throw new InvalidOperationException();
    }

    byte modulus = ReadNormalizedInteger(rsaPrivateKey);
    int halfModulusLen = (modulus.Length + 1) / 2;

    RSAParameters rsaParameters = new RSAParameters
    {
    Modulus = modulus,
    Exponent = ReadNormalizedInteger(rsaPrivateKey),
    D = ReadNormalizedInteger(rsaPrivateKey, modulus.Length),
    P = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
    Q = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
    DP = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
    DQ = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
    InverseQ = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
    };

    rsaPrivateKey.ThrowIfNotEmpty();

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

    private static byte ReadNormalizedInteger(AsnReader reader, int length)
    {
    ReadOnlyMemory<byte> memory = reader.ReadIntegerBytes();
    ReadOnlySpan<byte> span = memory.Span;

    if (span[0] == 0)
    {
    span = span.Slice(1);
    }

    byte buf = new byte[length];
    int skipSize = length - span.Length;
    span.CopyTo(buf.AsSpan(skipSize));
    return buf;
    }





    share|improve this answer

























      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53997902%2fhow-to-get-rsacryptoserviceprovider-public-and-private-key-only-in-c-sharp%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      2 Answers
      2






      active

      oldest

      votes








      2 Answers
      2






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      0














      The Bouncycastle C# library has some helper classes that can make this relatively easy. It is not well documented unfortunately. Here is an example:



      using System;
      using System.IO;
      using System.Security.Cryptography;
      using Org.BouncyCastle.OpenSsl;
      using Org.BouncyCastle.Security;

      namespace ExportToStandardFormats
      {
      class MainClass
      {

      public static void Main(string args)
      {
      var rsa = new RSACryptoServiceProvider(2048);
      var rsaKeyPair = DotNetUtilities.GetRsaKeyPair(rsa);
      var writer = new StringWriter();
      var pemWriter = new PemWriter(writer);
      pemWriter.WriteObject(rsaKeyPair.Public);
      pemWriter.WriteObject(rsaKeyPair.Private);
      Console.WriteLine(writer);
      }
      }
      }





      share|improve this answer




























        0














        The Bouncycastle C# library has some helper classes that can make this relatively easy. It is not well documented unfortunately. Here is an example:



        using System;
        using System.IO;
        using System.Security.Cryptography;
        using Org.BouncyCastle.OpenSsl;
        using Org.BouncyCastle.Security;

        namespace ExportToStandardFormats
        {
        class MainClass
        {

        public static void Main(string args)
        {
        var rsa = new RSACryptoServiceProvider(2048);
        var rsaKeyPair = DotNetUtilities.GetRsaKeyPair(rsa);
        var writer = new StringWriter();
        var pemWriter = new PemWriter(writer);
        pemWriter.WriteObject(rsaKeyPair.Public);
        pemWriter.WriteObject(rsaKeyPair.Private);
        Console.WriteLine(writer);
        }
        }
        }





        share|improve this answer


























          0












          0








          0







          The Bouncycastle C# library has some helper classes that can make this relatively easy. It is not well documented unfortunately. Here is an example:



          using System;
          using System.IO;
          using System.Security.Cryptography;
          using Org.BouncyCastle.OpenSsl;
          using Org.BouncyCastle.Security;

          namespace ExportToStandardFormats
          {
          class MainClass
          {

          public static void Main(string args)
          {
          var rsa = new RSACryptoServiceProvider(2048);
          var rsaKeyPair = DotNetUtilities.GetRsaKeyPair(rsa);
          var writer = new StringWriter();
          var pemWriter = new PemWriter(writer);
          pemWriter.WriteObject(rsaKeyPair.Public);
          pemWriter.WriteObject(rsaKeyPair.Private);
          Console.WriteLine(writer);
          }
          }
          }





          share|improve this answer













          The Bouncycastle C# library has some helper classes that can make this relatively easy. It is not well documented unfortunately. Here is an example:



          using System;
          using System.IO;
          using System.Security.Cryptography;
          using Org.BouncyCastle.OpenSsl;
          using Org.BouncyCastle.Security;

          namespace ExportToStandardFormats
          {
          class MainClass
          {

          public static void Main(string args)
          {
          var rsa = new RSACryptoServiceProvider(2048);
          var rsaKeyPair = DotNetUtilities.GetRsaKeyPair(rsa);
          var writer = new StringWriter();
          var pemWriter = new PemWriter(writer);
          pemWriter.WriteObject(rsaKeyPair.Public);
          pemWriter.WriteObject(rsaKeyPair.Private);
          Console.WriteLine(writer);
          }
          }
          }






          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Jan 2 at 19:27









          James K PolkJames K Polk

          30.3k116897




          30.3k116897

























              1














              Starting in .NET Core 3.0, this is (largely) built-in.



              Writing SubjectPublicKeyInfo and RSAPrivateKey



              .NET Core 3.0 built-in API



              The output of the builtin API is the binary representation, to make them PEM you need to output the header, footer, and base64:



              private static string MakePem(byte ber, string header)
              {
              StringBuilder builder = new StringBuilder("-----BEGIN ");
              builder.Append(header);
              builder.AppendLine("-----");

              string base64 = Convert.ToBase64String(ber);
              int offset = 0;
              const int LineLength = 64;

              while (offset < base64.Length)
              {
              int lineEnd = Math.Min(offset + LineLength, base64.Length);
              builder.AppendLine(base64.Substring(offset, lineEnd - offset));
              offset = lineEnd;
              }

              builder.Append("-----END ");
              builder.Append(header);
              builder.AppendLine("-----");
              return builder.ToString();
              }


              So to produce the strings:



              string publicKey = MakePem(rsa.ExportSubjectPublicKeyInfo(), "PUBLIC KEY");
              string privateKey = MakePem(rsa.ExportRSAPrivateKey(), "RSA PRIVATE KEY");


              Semi-manually



              If you can't use .NET Core 3.0, but you can use pre-release NuGet packages, you can make use of the prototype ASN.1 writer package (which is the same code that's used internally in .NET Core 3.0; it's just that the API surface isn't finalized).



              To make the public key:



              private static string ToSubjectPublicKeyInfo(RSA rsa)
              {
              RSAParameters rsaParameters = rsa.ExportParameters(false);

              AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
              writer.PushSequence();

              writer.PushSequence();
              writer.WriteObjectIdentifier("1.2.840.113549.1.1.1");
              writer.WriteNull();
              writer.PopSequence();

              AsnWriter innerWriter = new AsnWriter(AsnEncodingRules.DER);

              innerWriter.PushSequence();
              WriteRSAParameter(innerWriter, rsaParameters.Modulus);
              WriteRSAParameter(innerWriter, rsaParameters.Exponent);
              innerWriter.PopSequence();

              writer.WriteBitString(innerWriter.Encode());

              writer.PopSequence();
              return MakePem(writer.Encode(), "PUBLIC KEY");
              }


              And to make the private key:



              private static string ToRSAPrivateKey(RSA rsa)
              {
              RSAParameters rsaParameters = rsa.ExportParameters(true);

              AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
              writer.PushSequence();

              writer.WriteInteger(0);
              WriteRSAParameter(writer, rsaParameters.Modulus);
              WriteRSAParameter(writer, rsaParameters.Exponent);
              WriteRSAParameter(writer, rsaParameters.D);
              WriteRSAParameter(writer, rsaParameters.P);
              WriteRSAParameter(writer, rsaParameters.Q);
              WriteRSAParameter(writer, rsaParameters.DP);
              WriteRSAParameter(writer, rsaParameters.DQ);
              WriteRSAParameter(writer, rsaParameters.InverseQ);

              writer.PopSequence();
              return MakePem(writer.Encode(), "RSA PRIVATE KEY");
              }


              Reading them back



              .NET Core 3.0 built-in API



              Except that .NET Core 3.0 doesn't understand PEM encoding, so you have to do PEM->binary yourself:



              private const string RsaPrivateKey = "RSA PRIVATE KEY";
              private const string SubjectPublicKeyInfo = "PUBLIC KEY";

              private static byte PemToBer(string pem, string header)
              {
              // Technically these should include a newline at the end,
              // and either newline-or-beginning-of-data at the beginning.
              string begin = $"-----BEGIN {header}-----";
              string end = $"-----END {header}-----";

              int beginIdx = pem.IndexOf(begin);
              int base64Start = beginIdx + begin.Length;
              int endIdx = pem.IndexOf(end, base64Start);

              return Convert.FromBase64String(pem.Substring(base64Start, endIdx - base64Start));
              }


              Once that's done you can now load the keys:



              using (RSA rsa = RSA.Create())
              {
              rsa.ImportRSAPrivateKey(PemToBer(pemPrivateKey, RsaPrivateKey), out _);

              ...
              }

              using (RSA rsa = RSA.Create())
              {
              rsa.ImportSubjectPublicKeyInfo(PemToBer(pemPublicKey, SubjectPublicKeyInfo), out _);

              ...
              }


              Semi-manually



              If you can't use .NET Core 3.0, but you can use pre-release NuGet packages, you can make use of the prototype ASN.1 reader package (which is the same code that's used internally in .NET Core 3.0; it's just that the API surface isn't finalized).



              For the public key:



              private static RSA FromSubjectPublicKeyInfo(string pem)
              {
              AsnReader reader = new AsnReader(PemToBer(pem, SubjectPublicKeyInfo), AsnEncodingRules.DER);
              AsnReader spki = reader.ReadSequence();
              reader.ThrowIfNotEmpty();

              AsnReader algorithmId = spki.ReadSequence();

              if (algorithmId.ReadObjectIdentifierAsString() != "1.2.840.113549.1.1.1")
              {
              throw new InvalidOperationException();
              }

              algorithmId.ReadNull();
              algorithmId.ThrowIfNotEmpty();

              AsnReader rsaPublicKey = spki.ReadSequence();

              RSAParameters rsaParameters = new RSAParameters
              {
              Modulus = ReadNormalizedInteger(rsaPublicKey),
              Exponent = ReadNormalizedInteger(rsaPublicKey),
              };

              rsaPublicKey.ThrowIfNotEmpty();

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

              private static byte ReadNormalizedInteger(AsnReader reader)
              {
              ReadOnlyMemory<byte> memory = reader.ReadIntegerBytes();
              ReadOnlySpan<byte> span = memory.Span;

              if (span[0] == 0)
              {
              span = span.Slice(1);
              }

              return span.ToArray();
              }


              And because the private key values have to have the correct size arrays, the private key one is just a little trickier:



              private static RSA FromRSAPrivateKey(string pem)
              {
              AsnReader reader = new AsnReader(PemToBer(pem, RsaPrivateKey), AsnEncodingRules.DER);
              AsnReader rsaPrivateKey = reader.ReadSequence();
              reader.ThrowIfNotEmpty();

              if (!rsaPrivateKey.TryReadInt32(out int version) || version != 0)
              {
              throw new InvalidOperationException();
              }

              byte modulus = ReadNormalizedInteger(rsaPrivateKey);
              int halfModulusLen = (modulus.Length + 1) / 2;

              RSAParameters rsaParameters = new RSAParameters
              {
              Modulus = modulus,
              Exponent = ReadNormalizedInteger(rsaPrivateKey),
              D = ReadNormalizedInteger(rsaPrivateKey, modulus.Length),
              P = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
              Q = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
              DP = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
              DQ = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
              InverseQ = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
              };

              rsaPrivateKey.ThrowIfNotEmpty();

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

              private static byte ReadNormalizedInteger(AsnReader reader, int length)
              {
              ReadOnlyMemory<byte> memory = reader.ReadIntegerBytes();
              ReadOnlySpan<byte> span = memory.Span;

              if (span[0] == 0)
              {
              span = span.Slice(1);
              }

              byte buf = new byte[length];
              int skipSize = length - span.Length;
              span.CopyTo(buf.AsSpan(skipSize));
              return buf;
              }





              share|improve this answer






























                1














                Starting in .NET Core 3.0, this is (largely) built-in.



                Writing SubjectPublicKeyInfo and RSAPrivateKey



                .NET Core 3.0 built-in API



                The output of the builtin API is the binary representation, to make them PEM you need to output the header, footer, and base64:



                private static string MakePem(byte ber, string header)
                {
                StringBuilder builder = new StringBuilder("-----BEGIN ");
                builder.Append(header);
                builder.AppendLine("-----");

                string base64 = Convert.ToBase64String(ber);
                int offset = 0;
                const int LineLength = 64;

                while (offset < base64.Length)
                {
                int lineEnd = Math.Min(offset + LineLength, base64.Length);
                builder.AppendLine(base64.Substring(offset, lineEnd - offset));
                offset = lineEnd;
                }

                builder.Append("-----END ");
                builder.Append(header);
                builder.AppendLine("-----");
                return builder.ToString();
                }


                So to produce the strings:



                string publicKey = MakePem(rsa.ExportSubjectPublicKeyInfo(), "PUBLIC KEY");
                string privateKey = MakePem(rsa.ExportRSAPrivateKey(), "RSA PRIVATE KEY");


                Semi-manually



                If you can't use .NET Core 3.0, but you can use pre-release NuGet packages, you can make use of the prototype ASN.1 writer package (which is the same code that's used internally in .NET Core 3.0; it's just that the API surface isn't finalized).



                To make the public key:



                private static string ToSubjectPublicKeyInfo(RSA rsa)
                {
                RSAParameters rsaParameters = rsa.ExportParameters(false);

                AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
                writer.PushSequence();

                writer.PushSequence();
                writer.WriteObjectIdentifier("1.2.840.113549.1.1.1");
                writer.WriteNull();
                writer.PopSequence();

                AsnWriter innerWriter = new AsnWriter(AsnEncodingRules.DER);

                innerWriter.PushSequence();
                WriteRSAParameter(innerWriter, rsaParameters.Modulus);
                WriteRSAParameter(innerWriter, rsaParameters.Exponent);
                innerWriter.PopSequence();

                writer.WriteBitString(innerWriter.Encode());

                writer.PopSequence();
                return MakePem(writer.Encode(), "PUBLIC KEY");
                }


                And to make the private key:



                private static string ToRSAPrivateKey(RSA rsa)
                {
                RSAParameters rsaParameters = rsa.ExportParameters(true);

                AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
                writer.PushSequence();

                writer.WriteInteger(0);
                WriteRSAParameter(writer, rsaParameters.Modulus);
                WriteRSAParameter(writer, rsaParameters.Exponent);
                WriteRSAParameter(writer, rsaParameters.D);
                WriteRSAParameter(writer, rsaParameters.P);
                WriteRSAParameter(writer, rsaParameters.Q);
                WriteRSAParameter(writer, rsaParameters.DP);
                WriteRSAParameter(writer, rsaParameters.DQ);
                WriteRSAParameter(writer, rsaParameters.InverseQ);

                writer.PopSequence();
                return MakePem(writer.Encode(), "RSA PRIVATE KEY");
                }


                Reading them back



                .NET Core 3.0 built-in API



                Except that .NET Core 3.0 doesn't understand PEM encoding, so you have to do PEM->binary yourself:



                private const string RsaPrivateKey = "RSA PRIVATE KEY";
                private const string SubjectPublicKeyInfo = "PUBLIC KEY";

                private static byte PemToBer(string pem, string header)
                {
                // Technically these should include a newline at the end,
                // and either newline-or-beginning-of-data at the beginning.
                string begin = $"-----BEGIN {header}-----";
                string end = $"-----END {header}-----";

                int beginIdx = pem.IndexOf(begin);
                int base64Start = beginIdx + begin.Length;
                int endIdx = pem.IndexOf(end, base64Start);

                return Convert.FromBase64String(pem.Substring(base64Start, endIdx - base64Start));
                }


                Once that's done you can now load the keys:



                using (RSA rsa = RSA.Create())
                {
                rsa.ImportRSAPrivateKey(PemToBer(pemPrivateKey, RsaPrivateKey), out _);

                ...
                }

                using (RSA rsa = RSA.Create())
                {
                rsa.ImportSubjectPublicKeyInfo(PemToBer(pemPublicKey, SubjectPublicKeyInfo), out _);

                ...
                }


                Semi-manually



                If you can't use .NET Core 3.0, but you can use pre-release NuGet packages, you can make use of the prototype ASN.1 reader package (which is the same code that's used internally in .NET Core 3.0; it's just that the API surface isn't finalized).



                For the public key:



                private static RSA FromSubjectPublicKeyInfo(string pem)
                {
                AsnReader reader = new AsnReader(PemToBer(pem, SubjectPublicKeyInfo), AsnEncodingRules.DER);
                AsnReader spki = reader.ReadSequence();
                reader.ThrowIfNotEmpty();

                AsnReader algorithmId = spki.ReadSequence();

                if (algorithmId.ReadObjectIdentifierAsString() != "1.2.840.113549.1.1.1")
                {
                throw new InvalidOperationException();
                }

                algorithmId.ReadNull();
                algorithmId.ThrowIfNotEmpty();

                AsnReader rsaPublicKey = spki.ReadSequence();

                RSAParameters rsaParameters = new RSAParameters
                {
                Modulus = ReadNormalizedInteger(rsaPublicKey),
                Exponent = ReadNormalizedInteger(rsaPublicKey),
                };

                rsaPublicKey.ThrowIfNotEmpty();

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

                private static byte ReadNormalizedInteger(AsnReader reader)
                {
                ReadOnlyMemory<byte> memory = reader.ReadIntegerBytes();
                ReadOnlySpan<byte> span = memory.Span;

                if (span[0] == 0)
                {
                span = span.Slice(1);
                }

                return span.ToArray();
                }


                And because the private key values have to have the correct size arrays, the private key one is just a little trickier:



                private static RSA FromRSAPrivateKey(string pem)
                {
                AsnReader reader = new AsnReader(PemToBer(pem, RsaPrivateKey), AsnEncodingRules.DER);
                AsnReader rsaPrivateKey = reader.ReadSequence();
                reader.ThrowIfNotEmpty();

                if (!rsaPrivateKey.TryReadInt32(out int version) || version != 0)
                {
                throw new InvalidOperationException();
                }

                byte modulus = ReadNormalizedInteger(rsaPrivateKey);
                int halfModulusLen = (modulus.Length + 1) / 2;

                RSAParameters rsaParameters = new RSAParameters
                {
                Modulus = modulus,
                Exponent = ReadNormalizedInteger(rsaPrivateKey),
                D = ReadNormalizedInteger(rsaPrivateKey, modulus.Length),
                P = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
                Q = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
                DP = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
                DQ = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
                InverseQ = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
                };

                rsaPrivateKey.ThrowIfNotEmpty();

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

                private static byte ReadNormalizedInteger(AsnReader reader, int length)
                {
                ReadOnlyMemory<byte> memory = reader.ReadIntegerBytes();
                ReadOnlySpan<byte> span = memory.Span;

                if (span[0] == 0)
                {
                span = span.Slice(1);
                }

                byte buf = new byte[length];
                int skipSize = length - span.Length;
                span.CopyTo(buf.AsSpan(skipSize));
                return buf;
                }





                share|improve this answer




























                  1












                  1








                  1







                  Starting in .NET Core 3.0, this is (largely) built-in.



                  Writing SubjectPublicKeyInfo and RSAPrivateKey



                  .NET Core 3.0 built-in API



                  The output of the builtin API is the binary representation, to make them PEM you need to output the header, footer, and base64:



                  private static string MakePem(byte ber, string header)
                  {
                  StringBuilder builder = new StringBuilder("-----BEGIN ");
                  builder.Append(header);
                  builder.AppendLine("-----");

                  string base64 = Convert.ToBase64String(ber);
                  int offset = 0;
                  const int LineLength = 64;

                  while (offset < base64.Length)
                  {
                  int lineEnd = Math.Min(offset + LineLength, base64.Length);
                  builder.AppendLine(base64.Substring(offset, lineEnd - offset));
                  offset = lineEnd;
                  }

                  builder.Append("-----END ");
                  builder.Append(header);
                  builder.AppendLine("-----");
                  return builder.ToString();
                  }


                  So to produce the strings:



                  string publicKey = MakePem(rsa.ExportSubjectPublicKeyInfo(), "PUBLIC KEY");
                  string privateKey = MakePem(rsa.ExportRSAPrivateKey(), "RSA PRIVATE KEY");


                  Semi-manually



                  If you can't use .NET Core 3.0, but you can use pre-release NuGet packages, you can make use of the prototype ASN.1 writer package (which is the same code that's used internally in .NET Core 3.0; it's just that the API surface isn't finalized).



                  To make the public key:



                  private static string ToSubjectPublicKeyInfo(RSA rsa)
                  {
                  RSAParameters rsaParameters = rsa.ExportParameters(false);

                  AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
                  writer.PushSequence();

                  writer.PushSequence();
                  writer.WriteObjectIdentifier("1.2.840.113549.1.1.1");
                  writer.WriteNull();
                  writer.PopSequence();

                  AsnWriter innerWriter = new AsnWriter(AsnEncodingRules.DER);

                  innerWriter.PushSequence();
                  WriteRSAParameter(innerWriter, rsaParameters.Modulus);
                  WriteRSAParameter(innerWriter, rsaParameters.Exponent);
                  innerWriter.PopSequence();

                  writer.WriteBitString(innerWriter.Encode());

                  writer.PopSequence();
                  return MakePem(writer.Encode(), "PUBLIC KEY");
                  }


                  And to make the private key:



                  private static string ToRSAPrivateKey(RSA rsa)
                  {
                  RSAParameters rsaParameters = rsa.ExportParameters(true);

                  AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
                  writer.PushSequence();

                  writer.WriteInteger(0);
                  WriteRSAParameter(writer, rsaParameters.Modulus);
                  WriteRSAParameter(writer, rsaParameters.Exponent);
                  WriteRSAParameter(writer, rsaParameters.D);
                  WriteRSAParameter(writer, rsaParameters.P);
                  WriteRSAParameter(writer, rsaParameters.Q);
                  WriteRSAParameter(writer, rsaParameters.DP);
                  WriteRSAParameter(writer, rsaParameters.DQ);
                  WriteRSAParameter(writer, rsaParameters.InverseQ);

                  writer.PopSequence();
                  return MakePem(writer.Encode(), "RSA PRIVATE KEY");
                  }


                  Reading them back



                  .NET Core 3.0 built-in API



                  Except that .NET Core 3.0 doesn't understand PEM encoding, so you have to do PEM->binary yourself:



                  private const string RsaPrivateKey = "RSA PRIVATE KEY";
                  private const string SubjectPublicKeyInfo = "PUBLIC KEY";

                  private static byte PemToBer(string pem, string header)
                  {
                  // Technically these should include a newline at the end,
                  // and either newline-or-beginning-of-data at the beginning.
                  string begin = $"-----BEGIN {header}-----";
                  string end = $"-----END {header}-----";

                  int beginIdx = pem.IndexOf(begin);
                  int base64Start = beginIdx + begin.Length;
                  int endIdx = pem.IndexOf(end, base64Start);

                  return Convert.FromBase64String(pem.Substring(base64Start, endIdx - base64Start));
                  }


                  Once that's done you can now load the keys:



                  using (RSA rsa = RSA.Create())
                  {
                  rsa.ImportRSAPrivateKey(PemToBer(pemPrivateKey, RsaPrivateKey), out _);

                  ...
                  }

                  using (RSA rsa = RSA.Create())
                  {
                  rsa.ImportSubjectPublicKeyInfo(PemToBer(pemPublicKey, SubjectPublicKeyInfo), out _);

                  ...
                  }


                  Semi-manually



                  If you can't use .NET Core 3.0, but you can use pre-release NuGet packages, you can make use of the prototype ASN.1 reader package (which is the same code that's used internally in .NET Core 3.0; it's just that the API surface isn't finalized).



                  For the public key:



                  private static RSA FromSubjectPublicKeyInfo(string pem)
                  {
                  AsnReader reader = new AsnReader(PemToBer(pem, SubjectPublicKeyInfo), AsnEncodingRules.DER);
                  AsnReader spki = reader.ReadSequence();
                  reader.ThrowIfNotEmpty();

                  AsnReader algorithmId = spki.ReadSequence();

                  if (algorithmId.ReadObjectIdentifierAsString() != "1.2.840.113549.1.1.1")
                  {
                  throw new InvalidOperationException();
                  }

                  algorithmId.ReadNull();
                  algorithmId.ThrowIfNotEmpty();

                  AsnReader rsaPublicKey = spki.ReadSequence();

                  RSAParameters rsaParameters = new RSAParameters
                  {
                  Modulus = ReadNormalizedInteger(rsaPublicKey),
                  Exponent = ReadNormalizedInteger(rsaPublicKey),
                  };

                  rsaPublicKey.ThrowIfNotEmpty();

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

                  private static byte ReadNormalizedInteger(AsnReader reader)
                  {
                  ReadOnlyMemory<byte> memory = reader.ReadIntegerBytes();
                  ReadOnlySpan<byte> span = memory.Span;

                  if (span[0] == 0)
                  {
                  span = span.Slice(1);
                  }

                  return span.ToArray();
                  }


                  And because the private key values have to have the correct size arrays, the private key one is just a little trickier:



                  private static RSA FromRSAPrivateKey(string pem)
                  {
                  AsnReader reader = new AsnReader(PemToBer(pem, RsaPrivateKey), AsnEncodingRules.DER);
                  AsnReader rsaPrivateKey = reader.ReadSequence();
                  reader.ThrowIfNotEmpty();

                  if (!rsaPrivateKey.TryReadInt32(out int version) || version != 0)
                  {
                  throw new InvalidOperationException();
                  }

                  byte modulus = ReadNormalizedInteger(rsaPrivateKey);
                  int halfModulusLen = (modulus.Length + 1) / 2;

                  RSAParameters rsaParameters = new RSAParameters
                  {
                  Modulus = modulus,
                  Exponent = ReadNormalizedInteger(rsaPrivateKey),
                  D = ReadNormalizedInteger(rsaPrivateKey, modulus.Length),
                  P = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
                  Q = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
                  DP = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
                  DQ = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
                  InverseQ = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
                  };

                  rsaPrivateKey.ThrowIfNotEmpty();

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

                  private static byte ReadNormalizedInteger(AsnReader reader, int length)
                  {
                  ReadOnlyMemory<byte> memory = reader.ReadIntegerBytes();
                  ReadOnlySpan<byte> span = memory.Span;

                  if (span[0] == 0)
                  {
                  span = span.Slice(1);
                  }

                  byte buf = new byte[length];
                  int skipSize = length - span.Length;
                  span.CopyTo(buf.AsSpan(skipSize));
                  return buf;
                  }





                  share|improve this answer















                  Starting in .NET Core 3.0, this is (largely) built-in.



                  Writing SubjectPublicKeyInfo and RSAPrivateKey



                  .NET Core 3.0 built-in API



                  The output of the builtin API is the binary representation, to make them PEM you need to output the header, footer, and base64:



                  private static string MakePem(byte ber, string header)
                  {
                  StringBuilder builder = new StringBuilder("-----BEGIN ");
                  builder.Append(header);
                  builder.AppendLine("-----");

                  string base64 = Convert.ToBase64String(ber);
                  int offset = 0;
                  const int LineLength = 64;

                  while (offset < base64.Length)
                  {
                  int lineEnd = Math.Min(offset + LineLength, base64.Length);
                  builder.AppendLine(base64.Substring(offset, lineEnd - offset));
                  offset = lineEnd;
                  }

                  builder.Append("-----END ");
                  builder.Append(header);
                  builder.AppendLine("-----");
                  return builder.ToString();
                  }


                  So to produce the strings:



                  string publicKey = MakePem(rsa.ExportSubjectPublicKeyInfo(), "PUBLIC KEY");
                  string privateKey = MakePem(rsa.ExportRSAPrivateKey(), "RSA PRIVATE KEY");


                  Semi-manually



                  If you can't use .NET Core 3.0, but you can use pre-release NuGet packages, you can make use of the prototype ASN.1 writer package (which is the same code that's used internally in .NET Core 3.0; it's just that the API surface isn't finalized).



                  To make the public key:



                  private static string ToSubjectPublicKeyInfo(RSA rsa)
                  {
                  RSAParameters rsaParameters = rsa.ExportParameters(false);

                  AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
                  writer.PushSequence();

                  writer.PushSequence();
                  writer.WriteObjectIdentifier("1.2.840.113549.1.1.1");
                  writer.WriteNull();
                  writer.PopSequence();

                  AsnWriter innerWriter = new AsnWriter(AsnEncodingRules.DER);

                  innerWriter.PushSequence();
                  WriteRSAParameter(innerWriter, rsaParameters.Modulus);
                  WriteRSAParameter(innerWriter, rsaParameters.Exponent);
                  innerWriter.PopSequence();

                  writer.WriteBitString(innerWriter.Encode());

                  writer.PopSequence();
                  return MakePem(writer.Encode(), "PUBLIC KEY");
                  }


                  And to make the private key:



                  private static string ToRSAPrivateKey(RSA rsa)
                  {
                  RSAParameters rsaParameters = rsa.ExportParameters(true);

                  AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
                  writer.PushSequence();

                  writer.WriteInteger(0);
                  WriteRSAParameter(writer, rsaParameters.Modulus);
                  WriteRSAParameter(writer, rsaParameters.Exponent);
                  WriteRSAParameter(writer, rsaParameters.D);
                  WriteRSAParameter(writer, rsaParameters.P);
                  WriteRSAParameter(writer, rsaParameters.Q);
                  WriteRSAParameter(writer, rsaParameters.DP);
                  WriteRSAParameter(writer, rsaParameters.DQ);
                  WriteRSAParameter(writer, rsaParameters.InverseQ);

                  writer.PopSequence();
                  return MakePem(writer.Encode(), "RSA PRIVATE KEY");
                  }


                  Reading them back



                  .NET Core 3.0 built-in API



                  Except that .NET Core 3.0 doesn't understand PEM encoding, so you have to do PEM->binary yourself:



                  private const string RsaPrivateKey = "RSA PRIVATE KEY";
                  private const string SubjectPublicKeyInfo = "PUBLIC KEY";

                  private static byte PemToBer(string pem, string header)
                  {
                  // Technically these should include a newline at the end,
                  // and either newline-or-beginning-of-data at the beginning.
                  string begin = $"-----BEGIN {header}-----";
                  string end = $"-----END {header}-----";

                  int beginIdx = pem.IndexOf(begin);
                  int base64Start = beginIdx + begin.Length;
                  int endIdx = pem.IndexOf(end, base64Start);

                  return Convert.FromBase64String(pem.Substring(base64Start, endIdx - base64Start));
                  }


                  Once that's done you can now load the keys:



                  using (RSA rsa = RSA.Create())
                  {
                  rsa.ImportRSAPrivateKey(PemToBer(pemPrivateKey, RsaPrivateKey), out _);

                  ...
                  }

                  using (RSA rsa = RSA.Create())
                  {
                  rsa.ImportSubjectPublicKeyInfo(PemToBer(pemPublicKey, SubjectPublicKeyInfo), out _);

                  ...
                  }


                  Semi-manually



                  If you can't use .NET Core 3.0, but you can use pre-release NuGet packages, you can make use of the prototype ASN.1 reader package (which is the same code that's used internally in .NET Core 3.0; it's just that the API surface isn't finalized).



                  For the public key:



                  private static RSA FromSubjectPublicKeyInfo(string pem)
                  {
                  AsnReader reader = new AsnReader(PemToBer(pem, SubjectPublicKeyInfo), AsnEncodingRules.DER);
                  AsnReader spki = reader.ReadSequence();
                  reader.ThrowIfNotEmpty();

                  AsnReader algorithmId = spki.ReadSequence();

                  if (algorithmId.ReadObjectIdentifierAsString() != "1.2.840.113549.1.1.1")
                  {
                  throw new InvalidOperationException();
                  }

                  algorithmId.ReadNull();
                  algorithmId.ThrowIfNotEmpty();

                  AsnReader rsaPublicKey = spki.ReadSequence();

                  RSAParameters rsaParameters = new RSAParameters
                  {
                  Modulus = ReadNormalizedInteger(rsaPublicKey),
                  Exponent = ReadNormalizedInteger(rsaPublicKey),
                  };

                  rsaPublicKey.ThrowIfNotEmpty();

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

                  private static byte ReadNormalizedInteger(AsnReader reader)
                  {
                  ReadOnlyMemory<byte> memory = reader.ReadIntegerBytes();
                  ReadOnlySpan<byte> span = memory.Span;

                  if (span[0] == 0)
                  {
                  span = span.Slice(1);
                  }

                  return span.ToArray();
                  }


                  And because the private key values have to have the correct size arrays, the private key one is just a little trickier:



                  private static RSA FromRSAPrivateKey(string pem)
                  {
                  AsnReader reader = new AsnReader(PemToBer(pem, RsaPrivateKey), AsnEncodingRules.DER);
                  AsnReader rsaPrivateKey = reader.ReadSequence();
                  reader.ThrowIfNotEmpty();

                  if (!rsaPrivateKey.TryReadInt32(out int version) || version != 0)
                  {
                  throw new InvalidOperationException();
                  }

                  byte modulus = ReadNormalizedInteger(rsaPrivateKey);
                  int halfModulusLen = (modulus.Length + 1) / 2;

                  RSAParameters rsaParameters = new RSAParameters
                  {
                  Modulus = modulus,
                  Exponent = ReadNormalizedInteger(rsaPrivateKey),
                  D = ReadNormalizedInteger(rsaPrivateKey, modulus.Length),
                  P = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
                  Q = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
                  DP = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
                  DQ = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
                  InverseQ = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
                  };

                  rsaPrivateKey.ThrowIfNotEmpty();

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

                  private static byte ReadNormalizedInteger(AsnReader reader, int length)
                  {
                  ReadOnlyMemory<byte> memory = reader.ReadIntegerBytes();
                  ReadOnlySpan<byte> span = memory.Span;

                  if (span[0] == 0)
                  {
                  span = span.Slice(1);
                  }

                  byte buf = new byte[length];
                  int skipSize = length - span.Length;
                  span.CopyTo(buf.AsSpan(skipSize));
                  return buf;
                  }






                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited Jan 2 at 18:57

























                  answered Jan 2 at 18:14









                  bartonjsbartonjs

                  13.9k12658




                  13.9k12658






























                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53997902%2fhow-to-get-rsacryptoserviceprovider-public-and-private-key-only-in-c-sharp%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      MongoDB - Not Authorized To Execute Command

                      How to fix TextFormField cause rebuild widget in Flutter

                      in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith