Nitrokey / nethsm-pkcs11

PKCS#11 module for NetHSM
Other
36 stars 10 forks source link

Certificate upload with `certutil` #183

Open sosthene-nitrokey opened 8 months ago

sosthene-nitrokey commented 8 months ago

Certutils uses the CKA_CERTIFICATE_TYPE  attributes is ignored, leading to certificate insertion to fail.

sosthene-nitrokey commented 8 months ago

To reproduce, from the folder of the repo:

sosthene-nitrokey commented 8 months ago

The nethsm API requires the certificate to be associated with a key. Therefore importing a certificate without the associated key will fail.

A solution can be to export the private key and the certificate to a PKCS12 file, and instead use pk12util to import both at the same time.

rcritten commented 8 months ago

I've experience something similar. keygen from NSS works but I've been unable to use certutil -A to add a self-signed certificate to the database. Even using the test in tools/upload_certificate.sh to create a cert and key the resulting certificate is not visible to NSS. I'm testing this driver to test HSM support to the freeIPA project

sosthene-nitrokey commented 8 months ago

Is the private key for your certificate also stored on the HSM ?

rcritten commented 8 months ago

Yes. I added the nethsm PKCS#11 driver to p11-kit then tweaked upload_certificate.sh to match my users and library location.

# certutil -N -d /root/nssdb --empty-password
# bash upload_certificate.sh
# echo $?
0
# certutil -L -d nssdb/ -h all

Certificate Nickname                                         Trust Attributes
                                                             SSL,S/MIME,JAR/XPI
# certutil -K -d nssdb/
certutil: Checking token "NSS Certificate DB" in slot "NSS User Private Key and Certificate Services"
certutil: no keys found
certutil: Checking token "NSS Generic Crypto Services" in slot "NSS Internal Cryptographic Services"
certutil: no keys found
certutil: Checking token "LocalHSM" in slot "NetHSM"
< 0> rsa      "certtest"                                 certtest

The first certutil command lists certificates and none are found. The second lists private keys and the certtest key is found on the HSM>

sosthene-nitrokey commented 8 months ago

Looking at what certutil does, it looks like it opens a FindAll call with a custom object class of 9, which doesn't correspond to anything in the PKCS11 standard, and I couldn't find what it tries to do.

This leads the PKCS11 module to return no values since it doesn't know of anything of such class. Changing it to return everything when the Class is unknown still doesn't lead to the certificate showing up.

rcritten commented 8 months ago

This is what it looks like from within the NSS pk11wrapper code.

nss.txt

rcritten commented 8 months ago

CKO_PROFILE has a value of 0x00000009UL and was introduced in version 3 of the spec. Is that the issue?

rjrelyea commented 8 months ago

Hmm, that's not the issue. It's perfectly valid for you to return no objects for a CKO_PROFILE lookup. NSS isn't looking for certificates at this point, it's just trying to figure out if you support certain features.

NSS will try to store both certs associated with keys and certs not unassociated with keys, but if you don't support the latter, that should not be a problem. Those are usually intermediate CA certificates that are part of the full chain. Putting those certs in the local database is fine (NSS tries to put those on the token so that when you take the token somewhere else, you will still have the full chain).

Anyway NSS is not expecting you to return certificates when it asks for the profile certificate, what other c_findobject calls are you seeing?

rjrelyea commented 8 months ago

(failure to find weird objects you don't understand should not cause any problems here. We also look for trust objects which are still NSS vendor specific (will show up in PKCS 11 v3.2 for real). Returning empty objects for these are fine as well.

rjrelyea commented 8 months ago

In the key-gen/add certificate, The key is generated in the token. A cert request is sent to the CA to create a certificate. The new certificate is imported into the token because it found the matching key in the token, so the cert should match a key in the token.

sosthene-nitrokey commented 8 months ago

NSS will try to store both certs associated with keys and certs not unassociated with keys, but if you don't support the latter, that should not be a problem. Those are usually intermediate CA certificates that are part of the full chain. Putting those certs in the local database is fine (NSS tries to put those on the token so that when you take the token somewhere else, you will still have the full chain).

But in that case the certutll import does not fall back to the nss database and instead returns:

certutil -A -d certutil-db -h LocalHSM -n test-cert -a -i test.crt -t ',,'
certutil: could not add certificate to token or database: SEC_ERROR_ADDING_CERT: Error adding certificate to database.
sosthene-nitrokey commented 8 months ago

I moved the certificate listing issue to #187

This issue now concerns only the case where the private key is not stored on the NetHSM, which is not a use case we intend to support with the NetHSM.

rcritten commented 8 months ago

I'll note that trying to store a certificate using NSS/certutil even with the private key present will fail.

sosthene-nitrokey commented 8 months ago

Yes, you need to set the enable_set_attribute_value configuration to true.

rcritten commented 8 months ago

I have that set. To reproduce I run the upload_certificate.sh test to generate a key and cert and add them to the HSM.

certutil -L verifies that the certificate is present.

I delete the certificate using: certutil -D -d nssdb -n LocalHSM:certtest

certutil -L confirms the certificate is not available. certutil -K confirms a key named certtest

Re-add the certificate:

certutil -A -d nssdb -h LocalHSM -n certtest -t ,, -a -i _cert.pem certutil: could not add certificate to token or database: SEC_ERROR_ADDING_CERT: Error adding certificate to database.

The p11nethsm.log log contains:

response 404 to PUT https://localhost:8143/api/v1/keys/aff61410bc1bbe29d05b42459f5c020c07dac75e/cert

rcritten commented 7 months ago

Re-confirmed this is not working with a build of the 1.3.0 pkcs#11 module.

rcritten commented 7 months ago

I did some more troubleshooting.

NSS is creating the object based on CKA_ID. The object creation on the NSS side looks like:

-2094893248[55ced63c5cc0]: C_CreateObject -2094893248[55ced63c5cc0]: hSession = 0x13 -2094893248[55ced63c5cc0]: pTemplate = 0x7ffe781cb5c0 -2094893248[55ced63c5cc0]: ulCount = 9 -2094893248[55ced63c5cc0]: phObject = 0x7ffe781cb520 -2094893248[55ced63c5cc0]: CKA_TOKEN = CK_TRUE [1] -2094893248[55ced63c5cc0]: CKA_CLASS = CKO_CERTIFICATE [8] -2094893248[55ced63c5cc0]: CKA_CERTIFICATE_TYPE = 0000000000000000 [8] -2094893248[55ced63c5cc0]: CKA_ID = 62F3ACB9ECA8203F19562531B9520B00AADAA4EB [20] -2094893248[55ced63c5cc0]: CKA_LABEL = "LocalHSM:certtest" [17] -2094893248[55ced63c5cc0]: CKA_VALUE = 30820315308201FDA003020102021466ED6A33B991247A08 [793] -2094893248[55ced63c5cc0]: CKA_ISSUER = CN=www.example.com [28] -2094893248[55ced63c5cc0]: CKA_SUBJECT = CN=www.example.com [28] -2094893248[55ced63c5cc0]: CKA_SERIAL_NUMBER = 021466ED6A33B991247A08BEFA3CBE2A02DC468C5B68 [22] -2094893248[55ced63c5cc0]: *phObject = 0x781cb590 -2094893248[55ced63c5cc0]: rv = CKR_DEVICE_ERROR

From the p11nethsm.log it is a 404

[2024-03-15T20:18:42Z DEBUG ureq::unit] sending request (reused connection) PUT https://localhost:8143/api/v1/keys/62f3acb9eca8203f19562531b9520b00aadaa4eb/cert[2024-03-15T20:18:42Z DEBUG ureq::unit] writing prelude: PUT /api/v1/keys/62f3acb9eca8203f19562531b9520b00aadaa4eb/cert HTTP/1.1^M Host: localhost:8143^M Accept: /^M user-agent: pkcs11-rs/1.3.0^M authorization: ***^M content-type: application/octet-stream^M Transfer-Encoding: chunked [2024-03-15T20:18:42Z DEBUG ureq::response] Body entirely buffered (length: 29) [2024-03-15T20:18:42Z DEBUG ureq::pool] adding stream to pool: https|localhost|8143 -> Stream(RustlsStream) [2024-03-15T20:18:42Z DEBUG ureq::unit] response 404 to PUT https://localhost:8143/api/v1/keys/62f3acb9eca8203f19562531b9520b00aadaa4eb/cert [2024-03-15T20:18:42Z ERROR nethsm_pkcs11::backend] Request error : https://localhost:8143/api/v1/keys/62f3acb9eca8203f19562531b9520b00aadaa4eb/cert: status code 404

The view of the token using pkcs11-tool is:

pkcs11-tool --module /usr/lib64/pkcs11/nethsm-pkcs11-v1.1.0-x86_64-fedora.39.so --list-objects Using slot 0 with a present token (0x0) Public Key Object; RSA 2048 bits label: certtest ID: 6365727474657374 Usage: none Access: none Private Key Object; RSA label: certtest ID: 6365727474657374 Usage: decrypt, sign Access: sensitive, always sensitive, never extractable Allowed mechanisms: RSA-X-509,RSA-PKCS,RSA-PKCS-OAEP,RSA-PKCS-OAEP,RSA-PKCS-OAEP,RSA-PKCS-OAEP,RSA-PKCS-OAEP,RSA-PKCS-OAEP,RSA-PKCS,RSA-PKCS-PSS,RSA-PKCS-PSS,RSA-PKCS-PSS,RSA-PKCS-PSS,RSA-PKCS-PSS,RSA-PKCS-PSS

What I can't tell is who is generating this CKA_ID for the cert add.

sosthene-nitrokey commented 7 months ago

The issue comes from the fact that the NetHSM cannot rename keys, so the enable_set_attributes feature works by maintaining an in-memory map for the name exposed to the application and that exposed to the nethsm.

This means that in some cases you will need to use the actual name used by the nethsm to get address the key.

In your case can you try to re-add the certificate with certutil -A -d nssdb -h NetSM -n NetHSM:62f3acb9eca8203f19562531b9520b00aadaa4eb -t ,, -a -i _cert.pem ?

rcritten commented 7 months ago

Thanks for the suggestion. Assuming I'm following your suggestion correctly NSS always seems to be dereferencing things and sending the CKA_ID (my guess) such that the PUT is always using that and not the contents of the nickname. It looks like the CKA label is correct in the template. I'm not sure if the pkcs#11 module could use that instead.

rjrelyea commented 2 months ago

I should point out that CKA_ID is explicitly a changable attribute. it's purpose is to associate keys and certs together. The spec is silent on how the token can do that, and if the token is doing it's own provisioning, It can set the CKA_ID to anything it wants as long as certs and other keys (like the public key) associated with the key have the same CKA_ID. Usually these tokens appear in PKCS #11 as read only, and aren't expected to work in any external provisioning system (like certutil or a certificate system).

In order to support the system, The private key needs to be tagged with something that we can use to find the key given a new certificate. NSS tags the key with the SHA1 hash of a component of the public key (for RSA it's the CKA_MODULUS, for other keys it's usually the CKA_VALUE of the public key). NSS can't supply it at keygen time because the public key is unknown until after keygen. Anyway a token that doesn't supply CKA_ID can't really be provisioned in the normal secure way (generating keys on the token and then getting an issued certificate for that key) because there is no way to match the cert up with the key without the ability to set the CKA_ID.

Some vendors assume CKA_ID is a unique ID which is invariant on the key for the life of the key. That would be CKA_UNIQUE_ID, which is something completely different.

rjrelyea commented 2 months ago

(NOTE: if you really don't want to support changable CKA_IDs, you can always take the SHA-1 hash of the public key and implicitly set your CKA_ID. When NSS asks to set the CKA_ID to that value, you can just return OK. NSS's old dbm code did this because there was no place to store the CKA_ID in the database, so it regenerated the CKA_ID on the fly and just silently accepted the SetAttribute call without an error).