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

X509Store.Add Fails On Ubuntu 22.04 in FIPS Mode #111560

Open
jamesmblair opened this issue Jan 17, 2025 · 8 comments
Open

X509Store.Add Fails On Ubuntu 22.04 in FIPS Mode #111560

jamesmblair opened this issue Jan 17, 2025 · 8 comments
Assignees
Labels
area-System.Security os-linux Linux OS (any supported distro)
Milestone

Comments

@jamesmblair
Copy link

Description

Calling X509Store.Add(X509Certificate2 certificate) in .NET 8 on Ubuntu 22.04 in FIPS Mode fails with an OpenSslCryptographicException due to 3DES/SHA-1 being unsupported in FIPS mode. It appears the underlying issue is in the OpenSslDirectoryBasedStoreProvider used by X509Store on Linux systems. Instead of delegating certificate storage to an OS-level store/keychain, it appears this implementation is storing the certificate and key on the filesystem in PFX format. This export uses 3DES/SHA-1, which to the best of my knowledge is disallowed in FIPS 140-3, which Ubuntu 22.04 complies with.

Reproduction Steps

async Task ImportWithStreams(Stream certificateStream, Stream keyStream,
    CancellationToken cancellationToken = default)
{
    // Assume both Stream parameters are for PEM-formatted text.
    var certPem = await new StreamReader(certificateStream).ReadToEndAsync(cancellationToken);
    var keyPem = await new StreamReader(keyStream).ReadToEndAsync(cancellationToken);

    var privateKey = RSA.Create();
    privateKey.ImportFromPem(keyPem);

    var certificate = new X509Certificate2(Encoding.UTF8.GetBytes(certPem))
        .CopyWithPrivateKey(privateKey);

    var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
    store.Open(OpenFlags.ReadWrite);
    store.Add(certificate); // Fails
}

Expected behavior

X509Store.Add should succeed on Ubuntu 22.04 using a FIPS-supported algorithm like AES/SHA-256.

Actual behavior

Stack Trace:

Unhandled exception: Interop+Crypto+OpenSslCryptographicException: error:0308010C:digital envelope routines::unsupported
   at Interop.Crypto.CheckValidOpenSslHandle(SafeHandle handle)
   at System.Security.Cryptography.TripleDesImplementation.CreateTransform(Byte[] rgbKey, Byte[] rgbIV, Boolean encrypting)
   at System.Security.Cryptography.PasswordBasedEncryption.Encrypt(ReadOnlySpan`1 password, ReadOnlySpan`1 passwordBytes, SymmetricAlgorithm cipher, Boolean isPkcs12, AsnWriter source, PbeParameters pbeParameters, ReadOnlySpan`1 salt, Byte[] destination, Span`1 ivDest)
   at System.Security.Cryptography.KeyFormatHelper.WriteEncryptedPkcs8(ReadOnlySpan`1 password, ReadOnlySpan`1 passwordBytes, AsnWriter pkcs8Writer, PbeParameters pbeParameters)
   at System.Security.Cryptography.RSA.TryExportEncryptedPkcs8PrivateKey(ReadOnlySpan`1 password, PbeParameters pbeParameters, Span`1 destination, Int32& bytesWritten)
   at System.Security.Cryptography.AsymmetricAlgorithm.ExportArray[T](ReadOnlySpan`1 password, PbeParameters pbeParameters, TryExportPbe`1 exporter)
   at System.Security.Cryptography.X509Certificates.UnixExportProvider.BuildBags(ICertificatePalCore certPal, ReadOnlySpan`1 passwordSpan, AsnWriter tmpWriter, CertBagAsn[] certBags, AttributeAsn[] certAttrs, SafeBagAsn[] keyBags, Int32& certIdx, Int32& keyIdx)
   at System.Security.Cryptography.X509Certificates.UnixExportProvider.ExportPfx(SafePasswordHandle password)
   at System.Security.Cryptography.X509Certificates.OpenSslX509CertificateReader.Export(X509ContentType contentType, SafePasswordHandle password)
   at System.Security.Cryptography.X509Certificates.X509Certificate.Export(X509ContentType contentType, String password)
   at System.Security.Cryptography.X509Certificates.OpenSslDirectoryBasedStoreProvider.AddCertToStore(ICertificatePal certPal)
   at System.Security.Cryptography.X509Certificates.OpenSslDirectoryBasedStoreProvider.Add(ICertificatePal certPal)
   at **REDACTED CALLING CODE**

Regression?

No response

Known Workarounds

No response

Configuration

.NET 8
Ubuntu 22.04 in FIPS mode

Other information

See: OpenSslDirectoryBasedStoreProvider.AddCertToStore(ICertificatePal certPal)

When OpenSslExportProvider.ExportPkcs8 calls ExportEncryptedPkcs8PrivateKey on the selected algorithm, it passes in s_windowsPbe, hard-coded parameters that are disallowed in FIPS 140-3:
https://github.com/dotnet/runtime/blob/6c58f7992cfd628a53d9b90f258ac123cb803644/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslExportProvider.cs#L42C1-L42C2

Possibly related issues:

@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Jan 17, 2025
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.

@bartonjs
Copy link
Member

Instead of delegating certificate storage to an OS-level store/keychain

There isn't one.

X509Store.Add Fails On Ubuntu 22.04 in FIPS Mode

Yep. Until we figure out a path forward, you'll have to either not export in the PFX format (which this does under the covers) or get out of FIPS mode.

@bartonjs bartonjs added this to the 10.0.0 milestone Jan 18, 2025
@bartonjs bartonjs added os-linux Linux OS (any supported distro) and removed untriaged New issue has not been triaged by the area owner labels Jan 18, 2025
@jamesmblair
Copy link
Author

Sorry if I wasn't clear. I'm aware there isn't an OS-level keychain. I'm just highlighting how this issue is arising on Linux.

I've been able to work around this and manage my certificate a different way in the app that's running on the FIPS-mode machine, so this is not causing an ongoing production issue for me. I just wanted to kick off a discussion of the issue and some possible paths forward.

I think it's probably fine that the internal store on Linux is exporting to PFX format. The trouble is the 3DES/SHA-1 parameters being used. I know there will be compatibility considerations involved, but AES/SHA-256 would be a more secure (and FIPS-compatible) default. Maybe the algorithm could also be user-parameterizable by passing it as an option in some way to the X509Store, but that would leak Linux-specific implementation details that are irrelevant on Windows and macOS.

@vcsjones
Copy link
Member

I think it would probably be fine to just use AES+SHA256 in Linux by default only for the OpenSslDirectoryBasedStoreProvider. It's an implementation detail of how the directory store works - we don't expect people to read files from that directory, and nor do we support people writing to that directory. Reading it back will of course continue to support 3DES+SHA1 for things already persisted.

The public API Export on X509Certificate2 will need to stay on 3DES+SHA1.

Ideally #80314 would be done first, which I am pretty close barring some questions for @bartonjs about where things need to live. If we do that, then it should be a matter of changing this line:

To use the new API.

This line:

Already supports reading back in either PBES1 or PBES2 algorithms.

@okay9109
Copy link

okay9109 commented Feb 4, 2025

Hello,

Sorry to add to the discussion, but I'm having the same issue on a RedHat environment with FIPS:

 Interop+Crypto+OpenSslCryptographicException: error:0308010C:digital envelope routines::unsupported
         at Interop.Crypto.CheckValidOpenSslHandle(SafeHandle handle)
         at System.Security.Cryptography.OpenSslCipher..ctor(IntPtr algorithm, CipherMode cipherMode, Int32 blockSizeInBytes, Int32 paddingSizeInBytes, Byte[] key, Byte[] iv, Boolean encrypting)
         at System.Security.Cryptography.TripleDesImplementation.CreateTransformCore(CipherMode cipherMode, PaddingMode paddingMode, Byte[] key, Byte[] iv, Int32 blockSize, Int32 paddingSize, Int32 feedbackSize, Boolean encrypting)
         at System.Security.Cryptography.TripleDesImplementation.CreateTransform(Byte[] rgbKey, Byte[] rgbIV, Boolean encrypting)
         at System.Security.Cryptography.PasswordBasedEncryption.Encrypt(ReadOnlySpan`1 password, ReadOnlySpan`1 passwordBytes, SymmetricAlgorithm cipher, Boolean isPkcs12, AsnWriter source, PbeParameters pbeParameters, ReadOnlySpan`1 salt, Byte[] destination, Span`1 ivDest)
         at System.Security.Cryptography.X509Certificates.UnixExportProvider.EncodeCerts(AsnWriter tmpWriter, CertBagAsn[] certBags, AttributeAsn[] certAttrs, Int32 certCount, Span`1 salt, ReadOnlySpan`1 passwordSpan, Span`1 certContentsIv, String& hmacOid, String& encryptionAlgorithmOid, Boolean& isPkcs12)
         at System.Security.Cryptography.X509Certificates.UnixExportProvider.EncodeAuthSafe(AsnWriter tmpWriter, SafeBagAsn[] keyBags, Int32 keyCount, CertBagAsn[] certBags, AttributeAsn[] certAttrs, Int32 certIdx, ReadOnlySpan`1 passwordSpan)
         at System.Security.Cryptography.X509Certificates.UnixExportProvider.ExportPfx(SafePasswordHandle password)
         at System.Security.Cryptography.X509Certificates.OpenSslX509CertificateReader.Export(X509ContentType contentType, SafePasswordHandle password)
         at System.Security.Cryptography.X509Certificates.X509Certificate.Export(X509ContentType contentType, String password)
         at System.Security.Cryptography.X509Certificates.OpenSslDirectoryBasedStoreProvider.AddCertToStore(ICertificatePal certPal)
         at System.Security.Cryptography.X509Certificates.OpenSslDirectoryBasedStoreProvider.Add(ICertificatePal certPal)
(...)

My application is now running with .Net 8 but I first observed this issue with .Net 6.

I also get this issue when adding certificates to the Root store (so adding as a trusted certificate), like so:

var filePath = "path/to/cert.pfx";
var certs = new X509Certificate2Collection();
certs.Import(filePath);
foreach (var certificate in certs.Cast<X509Certificate2>())
{
    using (var store = new X509Store(storeName, StoreLocation.CurrentUser))
    {
        store.Open(OpenFlags.ReadWrite);
        store.Add(certificate);
        store.Close();
    }
}

I don't understand, however, why the store.Add method eventually attempts to encrypt a blank password. Is there any way around this?

Thank you!

@bartonjs
Copy link
Member

bartonjs commented Feb 4, 2025

I don't understand, however, why the store.Add method eventually attempts to encrypt a blank password.

Because the format we use on X509Store on Linux is a PKCS#12/PFX export.

Is there any way around this?

"Don't use X509Store" or "Don't have FIPS mode engaged" are, unfortunately, the best suggestions at the moment. That's probably tantamount to "no", but I thought I'd be more verbose.

@okay9109
Copy link

okay9109 commented Feb 4, 2025

I see, thank you for the clarification. Unfortunately, I get a similar issue when I try to export a certificate to PFX, like so:

certificate.Export(X509ContentType.Pkcs12, (string)null);

I get the following error:

Interop+Crypto+OpenSslCryptographicException: error:0308010C:digital envelope routines::unsupported
at Interop.Crypto.CheckValidOpenSslHandle(SafeHandle handle)
at System.Security.Cryptography.OpenSslCipher..ctor(IntPtr algorithm, CipherMode cipherMode, Int32 blockSizeInBytes, Int32 paddingSizeInBytes, Byte[] key, Byte[] iv, Boolean encrypting)
at System.Security.Cryptography.TripleDesImplementation.CreateTransformCore(CipherMode cipherMode, PaddingMode paddingMode, Byte[] key, Byte[] iv, Int32 blockSize, Int32 paddingSize, Int32 feedbackSize, Boolean encrypting)
at System.Security.Cryptography.TripleDesImplementation.CreateTransform(Byte[] rgbKey, Byte[] rgbIV, Boolean encrypting)
at System.Security.Cryptography.PasswordBasedEncryption.Encrypt(ReadOnlySpan`1 password, ReadOnlySpan`1 passwordBytes, SymmetricAlgorithm cipher, Boolean isPkcs12, AsnWriter source, PbeParameters pbeParameters, ReadOnlySpan`1 salt, Byte[] destination, Span`1 ivDest)
at System.Security.Cryptography.KeyFormatHelper.WriteEncryptedPkcs8(ReadOnlySpan`1 password, ReadOnlySpan`1 passwordBytes, AsnWriter pkcs8Writer, PbeParameters pbeParameters)
at System.Security.Cryptography.RSA.TryExportEncryptedPkcs8PrivateKey(ReadOnlySpan`1 password, PbeParameters pbeParameters, Span`1 destination, Int32& bytesWritten)
at System.Security.Cryptography.AsymmetricAlgorithm.ExportArray[T](ReadOnlySpan`1 password, PbeParameters pbeParameters, TryExportPbe`1 exporter)
at System.Security.Cryptography.X509Certificates.UnixExportProvider.BuildBags(ICertificatePalCore certPal, ReadOnlySpan`1 passwordSpan, AsnWriter tmpWriter, CertBagAsn[] certBags, AttributeAsn[] certAttrs, SafeBagAsn[] keyBags, Int32& certIdx, Int32& keyIdx)
at System.Security.Cryptography.X509Certificates.UnixExportProvider.ExportPfx(SafePasswordHandle password)
at System.Security.Cryptography.X509Certificates.OpenSslX509CertificateReader.Export(X509ContentType contentType, SafePasswordHandle password)
at System.Security.Cryptography.X509Certificates.X509Certificate.Export(X509ContentType contentType, String password)
(...)

Do you know if there is any ETA for this fix? Could I expect a fix for .Net 8 or would it be just in future versions?

Thank you!

Edit: just found this workaround, I'll give it a try. It would be very nice this new API proposal be implemented, I'm also curious about the ETA for this, if there's any. Thank you!

@vcsjones
Copy link
Member

I'm also curious about the ETA for this, if there's any.

Both the new API for ExportPkcs12 and having the Linux cert store use it should be addressed in .NET 10.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.Security os-linux Linux OS (any supported distro)
Projects
None yet
Development

No branches or pull requests

4 participants