Last active
April 26, 2021 08:51
-
-
Save timmc/d2814d7da19521dda1883dd3cc046217 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
echo "DO NOT USE -- incorrect signature format, see comments on gist." | |
exit 1 | |
# Create and sign a JWT token with ES256 given the path to an ECDSA | |
# private key and a JSON payload. | |
# $0 path/to/keypair.der '{"JSON": "payload"}' | |
# Example keypair creation: | |
# openssl ecparam -name prime256v1 -genkey -noout -outform DER > example-keypair.der | |
# A few tips for generating the payload: | |
# - Pipe raw strings through `jq --raw-input .` to encode them as | |
# JSON strings. https://stedolan.github.io/jq/ | |
# - GNU date is great for generating the iat, nbf, and exp time | |
# fields: `date --date="15 minutes" +"%s"` | |
set -eu -o pipefail | |
keypair_path="$1" | |
payload="$2" | |
function base64_urlsafe { | |
# Implement own URL-safe Base64 based on standard version. Delete | |
# padding, undo wrapping, and swap out chars 62 and 63. Not all | |
# versions of `base64` have the `--wrap=0` that GNU coreutils has. | |
base64 | tr -d '\r\n=' | tr '+/' '-_' | |
} | |
header_enc="$(echo -n '{"typ":"JWT","alg":"ES256"}' | base64_urlsafe)" | |
payload_enc="$(echo -n "$payload" | base64_urlsafe)" | |
message="$header_enc.$payload_enc" | |
# If you're on a Mac, you might have a really old version of openssl | |
# that doesn't support ECDSA signing this way. | |
sig="$(echo -n "$message" | openssl dgst -sha256 -sign "$keypair_path" -keyform DER | base64_urlsafe)" | |
echo -n "$message.$sig" |
Hmm, thanks! That's unfortunate.
Luckily, I only ever used this script to generate values for a test suite, or something similar. We did have trouble with it not working on everyone's computer, so I wonder if there was a difference in output for certain openssl versions.
I'll put a giant disclaimer at the top. :-)
I posted my take on the procedure here.
I think you can fix this by making the following switch:
# Current signature construction
sig="$(echo -n "$message" | openssl dgst -sha256 -sign "$keypair_path" -keyform DER | base64_urlsafe)"
# Replacement
sig ="$(echo -n "$message" | openssl dgst -sha256 -sign "$keypair_path" -keyform DER | openssl asn1parse -inform DER | perl -n -e'/INTEGER :([0-9A-Z]*)$/ && print $1' | xxd -p -r | base64_urlsafe)"
I've just added a couple extra processing steps:
- To let openssl parse the signature
- A regex to extract the R and S integers from that output
- xxd to parse the strings as hex (that's how asn1parse outputs them) and converts them to binary before passing to your base64_urlsafe alg.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@bric3, @timmc, I think this is wrong. See this Stack Overflow for details, but effectively openssl is outputting a specific format of signature that isn't actually a valid JWS signature (not even a base64url decoded one). It's wrapping the two integers (R and S) in a DER format that can be read by the ASN1 module within openssl. This SO article goes into detail about that encoding/wrapper.