Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rsa ToXmlString is not supported on .NET Core 2.0 #23686

Open
danmosemsft opened this Issue Aug 30, 2017 · 29 comments

Comments

Projects
None yet
@danmosemsft
Copy link
Member

commented Aug 30, 2017

@myloveCc commented on Sun Aug 20 2017

rsa.ToXmlString(true) is not supported on .NET Core 2.0

 var rsa = RSA.Create(2048);
 var privateKey = rsa.ToXmlString(true);    // //not supported exception

@bartonjs commented on Mon Aug 21 2017

Yep. In .NET Framework networking, XML, and cryptography all live in one library (mscorlib). In .NET Core they're in three separate libraries, and making this method work requires a circle:

  • XML needs networking, because (even though we discourage it) DTDs can initiate downloads.
  • Networking needs cryptography because TLS/https have X.509 certificates.
  • Cryptography needs XML for ToXmlString/FromXmlString.

The easiest link to snip is the ToXmlString/FromXmlString methods.

Technically ToXmlString can be written without XML (like in .NET Framework's implementation, but FromXmlString requires an XML parser.

My recommendation would be to make a public static string ToXmlString(this RSAParameters rsaParameters) method, and a public static RSAParameters FromXmlString(string xml) method; borrowing from NetFx and CoreFx's SignedXml; and to ultimately try moving off of the XML representations.


@myloveCc commented on Mon Aug 21 2017

This feature is not difficult, and I have been achieved in my project。

using System;
using System.Security.Cryptography;
using System.Xml;
using Newtonsoft.Json;
using NETCore.Encrypt.Shared;
using NETCore.Encrypt.Internal;

namespace NETCore.Encrypt.Extensions.Internal
{

    internal static class RSAKeyExtensions
    {
        #region JSON
        internal static void FromJsonString(this RSA rsa, string jsonString)
        {
            Check.Argument.IsNotEmpty(jsonString, nameof(jsonString));
            try
            {
                var paramsJson = JsonConvert.DeserializeObject<RSAParametersJson>(jsonString);

                RSAParameters parameters = new RSAParameters();

                parameters.Modulus = paramsJson.Modulus != null ? Convert.FromBase64String(paramsJson.Modulus) : null;
                parameters.Exponent = paramsJson.Exponent != null ? Convert.FromBase64String(paramsJson.Exponent) : null;
                parameters.P = paramsJson.P != null ? Convert.FromBase64String(paramsJson.P) : null;
                parameters.Q = paramsJson.Q != null ? Convert.FromBase64String(paramsJson.Q) : null;
                parameters.DP = paramsJson.DP != null ? Convert.FromBase64String(paramsJson.DP) : null;
                parameters.DQ = paramsJson.DQ != null ? Convert.FromBase64String(paramsJson.DQ) : null;
                parameters.InverseQ = paramsJson.InverseQ != null ? Convert.FromBase64String(paramsJson.InverseQ) : null;
                parameters.D = paramsJson.D != null ? Convert.FromBase64String(paramsJson.D) : null;
                rsa.ImportParameters(parameters);
            }
            catch
            {
                throw new Exception("Invalid JSON RSA key.");
            }
        }

        internal static string ToJsonString(this RSA rsa, bool includePrivateParameters)
        {
            RSAParameters parameters = rsa.ExportParameters(includePrivateParameters);

            var parasJson = new RSAParametersJson()
            {
                Modulus = parameters.Modulus != null ? Convert.ToBase64String(parameters.Modulus) : null,
                Exponent = parameters.Exponent != null ? Convert.ToBase64String(parameters.Exponent) : null,
                P = parameters.P != null ? Convert.ToBase64String(parameters.P) : null,
                Q = parameters.Q != null ? Convert.ToBase64String(parameters.Q) : null,
                DP = parameters.DP != null ? Convert.ToBase64String(parameters.DP) : null,
                DQ = parameters.DQ != null ? Convert.ToBase64String(parameters.DQ) : null,
                InverseQ = parameters.InverseQ != null ? Convert.ToBase64String(parameters.InverseQ) : null,
                D = parameters.D != null ? Convert.ToBase64String(parameters.D) : null
            };

            return JsonConvert.SerializeObject(parasJson);
        }
        #endregion

        #region XML

        public static void FromXmlString(this RSA rsa, string xmlString)
        {
            RSAParameters parameters = new RSAParameters();

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xmlString);

            if (xmlDoc.DocumentElement.Name.Equals("RSAKeyValue"))
            {
                foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes)
                {
                    switch (node.Name)
                    {
                        case "Modulus": parameters.Modulus = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "Exponent": parameters.Exponent = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "P": parameters.P = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "Q": parameters.Q = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "DP": parameters.DP = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "DQ": parameters.DQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "InverseQ": parameters.InverseQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "D": parameters.D = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                    }
                }
            }
            else
            {
                throw new Exception("Invalid XML RSA key.");
            }

            rsa.ImportParameters(parameters);
        }

        public static string ToXmlString(this RSA rsa, bool includePrivateParameters)
        {
            RSAParameters parameters = rsa.ExportParameters(includePrivateParameters);

            return string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent><P>{2}</P><Q>{3}</Q><DP>{4}</DP><DQ>{5}</DQ><InverseQ>{6}</InverseQ><D>{7}</D></RSAKeyValue>",
                  parameters.Modulus != null ? Convert.ToBase64String(parameters.Modulus) : null,
                  parameters.Exponent != null ? Convert.ToBase64String(parameters.Exponent) : null,
                  parameters.P != null ? Convert.ToBase64String(parameters.P) : null,
                  parameters.Q != null ? Convert.ToBase64String(parameters.Q) : null,
                  parameters.DP != null ? Convert.ToBase64String(parameters.DP) : null,
                  parameters.DQ != null ? Convert.ToBase64String(parameters.DQ) : null,
                  parameters.InverseQ != null ? Convert.ToBase64String(parameters.InverseQ) : null,
                  parameters.D != null ? Convert.ToBase64String(parameters.D) : null);
        }

        #endregion
    }
}

@Petermarcu commented on Wed Aug 30 2017

@danmosemsft can you get this moved over to dotnet/corefx to be tracked as an issue there?

@danmosemsft

This comment has been minimized.

Copy link
Member Author

commented Aug 30, 2017

@bartonjs I wonder if we should put implementations in a separate library, that could depend on both Crypto and XML? Otherwise everyone has to write/paste similar code.

@danmosemsft

This comment has been minimized.

Copy link
Member Author

commented Aug 30, 2017

These ToXmlSTring/FromXmlSTring methods do seem to have significant usage..

@bartonjs

This comment has been minimized.

Copy link
Member

commented Aug 30, 2017

This is an understood limitation in .NET Core (and on the PNSE warning list, etc). If we continue getting feedback once the PNSE warning tooling is readily available we can look into doing trickery with lightweight XML parsers.

To restate the problem: Networking, XML, and Crypto ended up in a circular dependency in mscorlib. The factoring that we've done in Core makes it not possible to do this right now.

@bartonjs bartonjs closed this Aug 30, 2017

@danmosemsft

This comment has been minimized.

Copy link
Member Author

commented Aug 30, 2017

@bartonjs, I was referring to shipping extension methods like those above. I don't believe that would cause a circular dependency.

@bartonjs

This comment has been minimized.

Copy link
Member

commented Aug 30, 2017

@danmosemsft Yeah, I noticed the usage comment but missed the previous one.

I don't know if the problem is that people want

  • to use XML serialization specifically (which could be solved by a separate package)
  • a compact serialization for keys-without-certificates, in which case #20414 is the better solution (more standardized),
  • existing code/samples to work (which would mean making this method work. By reflection, a lightweight parser, whatever)
@fletchsod-developer

This comment has been minimized.

Copy link

commented Aug 31, 2017

:-( :-( :-(

Suggestion as workaround to it? Because DotNetCore 2.1 and higher are still a work in progress.

@danmosemsft

This comment has been minimized.

Copy link
Member Author

commented Aug 31, 2017

@fletchsod-developer I suggest adding to your project extension methods similar to the ones posted above. (I didn't review that code but essentially that approach)

@strombringer

This comment has been minimized.

Copy link

commented Sep 26, 2017

Just an FYI: The extension method doesn't generate xml in the same format as the original mscorlib method. For public keys it leaves empty tags for P, W, DP, DQ, InverseQ and D, whereas the mscorlib method just omits those tags:

mscorlib:
<RSAKeyValue><Modulus>3ZWrUY0Y6...</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>

extension method
<RSAKeyValue><Modulus>3ZWrUY0Y6...</Modulus><Exponent>AQAB</Exponent><P></P><Q></Q><DP></DP><DQ></DQ><InverseQ></InverseQ><D></D></RSAKeyValue>

@eerhardt

This comment has been minimized.

Copy link
Member

commented Sep 26, 2017

@karelz karelz added this to the 2.1.0 milestone Oct 28, 2017

@mattiasw2

This comment has been minimized.

Copy link

commented Nov 28, 2017

Thank you! Worked fine. Embarrasing that it is only detected in runtime by System.PlatformNotSupportedException

@danmosemsft

This comment has been minimized.

Copy link
Member Author

commented Nov 28, 2017

@pjanotti is this caught by the PNSE tooling?

@mattiasw2

This comment has been minimized.

Copy link

commented Nov 28, 2017

Sorry, do not know what PNSE tooling is. I am only moving some old crypto-code from an old ASP.NET project to Java, and seemed easier to use dotnet core, and call an console app for decryption of old data. Using Visual Studio Code as a test, more used to VS

@danmosemsft

This comment has been minimized.

Copy link
Member Author

commented Nov 28, 2017

@mattiasw2 it is this: https://github.com/dotnet/platform-compat
The intent of it is to help flag cases where code may not be portable despite the API being available. (If the API is unavailable, of course, the compiler will catch it.

@danmosemsft

This comment has been minimized.

Copy link
Member Author

commented Nov 28, 2017

It looks like it should flag it.
https://github.com/dotnet/platform-compat/blob/6613b6b16bc8e22aa3cc2a3debee49f5e2c4ccd6/etc/exceptions.filtered.site.csv#L1109
It woudl be great if you could try it and confirm.

@mattiasw2

This comment has been minimized.

Copy link

commented Nov 28, 2017

It only flags it during runtime by throwing the not supported exception. There is no warning in compile time. Just downloaded latest Visual Studio Core and netcore2.0

@danmosemsft

This comment has been minimized.

Copy link
Member Author

commented Nov 28, 2017

@mattiasw2 the platform-compat tool I linked to has to be installed into VS. As you see in the screenshots https://github.com/dotnet/platform-compat if it works correctly it will then flag issues at compile time.

e-llumin added a commit to e-llumin-net-ltd/rhino-licensing that referenced this issue Jan 22, 2018

e-llumin added a commit to e-llumin-net-ltd/rhino-licensing that referenced this issue Jan 22, 2018

e-llumin added a commit to e-llumin-net-ltd/rhino-licensing that referenced this issue Jan 22, 2018

e-llumin added a commit to e-llumin-net-ltd/rhino-licensing that referenced this issue Jan 22, 2018

@Korporal

This comment has been minimized.

Copy link

commented Mar 4, 2018

@myloveCc @bartonjs @danmosemsft @Petermarcu - Hi.

I'm seeing the same problem but I must say the "solution" here is rather unimpressive. If after all these years we've come back full circle and the much lauded "code reuse" promised by .Net is now nothing more than copying/pasting source code. then we're in trouble.

The reason is I think obvious, frantically copying snippets of code every time we encounter an "issue" leads to fragility, once we actually get a non-trivial app constructed and we test it, we have much more work to do to prove if our code is flawed or something in the hand-copied-framewrork-support-logic is somehow in error, perhaps copied wrongly for example.

May I suggest that in situations like this at the very least Microsoft formally publish a recommended workaround until the platform itself gets the proper support. At least this way we'd be sure we're all using the same workaround and reduce risk.

How are companies expected to build rock solid mission critical applications when fundamental parts of their system (in this case stuff associated with identity, encryption and authentication) are gleaned by copying/pasting snippets from Github issues!

Given Microsoft's desire to be a strong player in cloud hosting it is absolutely astonishing that something as fundamental as this is apparently overlooked.

Thanks

@Korporal

This comment has been minimized.

Copy link

commented Mar 4, 2018

@myloveCc @bartonjs @danmosemsft @Petermarcu

Actually given that these keys are often represented as PEM files, why not (in .Net Core) add a new method that accepts that format and eliminate the need to even bother with other formats?

I'm trying to use BouncyCastle but alas that brings its own wonderful set of issues...

@bartonjs

This comment has been minimized.

Copy link
Member

commented Mar 4, 2018

PKCS1/PKCS8/EncryptedPKCS8 in DER and PEM are covered by #20414

timotei added a commit to timotei/RestSharp that referenced this issue Mar 15, 2018

Handle RsaSha1 properly in .NET Core 2.0
There are some issues on .NET's side about the
missing functionality of RSACryptoServiceProvider
ToXmlString/FromXmlString:

* dotnet/corefx#23686
* dotnet/core#874

So, let's do it ourselves, and if in the future we'll
get it in the .NET itself, we can remove this code

@timotei timotei referenced this issue Mar 15, 2018

Merged

Handle RsaSha1 properly in .NET Core 2.0 #1097

2 of 5 tasks complete
@YetaWF

This comment has been minimized.

Copy link

commented Apr 20, 2018

While closed, this issue is really not fixed. To/FromXmlString don't work. At least be honest about it and remove them altogether. Or is there a platform where they work?

@bartonjs

This comment has been minimized.

Copy link
Member

commented Apr 20, 2018

@YetaWF They are defined in .NET Core because they are defined by .NET Standard.

They don't work on any .NET Core platform, because .NET Core doesn't have mscorlib.dll, the one library which contained cryptography, XML, and networking all together.

@YetaWF

This comment has been minimized.

Copy link

commented Apr 20, 2018

That's not much of an reason. There are numerous other APIs that are in mscorlib.dll and have been replaced by netstandard/netcoreapp. That's like saying .NET Core doesn't have .NET so we can't do anything. So everyone has to implement their own. I see the progress we're making.

@bartonjs

This comment has been minimized.

Copy link
Member

commented Apr 20, 2018

To/FromXmlString requires XML. XML requires networking (because of legacy DTD support). Networking requires X509Certificates (because of SslStream). X509Certificates requires the crypto algorithms classes.

There's a circular dependency there. The link in the circle that we decided to break was To/FromXmlString.

@YetaWF

This comment has been minimized.

Copy link

commented Apr 20, 2018

Dude, I read the whole thread. Yet, the solution is as simple as a cut/paste (with minor mods). Still...
Yeah I copied that in case you missed it.

So still, the problem is not fixed.

using System;
using System.Security.Cryptography;
using System.Xml;
using Newtonsoft.Json;
using NETCore.Encrypt.Shared;
using NETCore.Encrypt.Internal;

namespace NETCore.Encrypt.Extensions.Internal
{

    internal static class RSAKeyExtensions
    {
        #region JSON
        internal static void FromJsonString(this RSA rsa, string jsonString)
        {
            Check.Argument.IsNotEmpty(jsonString, nameof(jsonString));
            try
            {
                var paramsJson = JsonConvert.DeserializeObject<RSAParametersJson>(jsonString);

                RSAParameters parameters = new RSAParameters();

                parameters.Modulus = paramsJson.Modulus != null ? Convert.FromBase64String(paramsJson.Modulus) : null;
                parameters.Exponent = paramsJson.Exponent != null ? Convert.FromBase64String(paramsJson.Exponent) : null;
                parameters.P = paramsJson.P != null ? Convert.FromBase64String(paramsJson.P) : null;
                parameters.Q = paramsJson.Q != null ? Convert.FromBase64String(paramsJson.Q) : null;
                parameters.DP = paramsJson.DP != null ? Convert.FromBase64String(paramsJson.DP) : null;
                parameters.DQ = paramsJson.DQ != null ? Convert.FromBase64String(paramsJson.DQ) : null;
                parameters.InverseQ = paramsJson.InverseQ != null ? Convert.FromBase64String(paramsJson.InverseQ) : null;
                parameters.D = paramsJson.D != null ? Convert.FromBase64String(paramsJson.D) : null;
                rsa.ImportParameters(parameters);
            }
            catch
            {
                throw new Exception("Invalid JSON RSA key.");
            }
        }

        internal static string ToJsonString(this RSA rsa, bool includePrivateParameters)
        {
            RSAParameters parameters = rsa.ExportParameters(includePrivateParameters);

            var parasJson = new RSAParametersJson()
            {
                Modulus = parameters.Modulus != null ? Convert.ToBase64String(parameters.Modulus) : null,
                Exponent = parameters.Exponent != null ? Convert.ToBase64String(parameters.Exponent) : null,
                P = parameters.P != null ? Convert.ToBase64String(parameters.P) : null,
                Q = parameters.Q != null ? Convert.ToBase64String(parameters.Q) : null,
                DP = parameters.DP != null ? Convert.ToBase64String(parameters.DP) : null,
                DQ = parameters.DQ != null ? Convert.ToBase64String(parameters.DQ) : null,
                InverseQ = parameters.InverseQ != null ? Convert.ToBase64String(parameters.InverseQ) : null,
                D = parameters.D != null ? Convert.ToBase64String(parameters.D) : null
            };

            return JsonConvert.SerializeObject(parasJson);
        }
        #endregion

        #region XML

        public static void FromXmlString(this RSA rsa, string xmlString)
        {
            RSAParameters parameters = new RSAParameters();

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xmlString);

            if (xmlDoc.DocumentElement.Name.Equals("RSAKeyValue"))
            {
                foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes)
                {
                    switch (node.Name)
                    {
                        case "Modulus": parameters.Modulus = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "Exponent": parameters.Exponent = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "P": parameters.P = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "Q": parameters.Q = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "DP": parameters.DP = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "DQ": parameters.DQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "InverseQ": parameters.InverseQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "D": parameters.D = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                    }
                }
            }
            else
            {
                throw new Exception("Invalid XML RSA key.");
            }

            rsa.ImportParameters(parameters);
        }

        public static string ToXmlString(this RSA rsa, bool includePrivateParameters)
        {
            RSAParameters parameters = rsa.ExportParameters(includePrivateParameters);

            return string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent><P>{2}</P><Q>{3}</Q><DP>{4}</DP><DQ>{5}</DQ><InverseQ>{6}</InverseQ><D>{7}</D></RSAKeyValue>",
                  parameters.Modulus != null ? Convert.ToBase64String(parameters.Modulus) : null,
                  parameters.Exponent != null ? Convert.ToBase64String(parameters.Exponent) : null,
                  parameters.P != null ? Convert.ToBase64String(parameters.P) : null,
                  parameters.Q != null ? Convert.ToBase64String(parameters.Q) : null,
                  parameters.DP != null ? Convert.ToBase64String(parameters.DP) : null,
                  parameters.DQ != null ? Convert.ToBase64String(parameters.DQ) : null,
                  parameters.InverseQ != null ? Convert.ToBase64String(parameters.InverseQ) : null,
                  parameters.D != null ? Convert.ToBase64String(parameters.D) : null);
        }

        #endregion
    }
}```
@timotei

This comment has been minimized.

Copy link

commented Apr 22, 2018

@YetaWF The PR I did does/handles exactly the part you posted, so what exactly is missing?

@0x53A

This comment has been minimized.

Copy link

commented Jun 5, 2018

Is there any chance to fix this inside netcore? Even a hackish solution (reflection or a small one-off parser) would be preferably to PNSE ...

For first-party code, the workaround with the copy-pasted code is okayish, but I have the situation that a third-party dll (targeting net40) breaks because of this, making me trip on the first stone earlier than expected.

As I understand it, loading full-fx libraries from netcore is more-or-less supported, with the occasional PNSE.

It is kind of sad that such a "trivial" issue breaks this. (I know that the circular dependency isn't trivial from your perspective, but from the outside it is).

@danmosemsft danmosemsft reopened this Jun 5, 2018

@danmosemsft

This comment has been minimized.

Copy link
Member Author

commented Jun 5, 2018

Reopening because discussion is continuing. @bartonjs we've broken similar circles in the past with reflection, as you mentioned above. Do you have any concerns with doing that? And if so which dependency would use reflection?

@bartonjs

This comment has been minimized.

Copy link
Member

commented Aug 15, 2018

ToXmlString can be written with string.Format, so that part's fine. FromXmlString will need to reflect into an XML library.

@bartonjs bartonjs modified the milestones: 2.1.0, 3.0 Aug 15, 2018

Psypher9 added a commit to Psypher9/RestSharp that referenced this issue Sep 7, 2018

Get Latest from upstream Fork (#1)
* Adding usage of WebRequest.DefaultWebProxy to address restsharp#1066

* Update CONTRIBUTING.md

* Update CONTRIBUTING.md

* Using the exact name provided in DeserializeAs attribute, if any, on Deserialization

* Removed unnecessary abstraction

* Added a request configurator

* Handle RsaSha1 properly in .NET Core 2.0

There are some issues on .NET's side about the
missing functionality of RSACryptoServiceProvider
ToXmlString/FromXmlString:

* dotnet/corefx#23686
* dotnet/core#874

So, let's do it ourselves, and if in the future we'll
get it in the .NET itself, we can remove this code

* Extract FromXmlString2 into a separate file

* Move the pragma into the extension method

* Add a test for FromXmlString

* Add more tests

* Added the configurator method to the interface
Build should be Release
Added some XML docs

* Trying to fix the AppVeyor build

* Fixes restsharp#1059

* Fixes restsharp#976

* Fixes restsharp#1093

* Removed Tuple package

* Added a request configurator

* Fix Proxy in .NET Core 2.0, Add Proxy Test

* Added semicolon test

* Renamed the test

* Don't use compiled Regexes and make sure they are all static

* Fixes restsharp#1104

* fix Codacy issue

* Fixes restsharp#1126

* Adding dotnet restore

@Psypher9 Psypher9 referenced this issue Sep 18, 2018

Closed

Get Latest from upstream Fork (#1) #2

0 of 5 tasks complete
@SteveKennedy

This comment has been minimized.

Copy link

commented Jan 17, 2019

The fact that I can create a brand new .NET 2 application, then get an intellisense suggestion to use rsaKey.ToXmlString(), but it immediately throws a PlatformNotSupported exception is nuts.

ebekker added a commit to PKISharp/ACMESharpCore that referenced this issue Feb 20, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.