Skip to content

Conversation

@djboris9
Copy link

Hi

This PR is open for discussion. We have servers that include a CAPI software container, but do not have a CNG software container.

When the key of a certificate is requested (CertKey -> keyMetadata -> softwareKeyContainers), the process fails with CNG key was empty if the CNG key is not present, even though the CAPI key is found correctly.

According to Win32 / CNG / Key Storage and Retrieval, the application API prefers CNG and then falls back to CAPI. Therefore, a CNG key is not always expected to be available:

When an application attempts to open an existing persisted key, CNG first attempts to open the native CNG file. If this file does not exist, then CNG attempts to locate a matching key in the legacy CryptoAPI key container.

I propose to not return an error if the CNG key is not found but the CAPI key is present. In this case, the CNG path would simply not be populated, similar to how the CAPI path is omitted when its key is not found in softwareKeyContainers.

I am unsure if this change would have any implications, such as breaking changes, so feedback is appreciated.

return "", "", fmt.Errorf("unable to locate CNG key: %v", err)
}
if cng == "" {
return "", "", errors.New("CNG key was empty")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add some log for this case?
Otherwise, handshake failure will be difficult to debug where tls applications expected CNG key.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if emitting logs from a package used as a library is the right way to do it (unexpected stdout/stderr, formatting differences, log vs. slog etc.).
Can you elaborate more, in what use cases an explicit CNG key is required? Maybe the exported functions could be adapted in order to prevent a breaking change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Casual search says following cases will not work with CAPI keys.

  • Modern TLS stacks (e.g., Schannel with ECC or AES-GCM) may require CNG keys, especially if the server enforces modern cipher suites.
  • Smart cards or virtual smart cards that use CNG Key Storage Providers (KSPs) will not work with CAPI-only applications.
  • Custom applications using BCrypt* or NCrypt* APIs will not be able to use CAPI keys for mTLS.

I am not expert with these solutions and cannot say for certain.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One other thought is that this could be skipped via an option in the WinCertStoreOptions. That way if something is relying on this behavior it won't break.

Also FWIW, using non-exported keys has been a pain due to this so this change would massively improve that usability.

It would be fairly easy to:

  • add new option - IgnoreNoCNG - that is set on the WinCertStore
  • make no chg key a named error
  • in keyMetadata check is err is the new named error. If yes and skip is set continue, else return the err

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the idea using WinCertStoreOptions looks good to me as it doesn't break the existing behavior and allows to selectively ignore the no-CNG key issue. I will update this PR in around 1.5 weeks (holidays), if there are no votes against it

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just added the IgnoreNoCNG option in 81cf01c and kept the original error string, both should provide backward compatibility without breaking stuff. Sorry for the late commit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants