-
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
[API Proposal]: X509Certificate.ExportPkcs12 with PBE parameters #80314
Comments
Tagging subscribers to this area: @dotnet/area-system-security, @vcsjones Issue DetailsBackground and motivationRight now Windows 10 1709 supports Since this is only present on recent versions of Windows, this would throw PNSE on down level platforms. Non-Windows platforms implement this in managed code, so we can swap out API Proposalnamespace System.Security.Cryptography.X509Certificates;
public partial class X509Certificate {
public byte[] ExportPkcs12(Pkcs12ExportPbeParameters exportParameters, string? password);
}
public enum Pkcs12ExportPbeParameters {
Default = 0,
Pbes2Aes256Sha256 = 1,
} API UsageX509Certificate certificate = GetCert();
byte[] pkcs12 = certificate.ExportPkcs12(Pkcs12ExportPbeParameters.Pbes2Aes256Sha256, "potato"); Alternative DesignsI originally had a more complicate design that attempted to be a "classenum" so that we have more flexibility in the future. A future version of this class might have a public constructor that accepted After thinking about it though, I deemed this too complicated. If, in the future, we need this kind of capability, we can overload namespace System.Security.Cryptography.X509Certificates;
public partial class X509Certificate {
public byte[] ExportPkcs12(Pkcs12ExportPbeParameters exportParameters, string? password);
}
public sealed class Pkcs12ExportPbeParameters : IEquatable<Pkcs12ExportPbeParameters> {
public static Pkcs12ExportPbeParameters Default { get; }
public static Pkcs12ExportPbeParameters Pbes2Aes256Sha256 { get; }
public static bool operator ==(Pkcs12ExportPbeParameters? left, Pkcs12ExportPbeParameters? right);
public static bool operator !=(Pkcs12ExportPbeParameters? left, Pkcs12ExportPbeParameters? right);
// Override Equals, GetHashCode, etc.
} RisksIt's a Window 10 1709+ API. This will throw PNSE on down level Windows if
|
Amusingly, I was just answering an SO question yesterday where someone had an AES-256/SHA-256 PFX that they couldn't load (because it was being loaded by Mono, which doesn't support the newer revision with PBES2). The version with the enum is probably a good balance of allowing more modern algorithm choices without impeding a common operating too much... but I wonder if we should just go ahead and overload it with PbeParameters at the same time to allow the much richer control. We should add TripleDes192Sha1 to the enum now, though, so that we can have "Default means we pick, but you have the option of specifying 'legacy' if we ever change the defaults".
We could probably avoid the PNSE and just have both the OS-exporter and our custom-exporter available on Windows. |
I thought about that but.. I got a little concerned about all of the Win32-isms and Bag Attributes that get applied. It might be surprising to folks that using AES means "you don't get a keyAttributes on your PKCS12 bag anymore". But maybe that is acceptable for Windows 8 / Server 2012.
That makes sense if we are okay with using a managed export on Windows. Win32 doesn't let us explicitly pick
My concern is largely the same as the first one, where if on Windows you use the managed one, you might not get all of the bag attributes that Windows likes to shove in there. If we're okay losing bag attributes on Windows... then... I guess we can do |
Worst case: we could always let Windows export it, then we decompose and recompose it with the better algorithms... |
I am annoyed that did not occur to me. This sounds reasonable to me, and should make it (relatively) straight forward to make this work with any set of I'll adjust the proposal. It also occurs to me that we probably want symmetry with |
Looks good as proposed. We discussed replacing the enum with prepopulated accelerators on PbeParameters, but the enum values don't specifically encode the iteration count (where we're following OS defaults). namespace System.Security.Cryptography.X509Certificates;
public partial class X509Certificate {
public byte[] ExportPkcs12(Pkcs12ExportPbeParameters exportParameters, string? password);
public byte[] ExportPkcs12(PbeParameters exportParameters, string? password);
}
public partial class X509Certificate2Collection {
public byte[] ExportPkcs12(Pkcs12ExportPbeParameters exportParameters, string? password);
public byte[] ExportPkcs12(PbeParameters exportParameters, string? password);
}
public enum Pkcs12ExportPbeParameters {
// Initially will behave as `Pbes2TripleDesSha1`, but reserved to change behavior in the future.
Default = 0,
// TripleDes3KeyPkcs12, SHA1, 2000 PBKDF2 rounds. Matches Win32.
Pbes2TripleDesSha1 = 1,
// AES-256, SHA256, 2000 PBKDF2 rounds. Matches Win32.
Pbes2Aes256Sha256 = 2,
} |
@bartonjs I have a prototype of this, but the question that arose from this is "Should we inbox The strategy that ended up working the best is to do an export from the system and re-write it with the specified PBE parameters. This is more or less required for Windows because we need all of the attributes from the cert store. On other platforms we can just build the PKCS12 object from the respective pieces. The reason for inboxing is it ends up being a lot of shared source between S.S.Cryptography and S.S.C.Pkcs. Anyone that has a package reference to S.S.C.Pkcs is going to be carrying around two copies of the PKCS12 reader / builder since they would be types with different identities. |
Inboxing the type would effectively stop it from evolving in the NuGet package... but I don't think either of those types (or their closure) has ever changed; so it's not like anyone would notice. We'd still need to build them for downlevel platforms, so they'd have to move into shared source. At that point it's sort of "6 of one, half-dozen of the other" if they're shared as internal vs public or if it's inboxed (engineeringwise). If you want to hedge, share it with a conditional access modifier. If you believe that there's benefit to fully moving the public types, and can articulate a reason, then I'll deal with the paperwork. |
@bartonjs the main scenario I had in mind was trimmability. If you have a .NET 10 app that does: X509Certificate2 cert = GetCert();
cert.Export(X509ContentType.Pkcs12); and Pkcs12Builder builder = new(); Because they are shared source, they have different type identities. Your trimmed application will carry two copies of Pkcs12Builder, one internal used by |
Nope, that would not solve the circular dependency problem. We would need to type forward, if we inbox. |
Background and motivation
Right now
Export(X509ContentType.Pkcs12, ...)
assumes 3DES, SHA1, and 2000 rounds of PBKDF. These are not great in 2023 and there is no control over it today. Developers could usePkcs12Builder
themselves, but, that's a fairly low-level mechanism that requires understanding how PKCS12 works.I propose we expose new APIs that allow stronger PKCS12 exports.
In the proposal, there are two overloads. An enum that provides simple options that match Windows behavior. The second is
PbeParameters
where people can have total control over the encryption, hash, and number ob PBES2 rounds.Win32 by default does not give us the flexibility that is needed to implement
PbeParameters
, as it does not let us choose the number of PBES2 rounds. In that case, the export will be performed using an existing Win32 API, decoded, and re-encoded with the managed implementation using the specified parameters.API Proposal
API Usage
The text was updated successfully, but these errors were encountered: