Skip to content

Conversation

@njunot
Copy link

@njunot njunot commented Sep 5, 2025

When using JWK, Step CA sends out the Base64-encoded encrypted key. Currently that key is directly fed to jose.ParseEncrypted which expects compact JWE format and not the raw Base64 string. This fix attempts to decode the Base64 encoded key before passing it to the ParseEncoded function. If that doesn't work, it is assumed the string was already decoded so the encrypted key is passed as is.

Pain or issue this feature alleviates:

This issue resolves the error "error":"error parsing provisioner encrypted key: square/go-jose: compact JWE format must have five parts" that is encountered with step-issuer. It allows a step-issuer instance to issue certificates using the JWK from the corresponding Step CA instance.

Supporting links/other PRs/issues:

smallstep/step-issuer#107

💔Thank you!

@github-actions github-actions bot added the needs triage Waiting for discussion / prioritization by team label Sep 5, 2025
@CLAassistant
Copy link

CLAassistant commented Sep 30, 2025

CLA assistant check
All committers have signed the CLA.

@github-advanced-security
Copy link

This pull request sets up GitHub code scanning for this repository. Once the scans have completed and the checks have passed, the analysis results for this pull request branch will appear on this overview. Once you merge this pull request, the 'Security' tab will show more code scanning analysis results (for example, for the default branch). Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results. For more information about GitHub code scanning, check out the documentation.

var encryptedKey string
encryptedKey_bytes, err := base64.StdEncoding.DecodeString(encryptedKeyEnc)
if err != nil {
encryptedKey = encryptedKeyEnc

Check warning

Code scanning / CodeQL

Useless assignment to local variable Warning

This definition of encryptedKey is never used.
@maraino
Copy link
Contributor

maraino commented Sep 30, 2025

Hi @njunot, the response is not base64 encoded, it's a JWE

$ curl https://localhost:8443/provisioners/P1zJB3C7CQiUmU7zxpF-knn3PwipbKAKgGILDgDtT84/encrypted-key
{"key":"eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjYwMDAwMCwicDJzIjoiUV9nZkJfckdBZFFTNHlaVzNKUlRLQSJ9.O1V9h-AlL7I-38-8_ceEUPWH8cPNh83MrujO6Os_KhSGlUREAWD-CQ.ttb1n-CNAssaugcR.5pBdDgfuIMM0Og1v_afwhKdlj5OOJdo_wmLK0yzvk9aT78ftS1dopcL74rrncGOuG9i5TVnC4aLLibcSbO3HvG-rPZsTzvmzz9zBPTkga0fOicPPChiEUo5F2jd7wHs42UuJmxKjZdXsbLd7WgBRl6Jo6ll835NSN8_kNiqRPi79BmbwIsPn4wJ_PLybB-eeNweDh08AAuVuofXvk7dQzvWy37fsDQNpFChnzrUQlhjn5InjWsd6MpeA1hoA27DSi85Yd6JzJm7w8MMAggoNh_WlmPLscTOAhcsFnx_73PtKVRfPKyK2fZlC0dcsMbNWAVW-YKVrEAH_QTAiFwY.29BybdokVLvPTS_-ZC7J2w"}

It has 5 parts that are base64url encoded as explained in RFC7516 section-3.1

You can decrypt it like this:

$ curl -s https://localhost:8443/provisioners/P1zJB3C7CQiUmU7zxpF-knn3PwipbKAKgGILDgDtT84/encrypted-key | jq -r .key | step crypto jwe decrypt --password-file <(echo asdf)
{"use":"sig","kty":"EC","kid":"P1zJB3C7CQiUmU7zxpF-knn3PwipbKAKgGILDgDtT84","crv":"P-256","alg":"ES256","x":"G-g5Qs9rTVLCPxgIj5ywwfkSyMYbdr2-fTNgcZChRA4","y":"4oBKBibmwFik83IHfM44uT64hmWTXC68n0xlJhhPvgg","d":"RT5hyC61UDp2tcqNuZ7KmFdPY1vSfJkRQ_q0pKvDrnA"}

@hslatman
Copy link
Member

hslatman commented Nov 11, 2025

@njunot how was the JWK provisioner created / initialized? As @maraino mentioned, the encrypted key should be in JWE format, and thus base64 decoding first should not be needed. Also see the example in our docs: https://smallstep.com/docs/step-ca/provisioners/#example.

@njunot
Copy link
Author

njunot commented Nov 11, 2025

Apologies for not replying earlier. The fault comes from my side. I am quite unfamiliar with JSON Web Encryption.

For the background, I've been working on automating the configuration of a Step CA on Kubernetes using Ansible. To that end, the whole configuration had to directly present in the configuration file. I chose to use the JWK provision to circumvent ACME limitations such as HTTP communication. On Kubernetes, HTTP communication isn't possible without the installation of a CNI (for which I wanted to have an automated Certificate Authority).

First, I created the JWK using step crypto jwk create ansible/pub.json ansible/priv.json --password-file=ansible/step_ca_password. Next, I imported the pub.json and priv.json files in Ansible which stores these content in base64 encoding. Then, I added these content of the file in my ca.json file. For pub.json it was quite straight forward because it was just clear text. For priv.json, I did miss the It's a JWE compact string containing the JWK representation of the private key-part and assumed from the example that the encryptedKey was a base64 string, given that both base64 and JWE string start the same way.

In conclusion, I saved my encrypted Key as Base64 string instead of a JWE string. My StepIssuer instance expected a JWE string but received a Base64 string instead which made him complain that the encrypted key must have 5 parts and thus preventing it from completing its initialization. I implemented my fix which decoded the Base64 string and revealed the JSON encrypted key, hence priv.json, as it was returned by step crypto jwk create.

Given, that the error is entirely from my side, I'll retract this PR. But before doing that, it seems like the jose.ParseUncrypted function works either its input is a JWE or the clear text encrypted key. Nice on that part, but is this an expected behavior? If not, a warning that a JWE is expected will be welcome.

@maraino
Copy link
Contributor

maraino commented Nov 12, 2025

Given, that the error is entirely from my side, I'll retract this PR. But before doing that, it seems like the jose.ParseUncrypted function works either its input is a JWE or the clear text encrypted key. Nice on that part, but is this an expected behavior? If not, a warning that a JWE is expected will be welcome.

@njunot, what do you mean exactly? We don't have a jose.ParseUncrypted method.

I suppose you're talking about jose.ParseEncrypted. It accepts only a JWE, but in both JSON or compact format.

step crypto jwe encrypt will return the JSON format, but you can pipe it to step crypto jose format to switch to the compact format.

$ echo asdf | step crypto jwe encrypt --key my.key
{"protected":"eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTI1NkdDTSIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6IlZjdGwweDItVnVYUmpNOG4yMTJONzY2MXVYLVlFZzZuMHIzTkhIQmluVEkiLCJ5IjoiOEF1ZFhOZ3ZuTUROb2Fla2JBbk1hU2tzNFRnck1QUW53a2ExcjVCQ2stcyJ9LCJraWQiOiJSS1oyTVVrV3A5M25NVG12el9za2RrNU1nSmNkV0dLNGU0RFNncGZWYmRZIn0","iv":"RmnHzV61WCQCmU8b","ciphertext":"vw9cflU","tag":"UcDvxFmnNyDxESAhRkL-QA"}
$ echo asdf | step crypto jwe encrypt --key my.key | step crypto jose format
eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTI1NkdDTSIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6Im9GNVRuVzktcVFRVS1pRmdSc0pVS2VrZDdSaDktdmNXWjhVUXo0eEp0emsiLCJ5IjoiYmM2STAwTXlzcFk3WTNwbjdRT21vZTJXRkVEM1VnMURxVGNqV2lVbGhUUSJ9LCJraWQiOiJSS1oyTVVrV3A5M25NVG12el9za2RrNU1nSmNkV0dLNGU0RFNncGZWYmRZIn0..yyMDF5q4C3ZnCOo-.E30Vg34.djtpM4FfmDW7xEug5Rha7g

@njunot
Copy link
Author

njunot commented Nov 14, 2025

@maraino Your supposition is exactly what I meant. Since it is an expected behavior, there is nothing to add.

Thanks for your patience and keep up with the good work! These products are amazing!

@njunot njunot closed this Nov 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs triage Waiting for discussion / prioritization by team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants