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

APIs for exporting certificates and keys to PEM #51630

Closed
vcsjones opened this issue Apr 21, 2021 · 9 comments · Fixed by #61946
Closed

APIs for exporting certificates and keys to PEM #51630

vcsjones opened this issue Apr 21, 2021 · 9 comments · Fixed by #61946
Assignees
Labels
api-approved API was approved in API review, it can be implemented area-System.Security
Milestone

Comments

@vcsjones
Copy link
Member

vcsjones commented Apr 21, 2021

Background and Motivation

In .NET 5, additional APIs were added to make it easier to import public/private keys and certificates that are PEM formatted. Currently there is a lack of making it easier to export PEM formatted cryptographic thingies.

Today, if a developer wanted to do this, they would need to write something like:

X509Certificate2 someCertificate;
string pemContents = new string(PemEncoding.Write("CERTIFICATE", someCertificate.RawData));

While this is just a little bit of code, it is fairly obtuse because it requires the developer to know what PEM label to use. For X.509 certificates it's somewhat straight forward. For keys, it isn't obvious to some if they would be using PRIVATE KEY, RSA PRIVATE KEY, ENCRYPTED PRIVATE KEY, etc.

See #51597 for some additional context.

Proposed API

namespace System.Security.Cryptography
{
    public partial class AsymmetricAlgorithm
    {
        public string ExportPkcs8PrivateKeyPem();
        public bool TryExportPkcs8PrivateKeyPem(Span<char> buffer, out int charsWritten);

        public string ExportEncryptedPkcs8PrivateKeyPem(ReadOnlySpan<char> password, PbeParameters pbeParameters);
        public bool TryExportEncryptedPkcs8PrivateKeyPem(ReadOnlySpan<char> password, PbeParameters pbeParameters, Span<char> destination, out int charsWritten);

        public string ExportSubjectPublicKeyInfoPem();
        public bool TryExportSubjectPublicKeyInfoPem(Span<char> destination, out int charsWritten);
    }

    public partial class RSA
    {
        public string ExportRSAPrivateKeyPem();
        public string ExportRSAPublicKeyPem();

        public bool TryExportRSAPrivateKeyPem(Span<char> destination, out int charsWritten);
        public bool TryExportRSAPublicKeyPem(Span<char> destination, out int charsWritten);
    }

    public partial class ECDsa
    {
        public string ExportECPrivateKeyPem();
        public bool TryExportECPrivateKeyPem(Span<char> destination, out int charsWritten);
    }

    public partial class ECDiffieHellman
    {
        public string ExportECPrivateKeyPem();
        public bool TryExportECPrivateKeyPem(Span<char> destination, out int charsWritten);
    }

    // DSA does not have anything beyond what it gets from AsymmetricAlgorithm
}

namespace System.Security.Cryptography.X509Certificates
{
    public partial class X509Certificate2
    {
        public string ExportCertificatePem();
        public bool TryExportCertificatePem(Span<char> buffer, out int charsWritten);
    }

    public partial class X509Certificate2Collection
    {
        // Exports a PEM aggregate of all certificates
        public string ExportCertificatePems();
        public bool TryExportCertificatePems(Span<char> buffer, out int charsWritten);

        // Exports a single PKCS7 PEM of all certificates
        public string ExportPkcs7Pem();
        public bool TryExportPkcs7Pem(Span<char> buffer, out int charsWritten);
    }
}

Usage Examples

string certPem = someCert. ExportCertificatePem();
// do something with the PEM

Alternative Designs

We do nothing and better document PEMs, labels, and using PemEncoding.

Risks

Not aware of any.

@vcsjones vcsjones added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Apr 21, 2021
@dotnet-issue-labeler dotnet-issue-labeler bot added area-System.Security untriaged New issue has not been triaged by the area owner labels Apr 21, 2021
@ghost
Copy link

ghost commented Apr 21, 2021

Tagging subscribers to this area: @bartonjs, @vcsjones, @krwq, @GrabYourPitchforks
See info in area-owners.md if you want to be subscribed.

Issue Details

Background and Motivation

In .NET 5, additional APIs were added to make it easier to import public/private keys and certificates that are PEM formatted. Currently there is a lack of making it easier to export PEM formatted cryptographic thingies.

Today, if a developer wanted to do this, they would need to write something like:

X509Certificate2 someCertificate;
string pemContents = new string(PemEncoding.Write("CERTIFICATE", someCertificate.RawData));

While this is just a little bit of code, it is fairly obtuse because it requires the developer to know what PEM label to use. For X.509 certificates it's somewhat straight forward. For keys, it isn't obvious to some if they would be using PRIVATE KEY, RSA PRIVATE KEY, ENCRYPTED PRIVATE KEY, etc.

See #51597 for some additional context.

Proposed API

namespace System.Security.Cryptography
{
    public partial class AsymmetricAlgorithm
    {
        public string ExportPkcs8PrivateKeyPem();
        public bool TryExportPkcs8PrivateKeyPem(Span<char> buffer, out int charsWritten);

        public string ExportEncryptedPkcs8PrivateKeyPem(ReadOnlySpan<char> password, PbeParameters pbeParameters);
        public bool TryExportEncryptedPkcs8PrivateKeyPem(ReadOnlySpan<char> password, PbeParameters pbeParameters, Span<char> destination, out int charsWritten);

        public string ExportSubjectPublicKeyInfoPem();
        public bool TryExportSubjectPublicKeyInfoPem(Span<char> destination, out int charsWritten);
    }

    public partial class RSA
    {
        public string ExportRSAPrivateKeyPem();
        public string ExportRSAPublicKeyPem();

        public bool TryExportRSAPrivateKeyPem(Span<char> destination, out int charsWritten);
        public bool TryExportRSAPublicKeyPem(Span<char> destination, out int charsWritten);
    }

    public partial class ECDsa
    {
        public string ExportECPrivateKeyPem();
        public bool TryExportECPrivateKeyPem(Span<char> destination, out int charsWritten);
    }

    public partial class ECDiffieHellman
    {
        public string ExportECPrivateKeyPem();
        public bool TryExportECPrivateKeyPem(Span<char> destination, out int charsWritten);
    }

    // DSA does not have anything beyond what it gets from AsymmetricAlgorithm
}

namespace System.Security.Cryptography.X509Certificates
{
    public partial class X509Certificate2
    {
        public string ExportCertificatePem();
        public bool TryExportCertificatePem(Span<char> buffer, out int charsWritten);
    }

    public partial class X509Certificate2Collection
    {
        // Not thrilled with these names
        public string ExportCertificatePem();
        public bool TryExportCertificatePem(Span<char> buffer, out int charsWritten);
    }
}

Usage Examples

string certPem = someCert. ExportCertificatePem();
// do something with the PEM

Alternative Designs

We do nothing and better document PEMs, labels, and using PemEncoding.

Risks

Not aware of any.

Author: vcsjones
Assignees: -
Labels:

api-suggestion, area-System.Security, untriaged

Milestone: -

@bartonjs
Copy link
Member

For X509Certificate2Collection: ExportPkcs7Pem, perhaps? Indicating it's not a multi-PEM, but the PEM version of Export(X509ContentType.Pkcs7).

@vcsjones
Copy link
Member Author

vcsjones commented Apr 21, 2021

For X509Certificate2Collection: ExportPkcs7Pem

Hmm. ImportFromPem on X509Certificate2Collection doesn't understand the PKCS7 label and nor will it import certificates in a PKCS7 blob. Should it?

If we have export working for it, I would expect some import symmetry.

@bartonjs
Copy link
Member

Hmm. ImportFromPem on X509Certificate2Collection doesn't understand the PKCS7 label and nor will it import certificates in a PKCS7 blob. Should it?

Seems reasonable to me to do so. The Import method understands them. Or we could do ImportPkcs7Pem so we don't have the oh-so-un-fun "it works on 6, and silently skips it on 5" problem (since unknown labels are just skipped in the current method).

If we have export working for it, I would expect some import symmetry.

That makes sense, and suggests to me that you were expecting the export here to be a multi-PEM. Is that the case? If so, maybe just pluralize the method to "ExportCertificatePems"?

@vcsjones
Copy link
Member Author

you were expecting the export here to be a multi-PEM

Yeah. I don't see PKCS7 PEMs much. A multi-PEM gives symmetry with the ImportFromPem method.

Or we could do ImportPkcs7Pem so we don't have the oh-so-un-fun "it works on 6, and silently skips it on 5" problem

Hm. I would not have expressed concern over that. My general expectations for the ImportFromPem API would that it just imports everything that it can make sense of. Otherwise a developer is going to do something like this:

coll.ImportFromPem(content); // Import certs
coll.ImportFromPkcs7Pem(content); // Import again to catch the PKCS7 items

Although I don't think I'd expect a lot of folks to have CERTIFICATE and PKCS7 PEMs in the same bundle very often.

pluralize the method to "ExportCertificatePems"?

That seems reasonable.

I added PKCS7 exports to the proposal since we may or may want them, at least as a point to consider. As far as the import, my immediate preference would be to improve the API we already have, but if that proves to be too contentious because of the silent skipping behavior, then I could be convinced (for all that it matters) that dedicated import methods make sense.

@bartonjs bartonjs added this to the 7.0.0 milestone Jul 2, 2021
@bartonjs bartonjs added api-ready-for-review API is ready for review, it is NOT ready for implementation and removed api-suggestion Early API idea and discussion, it is NOT ready for implementation untriaged New issue has not been triaged by the area owner labels Jul 2, 2021
@bartonjs
Copy link
Member

bartonjs commented Jul 2, 2021

Looks like this slipped through the cracks, sorry.

If we had added the Import methods this release I'd perhaps try to rush this as scenario completion... but we added them last release, so it's scenario expansion. So I've marked it as 7.0.

@terrajobst
Copy link
Member

  • Looks good as proposed
namespace System.Security.Cryptography
{
    public partial class AsymmetricAlgorithm
    {
        public string ExportPkcs8PrivateKeyPem();
        public bool TryExportPkcs8PrivateKeyPem(Span<char> buffer, out int charsWritten);
        public string ExportEncryptedPkcs8PrivateKeyPem(ReadOnlySpan<char> password, PbeParameters pbeParameters);
        public bool TryExportEncryptedPkcs8PrivateKeyPem(ReadOnlySpan<char> password, PbeParameters pbeParameters, Span<char> destination, out int charsWritten);
        public string ExportSubjectPublicKeyInfoPem();
        public bool TryExportSubjectPublicKeyInfoPem(Span<char> destination, out int charsWritten);
    }
    public partial class RSA
    {
        public string ExportRSAPrivateKeyPem();
        public string ExportRSAPublicKeyPem();
        public bool TryExportRSAPrivateKeyPem(Span<char> destination, out int charsWritten);
        public bool TryExportRSAPublicKeyPem(Span<char> destination, out int charsWritten);
    }
    public partial class ECDsa
    {
        public string ExportECPrivateKeyPem();
        public bool TryExportECPrivateKeyPem(Span<char> destination, out int charsWritten);
    }
    public partial class ECDiffieHellman
    {
        public string ExportECPrivateKeyPem();
        public bool TryExportECPrivateKeyPem(Span<char> destination, out int charsWritten);
    }
}
namespace System.Security.Cryptography.X509Certificates
{
    public partial class X509Certificate2
    {
        public string ExportCertificatePem();
        public bool TryExportCertificatePem(Span<char> buffer, out int charsWritten);
    }
    public partial class X509Certificate2Collection
    {
        public string ExportCertificatePems();
        public bool TryExportCertificatePems(Span<char> buffer, out int charsWritten);
        public string ExportPkcs7Pem();
        public bool TryExportPkcs7Pem(Span<char> buffer, out int charsWritten);
    }
}

@terrajobst terrajobst added api-approved API was approved in API review, it can be implemented and removed api-ready-for-review API is ready for review, it is NOT ready for implementation labels Jul 13, 2021
@vcsjones vcsjones self-assigned this Jul 14, 2021
@vcsjones
Copy link
Member Author

@bartonjs I noticed in the proposal that the in the Try- X.509 certificate exporting the receiving span is called buffer, while I think destination is a more typical name. Would there be any objection if we used destination in the approved API for these?

@bartonjs
Copy link
Member

Would there be any objection if we used destination in the approved API for these?

Nope, destination is the correct name to use. Clearly we glossed over that in API Review, assuming that you and I paid attention before proposing it. 😄

@ghost ghost added in-pr There is an active PR which will close this issue when it is merged and removed in-pr There is an active PR which will close this issue when it is merged labels Nov 11, 2021
@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Nov 22, 2021
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Nov 29, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Dec 30, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-approved API was approved in API review, it can be implemented area-System.Security
Projects
None yet
3 participants