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

Exporting Certificate with a password always chooses SHA1-3DES encryption #108330

Closed
FlamingDrake opened this issue Sep 27, 2024 · 4 comments
Closed

Comments

@FlamingDrake
Copy link

Description

When exporting a X509Certificate2 with a password, the method will always encrypt the key with the same encryption. SHA1-3DES.

This encryption is slightly weak, and while it's probably good enough in this use case I cannot find a way to export it with any other encryption. There is a way to export only the key with variable encryption, but not the certificate.

This is also present when importing certificates (pfx) where the key is encrypted with AES, after exporting it again it's back to SHA1-3DES.

Reproduction Steps

var key = new RSA(2048);
var req = new CertificateRequest(
  "test",
  key,
  HashAlgorithmName.SHA256,
  RSASignaturePadding.Pkcs1 );
var cert = req.CreateSelfSigned(DateTime.Now, DateTime.Now.AddDays(1));
var certBytes = cert.Export(X509ContentType.Pfx, "1234");

using var fileStreamKey = new FileStream("./testCert.pfx", FileMode.Create, FileAccess.Write);
fileStreamKey.Write(certBytes);
openssl pkcs12 -in testCert.pfx -info

// Output
// MAC: sha1, Iteration 2000
// MAC length: 20, salt length: 20
//PKCS7 Data
// Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 2000

The keybag will always have that encryption.

Expected behavior

The API does not allow input on encryption used. But, the expected behavior is that it would use some sort of "best effort" where better encryption is available should be used.

Actual behavior

Always selects SHA1-3DES encryption.

Regression?

No response

Known Workarounds

There is a way to encrypt the key alone with a different encryption and use other tools to merge the certificate (without private key) file with the private key file.

var key = RSA.Create(2048);
var keyBytes = key.ExportEncryptedPkcs8PrivateKey("1234", new PbeParameters(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 2048));

Configuration

This is present in .NET Framework and .NET 8 (Tested in both configurations)

Tested on Windows 10, 11, Server 2016.

Other information

No response

@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Sep 27, 2024
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-security, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

@vcsjones
Copy link
Member

vcsjones commented Sep 27, 2024

There is an API that is approved - but not implemented - to allow selecting PBE parameters during export over at #80314.

In the mean time, you can use Pkcs12Builder as a work around.

Something like this (I only lightly tested this)

using System.Security.Cryptography;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;

static byte[] ExportPkcs12(X509Certificate2 certificate, string password)
{
    Pkcs9LocalKeyId keyId = new([1]);
    PbeParameters pbe = new(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 100_000);
    using AsymmetricAlgorithm? key = (AsymmetricAlgorithm?)certificate.GetRSAPrivateKey() ?? certificate.GetECDsaPrivateKey();

    if (key is null)
        throw new CryptographicException("No private key");

    Pkcs12Builder builder = new();
    Pkcs12SafeContents certContainer = new();
    Pkcs12SafeContents keyContainer = new();
    Pkcs12SafeBag certBag = certContainer.AddCertificate(certificate);
    Pkcs12SafeBag keyBag = keyContainer.AddShroudedKey(key, password, pbe);
    certBag.Attributes.Add(keyId);
    keyBag.Attributes.Add(keyId);

    // Change to AddSafeContentsUnencrypted if you do not want the certificate encrypted in PKCS12
    builder.AddSafeContentsEncrypted(certContainer, password, pbe);

    //key safe contents do not need to be encrypted because the key is shrouded.
    builder.AddSafeContentsUnencrypted(keyContainer);

    builder.SealWithMac(password, pbe.HashAlgorithm, pbe.IterationCount);
    return builder.Encode();
}

@vcsjones
Copy link
Member

Keep in mind that some platforms, such as macOS and older Windows are not capable of working with "modern" PBE encryption at the operating system level. For example, Windows Server 2012 R2 may not be able to import an AES encrypted PFX/PCKS12.

@jeffhandley
Copy link
Member

Closing since this is working as expected and @vcsjones referenced the approved API in #80314.

@dotnet-policy-service dotnet-policy-service bot removed the untriaged New issue has not been triaged by the area owner label Sep 30, 2024
@github-actions github-actions bot locked and limited conversation to collaborators Oct 30, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants