-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Provide a way to load a CertificateRequest from a byte[] #29547
Comments
There are N things that I think are important before API is added here:
That still leaves out the fundamentally hard problems like "knowing if it's reasonable to sign the SAN entries", but that's a CA policy thing. The important part, to me, from the platform perspective is to make it such that anyone writing any form of CA be capable of executing on the main parts of policy. Effectively, if one were to walk through the CA Browser Forum's Baseline Requirements document, would they be capable of writing a conforming CA.... if not, I'd rather not have the ability to read the CSR. If the only needs from your CA solution are "I want your public key, and I'm going to make up the rest from some external context data", that's best conveyed by the client sending their key with the new (in Core 3.0) So, effectively, this request is the very end of a long chain of feature requests for a "I want to make a Certificate Authority with .NET". The goal of the API was just to be able to submit requests to a good CA, or to build certificates for testing scenarios... with the other half of the equation a giant TBD 😄. |
Thanks for your answer, that makes sense. |
You could, if it fits into your security model, have the client send a list of IP addresses, or take the encoded output of the SubjectAlternativeNameBuilder, and send that in your protocol (perhaps alongside the SubjectPublicKeyInfo). Yeah, it asymptotically approaches "just build/send and receive/read a PKCS#10", but doing things piecemeal, like not allowing the client to assert that it is, itself, a subordinate CA. |
+1 to deserialize a CertificateRequest byte array back into a CertificateRequest object for delayed signing. Agree this is conceptually at the long tail end of implementing a CA which is CA/B compliant and might look a bit like a public authority, but short of this there are there not myriad private and internal use-cases where signing a received CSR is a useful mechanic? |
Suppose I serialize it and write it to disk. Now I want to read it back in? I certainly don't want to have to dive into openssl or, worse, BouncyCastle. Or the other way around, depending on preference. |
We are currently developing a solution that needs this feature. I agree that numerous things must be added before a full-fledged CA can be developed using .NET. But when I'm able to manually create and serialize a certificate request, why shouldn't I be able to deserialize a certificate request? Now I am probably going to have to interop with OpenSSL :(. |
Sounds like there is some interest in this, so I would propose something like this, inspired from what we have an namespace System.Security.Cryptography.X509Certificates {
public partial class CertificateRequest {
+ public CertificateRequest(byte[] derContents);
+ public CertificateRequest(ReadOnlySpan<byte> derContents);
+ public static CertificateRequest CreateFromPem(ReadOnlySpan<char> csrPem);
}
} |
The API proposal isn't the hard part 😄. I'm still opposed to adding this functionality until at least numbers 1-3 in #29547 (comment) are tackled. But, getting to the point where this can be added to .NET 7 (then adding it) is my top personal goal for .NET 7. |
This comment has been minimized.
This comment has been minimized.
@vcsjones I still need to work in X509RevocationReason. Did you envision it as a nullable parameter to AddEntry (e.g. |
@bartonjs I envisioned something like this, both // Absolutely not tied to this name.
public enum CertificateRevocationListRevokeReason {
Unspecified = 0,
KeyCompromise = 1,
CACompromise = 2,
AffiliationChanged = 3,
Superseded = 4,
CessationOfOperation = 5,
CertificateHold = 6,
// No 7, per RFC 5280,
RemoveFromCRL = 8,
PrivilegeWithdrawn = 9,
AACompromise = 10,
}
// CRLBuilder AddEntry:
public void AddEntry(
byte[] serialNumber,
DateTimeOffset? revocationTime = null,
CertificateRevocationListRevokeReason? reason = null);
public void AddEntry(
ReadOnlySpan<byte> serialNumber,
DateTimeOffset? revocationTime = null,
CertificateRevocationListRevokeReason? reason = null);
public void AddEntry(
X509Certificate2 certificate,
DateTimeOffset? revocationTime = null,
CertificateRevocationListRevokeReason? reason = null);
|
namespace System.Security.Cryptography.X509Certificates;
public sealed partial class CertificateRevocationListBuilder
{
public CertificateRevocationListBuilder();
public void AddEntry(byte[] serialNumber,
DateTimeOffset? revocationTime = default,
X509RevocationReason? reason = default);
public void AddEntry(ReadOnlySpan<byte> serialNumber,
DateTimeOffset? revocationTime = default,
X509RevocationReason? reason = default);
public void AddEntry(X509Certificate2 certificate,
DateTimeOffset? revocationTime = default,
X509RevocationReason? reason = default);
public byte[] Build(X509Certificate2 issuerCertificate,
BigInteger crlNumber,
DateTimeOffset nextUpdate,
HashAlgorithmName hashAlgorithm,
RSASignaturePadding? rsaSignaturePadding = null,
DateTimeOffset? thisUpdate = null);
public byte[] Build(X500DistinguishedName issuerName,
X509SignatureGenerator generator,
BigInteger crlNumber,
DateTimeOffset nextUpdate,
HashAlgorithmName hashAlgorithm,
X509AuthorityKeyIdentifierExtension akid,
DateTimeOffset? thisUpdate = null));
public static CertificateRevocationListBuilder Load(byte[] currentCrl,
out BigInteger currentCrlNumber);
public static CertificateRevocationListBuilder Load(ReadOnlySpan<byte> currentCrl,
out BigInteger currentCrlNumber,
out int bytesConsumed);
public static CertificateRevocationListBuilder LoadPem(string currentCrl,
out BigInteger currentCrlNumber);
public static CertificateRevocationListBuilder LoadPem(ReadOnlySpan<char> currentCrl,
out BigInteger currentCrlNumber);
public bool RemoveEntry(byte[] serialNumber);
public bool RemoveEntry(ReadOnlySpan<byte> serialNumber);
public static X509Extension BuildCrlDistributionPointExtension(IEnumerable<string> uris,
bool critical = false);
}
public enum X509RevocationReason
{
Unspecified = 0,
KeyCompromise = 1,
CACompromise = 2,
AffiliationChanged = 3,
Superseded = 4,
CessationOfOperation = 5,
CertificateHold = 6,
RemoveFromCrl = 8,
PrivilegeWithdrawn = 9,
AACompromise = 10,
WeakAlgorithmOrKey = 11,
}
public partial class CertificateRequest
{
public CertificateRequest(X500DistinguishedName subjectName,
PublicKey publicKey,
HashAlgorithmName hashAlgorithm,
RSASignaturePadding? rsaSignaturePadding = default);
public Collection<AsnEncodedData> OtherRequestAttributes { get; }
public static CertificateRequest LoadSigningRequest(byte[] pkcs10,
HashAlgorithmName signerHashAlgorithm,
CertificateRequestLoadOptions options = default,
RSASignaturePadding? signerSignaturePadding = null);
public static CertificateRequest LoadSigningRequest(ReadOnlySpan<byte> pkcs10,
HashAlgorithmName signerHashAlgorithm,
out int bytesConsumed,
CertificateRequestLoadOptions options = default,
RSASignaturePadding? signerSignaturePadding = null);
public static CertificateRequest LoadSigningRequestPem(ReadOnlySpan<char> pkcs10Pem,
HashAlgorithmName signerHashAlgorithm,
CertificateRequestLoadOptions options = default,
RSASignaturePadding? signerSignaturePadding = null);
public static CertificateRequest LoadSigningRequestPem(string pkcs10Pem,
HashAlgorithmName signerHashAlgorithm,
CertificateRequestLoadOptions options = default,
RSASignaturePadding? signerSignaturePadding = null);
public string CreateSigningRequestPem();
public string CreateSigningRequestPem(X509SignatureGenerator signatureGenerator);
}
[Flags]
public enum CertificateRequestLoadOptions
{
Default = 0x0,
SkipSignatureValidation = 0x01,
UnsafeLoadCertificateExtensions = 0x02
}
public partial class X509BasicConstraintsExtension
{
public static X509BasicConstraintsExtension CreateForCertificateAuthority(int? pathLengthConstraint = null);
public static X509BasicConstraintsExtension CreateForEndEntity(bool critical = false);
} |
Rationale
I am using the
CertificateRequest.CreateSigningRequest()
to get a CSR byte array that I can send to the Certificate Authority.As far as I know, there is currently no way, on the CA side, to "deserialize" this byte array into a
CertificateRequest
.Use case
I am currently building a CA in .NET, that can sign certificate for .net client applications.
The .net client generates a PKCS 10 CSR, and send it over HTTPS to the CA, which will sign it.
Proposed API
Policies and best practices dictate that we want callers to be able to provide basic certificate revocation via CRL (Certificate Revocation Lists) prior to enabling them to load a PKCS#10 signing request, so CRL building is included in this proposal.
A CRL is, essentially, a signed list of
(byte[] SerialNumber, DateTimeOffset RevocationTime, X509Extension[] Extensions)
.Build a CRL from nothing
Load an existing CRL, modify it, save it back
Creating a CRL Distribution Points Extension for Generated Certificates
At this time we don't think it's important to expose a way to read these back, and we don't support generating them in their full complexity, so we provide a helper-builder on the closest appropriate type (without burning the best type name for if we add a rich type later).
Support PKCS#10 attributes, load a PKCS#10
Usability/Understanding Improvement in X509BasicConstraintsExtension
The text was updated successfully, but these errors were encountered: