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

PKCS#11 Integration #44

Merged
merged 41 commits into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
f9d8b99
Add PKCS#11 changes back
13ajay Aug 1, 2023
0528144
Fix file formatting using 'go fmt'
13ajay Aug 1, 2023
3b373bb
Support separate context-specific PIN for PKCS#11 integration
13ajay Aug 3, 2023
6712ed1
Ignore semgrep finding
13ajay Aug 3, 2023
0abea75
Remove unused function and fix typo in pkcs11_signer.go
13ajay Aug 5, 2023
3819b21
First pass at closing sessions after use
13ajay Aug 10, 2023
e383625
Fix empty credential output when TA and Profile ARN regions don't match
13ajay Aug 10, 2023
aa0f6e8
Save session and handle information by default
13ajay Aug 13, 2023
9246a8f
Add happy-path testing for signer.CloseSession()
13ajay Aug 13, 2023
e500b1e
Refactor functions and comments in pkcs11_signer.go
13ajay Aug 13, 2023
b39ffd8
Refactor debug logging
13ajay Aug 13, 2023
79b8418
Disallow intermediates from being specified with certain integrations
13ajay Aug 13, 2023
70d07c5
Modify certificate chain searching with PKCS#11 integration
13ajay Aug 13, 2023
c38d846
Allow intermediates to be specified with PKCS#12
13ajay Aug 14, 2023
3cad520
Parse certificate chain (if provided) from PKCS#12 file
13ajay Aug 14, 2023
975b55a
Fetch resources upfront when creating PKCS11Signer
13ajay Aug 15, 2023
e02b1db
README updates for PKCS#11 integration
13ajay Aug 15, 2023
d7aaea0
Fix double prompting for PKCS#11 user PIN
13ajay Aug 15, 2023
5e88fcf
Ignore errors when finding certificate chain to include in CreateSess…
13ajay Aug 15, 2023
999f679
Fix comparison between key URI and cert URI when only the latter is p…
13ajay Aug 15, 2023
ebc42e3
Change shallow copy to deep copy from certUri into keyUri in pkcs11_s…
13ajay Aug 16, 2023
9242ab0
Add --pkcs-lib as a valid flag for read-certificate-data
13ajay Aug 16, 2023
ed84141
Remove explicit slot and PIN flags for PKCS#11 integration
13ajay Aug 18, 2023
2cf71f7
Add note about YubiKey attestation certificates
13ajay Sep 5, 2023
8e3af69
Fix some PKCS#11 "hybrid" mode bugs.
13ajay Sep 6, 2023
783c337
Provide URI path attribute names for CKA_ID and CKA_LABEL in README
13ajay Sep 6, 2023
bd1863b
Fix PIN prompting on Windows
13ajay Sep 6, 2023
50120ab
Fix ReadCertificateBundleData
13ajay Sep 6, 2023
81e8290
Change implementation of cert chain parsing from PKCS#12 files
13ajay Sep 7, 2023
d4c7698
Add note about zeroing out variables that store PKCS#11 PINs in README
13ajay Sep 7, 2023
69b35b3
Add force-prompt flag to force prompting when CKA_ALWAYS_AUTHENTICATE
13ajay Sep 8, 2023
029aa43
Include private key object URI in prompt message when prompting for c…
13ajay Sep 8, 2023
be61bf7
Change wording in PKCS#11 integration implementation note
13ajay Sep 8, 2023
3f52117
Change "force-prompt" option to "reuse-pin"
13ajay Sep 8, 2023
a68303e
Add more debug logging for PKCS#11 integration
13ajay Sep 8, 2023
3cb350e
Update version number for PKCS#11 integration release
13ajay Sep 8, 2023
769e625
Update README with more documentation for PKCS#11 integration
13ajay Sep 9, 2023
8698e72
Miscellaneous minor fixes
13ajay Sep 19, 2023
aa65351
Update THIRD-PARTY-LICENSES.txt
13ajay Sep 19, 2023
8608552
Add note about unattended workloads and the reuse-pin flag
13ajay Sep 19, 2023
6842eef
Merge branch 'main' into pkcs11
13ajay Sep 20, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 52 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
VERSION=1.0.5
VERSION=1.1.0

release:
go build -buildmode=pie -ldflags "-X 'github.com/aws/rolesanywhere-credential-helper/cmd.Version=${VERSION}' -linkmode=external -w -s" -trimpath -o build/bin/aws_signing_helper main.go

# Setting up SoftHSM for PKCS#11 tests.
# This portion is largely copied from https://gitlab.com/openconnect/openconnect/-/blob/v9.12/tests/Makefile.am#L363.
SHM2_UTIL=SOFTHSM2_CONF=tst/softhsm2.conf.tmp softhsm2-util
P11TOOL=SOFTHSM2_CONF=tst/softhsm2.conf.tmp p11tool

certsdir=tst/certs
curdir=$(shell pwd)

Expand All @@ -13,8 +18,40 @@ ECCERTS := $(foreach digest, sha1 sha256 sha384 sha512, $(patsubst %-key.pem, %-
RSACERTS := $(foreach digest, md5 sha1 sha256 sha384 sha512, $(patsubst %-key.pem, %-$(digest)-cert.pem, $(RSAKEYS)))
PKCS12CERTS := $(patsubst %-cert.pem, %.p12, $(RSACERTS) $(ECCERTS))

test: test-certs
go test -v ./...
# It's hard to do a file-based rule for the contents of the SoftHSM token.
# So just populate it as a side-effect of creating the softhsm2.conf file.
tst/softhsm2.conf: tst/softhsm2.conf.template $(PKCS8KEYS) $(RSACERTS) $(ECCERTS)
rm -rf tst/softhsm/*
sed 's|@top_srcdir@|${curdir}|g' $< > $@.tmp
$(SHM2_UTIL) --show-slots
$(SHM2_UTIL) --init-token --free --label credential-helper-test \
--so-pin 12345678 --pin 1234

$(SHM2_UTIL) --token credential-helper-test --pin 1234 \
--import $(certsdir)/rsa-2048-key-pkcs8.pem --label rsa-2048 --id 01
$(P11TOOL) --load-certificate $(certsdir)/rsa-2048-sha256-cert.pem \
--no-mark-private --label rsa-2048 --id 01 --set-pin 1234 --login \
--write "pkcs11:token=credential-helper-test;pin-value=1234"

$(SHM2_UTIL) --token credential-helper-test --pin 1234 \
--import $(certsdir)/ec-prime256v1-key-pkcs8.pem --label ec-prime256v1 --id 02
$(P11TOOL) --load-certificate $(certsdir)/ec-prime256v1-sha256-cert.pem \
--no-mark-private --label ec-prime256v1 --id 02 --set-pin 1234 --login \
--write "pkcs11:token=credential-helper-test;pin-value=1234"

$(P11TOOL) --load-privkey $(certsdir)/rsa-2048-key-pkcs8.pem \
--label rsa-2048-always-auth --id 03 --set-pin 1234 --login \
--write "pkcs11:token=credential-helper-test;pin-value=1234" \
--mark-always-authenticate

$(P11TOOL) --load-privkey $(certsdir)/ec-prime256v1-key-pkcs8.pem \
--label ec-prime256v1-always-auth --id 04 --set-pin 1234 --login \
--write "pkcs11:token=credential-helper-test;pin-value=1234" \
--mark-always-authenticate
mv $@.tmp $@

test: test-certs tst/softhsm2.conf
SOFTHSM2_CONF=$(curdir)/tst/softhsm2.conf go test -v ./...

%-md5-cert.pem: %-key.pem
SUBJ=$$(echo "$@" | sed -r 's|.*/([^/]+)-cert.pem|\1|'); \
Expand Down Expand Up @@ -43,6 +80,8 @@ test: test-certs
-keypbe pbeWithSHA1And3-KeyTripleDES-CBC \
-inkey $${KEY} -out "$@" -in $${CERT}

# And once again, it's hard to do a file-based rule for the contents of the certificate store.
# So just populate it as a side-effect of creating the p12 file.
%-pass.p12: %-cert.pem
echo Creating $@...
ls -l $<
Expand All @@ -66,11 +105,20 @@ $(ECKEYS):
$(certsdir)/cert-bundle.pem: $(RSACERTS) $(ECCERTS)
cat $^ > $@

test-certs: $(PKCS8KEYS) $(RSAKEYS) $(ECKEYS) $(RSACERTS) $(ECCERTS) $(PKCS12CERTS) $(certsdir)/cert-bundle.pem
$(certsdir)/cert-bundle-with-comments.pem: $(RSACERTS) $(ECCERTS)
for dep in $^; do \
cat $$dep >> $@; \
echo "Comment in bundle\n" >> $@; \
done

test-certs: $(PKCS8KEYS) $(RSAKEYS) $(ECKEYS) $(RSACERTS) $(ECCERTS) $(PKCS12CERTS) $(certsdir)/cert-bundle.pem $(certsdir)/cert-bundle-with-comments.pem tst/softhsm2.conf

test-clean:
rm -f $(RSAKEYS) $(ECKEYS)
rm -f $(PKCS8KEYS)
rm -f $(RSACERTS) $(ECCERTS)
rm -f $(PKCS12CERTS)
rm -f $(certsdir)/cert-bundle.pem
rm -f $(certsdir)/cert-with-comments.pem
rm -f tst/softhsm2.conf
rm -rf tst/softhsm/*
78 changes: 75 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ The project also comes with two bash scripts at its root, called `generate-certs

### read-certificate-data

Reads a certificate that is on disk. Either the path to the certificate on disk is provided with the `--certificate` parameter, or the `--cert-selector` flag is provided to select a certificate within an OS certificate store. Further details about the flag are provided below.
Reads a certificate. Either the path to the certificate on disk or PKCS#11 URI to identify the certificate is provided with the `--certificate` parameter, or the `--cert-selector` flag is provided to select a certificate within an OS certificate store. Further details about the `--cert-selector` flag are provided below.

If there are multiple certificates that match a given `--cert-selector`, information about each of them is printed.
If there are multiple certificates that match a given `--cert-selector` or PKCS#11 URI (as specified through the `--certificate` parameter), information about each of them is printed. For PKCS#11, URIs for each matched certificate is also printed in the hopes that it will be useful in uniquely identifying a certificate.

#### cert-selector flag

Expand Down Expand Up @@ -79,7 +79,7 @@ Signs a fixed strings: `"AWS Roles Anywhere Credential Helper Signing Test" || S

### credential-process

Vends temporary credentials by sending a `CreateSession` request to the Roles Anywhere service. The request is signed by the private key whose path can be provided with the `--private-key` parameter. Other parameters include `--certificate` (the path to the end-entity certificate), `--role-arn` (the ARN of the role to obtain temporary credentials for), `--profile-arn` (the ARN of the profile that provides a mapping for the specified role), and `--trust-anchor-arn` (the ARN of the trust anchor used to authenticate). Optional parameters that can be used are `--debug` (to provide debugging output about the request sent), `--no-verify-ssl` (to skip verification of the SSL certificate on the endpoint called), `--intermediates` (the path to intermediate certificates), `--with-proxy` (to make the binary proxy aware), `--endpoint` (the endpoint to call), `--region` (the region to scope the request to), and `--session-duration` (the duration of the vended session). Instead of passing in paths to the plaintext private key on your file system, another option (depending on your OS) could be to use the `--cert-selector` flag. More details can be found below.
Vends temporary credentials by sending a `CreateSession` request to the Roles Anywhere service. The request is signed by the private key whose path can be provided with the `--private-key` parameter. Currently, only plaintext private keys are supported. Other parameters include `--certificate` (the path to the end-entity certificate), `--role-arn` (the ARN of the role to obtain temporary credentials for), `--profile-arn` (the ARN of the profile that provides a mapping for the specified role), and `--trust-anchor-arn` (the ARN of the trust anchor used to authenticate). Optional parameters that can be used are `--debug` (to provide debugging output about the request sent), `--no-verify-ssl` (to skip verification of the SSL certificate on the endpoint called), `--intermediates` (the path to intermediate certificates), `--with-proxy` (to make the binary proxy aware), `--endpoint` (the endpoint to call), `--region` (the region to scope the request to), and `--session-duration` (the duration of the vended session). Instead of passing in paths to the plaintext private key on your file system, another option (depending on your OS) could be to use the `--cert-selector` flag. More details can be found below.

Note that if more than one certificate matches the `--cert-selector` parameter within the OS-specific secure store, the `credential-process` command will fail. To find the list of certificates that match a given `--cert-selector` parameter, you can use the same flag with the `read-certificate-data` command.

Expand Down Expand Up @@ -129,6 +129,78 @@ The above command will import the PFX file into the user's "MY" certificate stor

Also note that the above step can be done through a [Powershell cmdlet](https://learn.microsoft.com/en-us/powershell/module/pki/import-pfxcertificate?view=windowsserver2022-ps) or through [Windows CNG/Cryptography APIs](https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-pfximportcertstore).

#### PKCS#11 Integration

As you should expect from all applications which use keys and certificates, you can simply give a
[PKCS#11 URI](https://datatracker.ietf.org/doc/html/rfc7512) in place of a filename in order to
use certificates and/or keys from hardware or software PKCS#11 tokens / HSMs. A hybrid mode
using a certificate from a file but only the key in the token is also supported. Some examples:

* `--certificate 'pkcs11:manufacturer=piv_II;id=%01'`
* `--certificate 'pkcs11:object=My%20RA%20key'`
* `--certificate client-cert.pem --private-key 'pkcs11:model=SoftHSM%20v2;object=My%20RA%20key'`

Some documentation which may assist with finding the correct URI for
your key can be found [here](https://www.infradead.org/openconnect/pkcs11.html). Otherwise, you
can also potentially scope down your PKCS#11 URI by using the `read-certificate-data` diagnostic
command.

Most Linux and similar *nix systems use
[p11-kit](https://p11-glue.github.io/p11-glue/p11-kit/manual/config.html)
to provide consistent system-wide and per-user configuration of
available PKCS#11 providers. Any properly packaged provider module
will register itself with p11-kit and will be automatically visible
through the `p11-kit-proxy.{dylib, dll, so}` provider which is used by default.

If you have a poorly packaged provider module from a vendor, then
Copy link
Contributor

Choose a reason for hiding this comment

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

Define "poorly" in this context.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll do this in a follow-up PR. Merging this for now.

after you have filed a bug, you can manually create a p11-kit [module
file](https://p11-glue.github.io/p11-glue/p11-kit/manual/pkcs11-conf.html)
for it.

For systems or containers which lack p11-kit, a specific PKCS#11
provider library can be specified using the `--pkcs11-lib` parameter.

The other relevant parameter is `--reuse-pin`. This is a boolean parameter that can
be specified if the private key object you would like to use to sign data has the
`CKA_ALWAYS_AUTHENTICATE` attribute set and the `CKU_CONTEXT_SPECIFIC` PIN for the
object matches the `CKU_USER` PIN. If this parameter isn't set, you will be prompted
to provide the `CKU_CONTEXT_SPECIFIC` PIN for the object through the console. If this
parameter is set and the `CKU_USER` PIN doesn't match the `CKU_CONTEXT_SPECIFIC` PIN,
the credential helper application will fall back to prompting you. In an unattended
scenario, this flag is very helpful. There is currently no way in which to specify
the `CKU_CONTEXT_SPECIFIC` PIN without being prompted for it, so you are out of luck
for the time being when it comes to unattended workloads if the `CKU_CONTEXT_SPECIFIC`
PIN of the private key object you want to use is different from the `CKU_USER` PIN of
the token that it belongs to.

The searching methodology used to find objects within PKCS#11 tokens can largely be found
[here](https://datatracker.ietf.org/doc/html/draft-woodhouse-cert-best-practice-01). Do note
that there are some slight differences in how objects are found in the credential helper
application.

#### Other Notes

##### YubiKey Attestation Certificates

Note that if you're using a YubiKey device with PIV support, when a key pair
and certificate exist in slots 9a or 9c (PIV authentication and digital signature,
respectively), the YubiKey will automatically generate an attestation certificate
for the slot. Testing has shown that the attestation certificate can't be deleted.
In this case, if you attempt to use the `CKA_ID` (the `id` path attribute in a URI)
of your certificate to identify it in your supplied PKCS#11 URI, there will be
two certificates that match. One way in which you can disambiguate between the
two in your PKCS#11 URI can be through `CKA_LABEL` (the `object` path attribute
in a URI). Attestation certificates in either of these two slots can be
identified through the hard-coded labels, `X.509 Certificate for PIV Attestation
9a` or `X.509 Certificate for PIV Attestation 9c`.

##### Implementation Note

Due to this package's use of a dependency to integrate with PKCS#11 modules, we are unable
to guarantee that PINs are zeroized in memory after they are no longer needed. We will continue
to explore options to overcome this. Customers are encouraged to study the impact of this limitation
and determine whether compensating controls are warranted for their system and threat model.

### update

Updates temporary credentials in the [credential file](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html). Parameters for this command include those for the `credential-process` command, as well as `--profile`, which specifies the named profile for which credentials should be updated (if the profile doesn't already exist, it will be created), and `--once`, which specifies that credentials should be updated only once. Both arguments are optional. If `--profile` isn't specified, the default profile will have its credentials updated, and if `--once` isn't specified, credentials will be continuously updated. In this case, credentials will be updated through a call to `CreateSession` five minutes before the previous set of credentials are set to expire. Please note that running the `update` command multiple times, creating multiple processes, may not work as intended. There may be issues with concurrent writes to the credentials file.
Expand Down
Loading