Search code examples
linuxbashshelljwt

How do you create an RS256 JWT assertion with bash/shell scripting?


I'm trying to set up a shell script to grab a file from a Box account. To do so, an auth token is required. The auth token must be generated automatically through the script, so no manual steps are required. This can be done by constructing and submitting a JWT Claim.

Box's documentation specifies that they accept only “RS256″, “RS384″, and “RS512″. I have been able to setup everything I need except for creating the assertion by using Box's documentation and this website:

http://willhaley.com/blog/generate-jwt-with-bash/

I've spent hours searching through websites to see if I can find any indication on how to generate the assertion with bash/shell scripting to no avail. The website I linked is the closest thing I found, but it uses HS256, which is not supported by Box.

My script is currently virtually identical to the first script on the website I linked earlier (I have a separate script I used to test actually grabbing the file with a manually generated auth token). Please help with the commands required to generate the assertion.


Solution

  • Consider the following, which supports both HS256 and RS256:

    #!/usr/bin/env bash
    
    # Inspired by implementation by Will Haley at:
    #   http://willhaley.com/blog/generate-jwt-with-bash/
    
    set -o pipefail
    
    # Shared content to use as template
    header_template='{
        "typ": "JWT",
        "kid": "0001",
        "iss": "https://stackoverflow.com/questions/46657001/how-do-you-create-an-rs256-jwt-assertion-with-bash-shell-scripting"
    }'
    
    build_header() {
            jq -c \
                    --arg iat_str "$(date +%s)" \
                    --arg alg "${1:-HS256}" \
            '
            ($iat_str | tonumber) as $iat
            | .alg = $alg
            | .iat = $iat
            | .exp = ($iat + 1)
            ' <<<"$header_template" | tr -d '\n'
    }
    
    b64enc() { openssl enc -base64 -A | tr '+/' '-_' | tr -d '='; }
    json() { jq -c . | LC_CTYPE=C tr -d '\n'; }
    hs_sign() { openssl dgst -binary -sha"${1}" -hmac "$2"; }
    rs_sign() { openssl dgst -binary -sha"${1}" -sign <(printf '%s\n' "$2"); }
    
    sign() {
            local algo payload header sig secret=$3
            algo=${1:-RS256}; algo=${algo^^}
            header=$(build_header "$algo") || return
            payload=${2:-$test_payload}
            signed_content="$(json <<<"$header" | b64enc).$(json <<<"$payload" | b64enc)"
            case $algo in
                    HS*) sig=$(printf %s "$signed_content" | hs_sign "${algo#HS}" "$secret" | b64enc) ;;
                    RS*) sig=$(printf %s "$signed_content" | rs_sign "${algo#RS}" "$secret" | b64enc) ;;
                    *) echo "Unknown algorithm" >&2; return 1 ;;
            esac
            printf '%s.%s\n' "${signed_content}" "${sig}"
    }
    
    (( $# )) && sign "$@"
    

    ...usage as:

    rsa_secret='
    -----BEGIN RSA PRIVATE KEY-----
    MIIEpAIBAAKCAQEAtHEDjwkBpsjhit+wXZMMj2AaRHyWSKatjzLtVEGdyXrbQGgQ
    PjbfqPtqKsBPjcifHh8VAgrEtETbLN8pbE/XLRaB9P76hib6DATBn2JC6XG/NkAu
    0b2F8WB6ZuJh3fbubSOZaORRIyRvfidV5Wjb7NbEDhuSxFLaq0ad2+rQHyBgMfQS
    43OqhEa463WQt5F9NuWRTqweh5UotT6Mg9YgkvmBdA4IbJMEDWGFNecUzAGuESYq
    wzJaaQ4S58ce7HxFDywM0nFXlNx1pxZwZOZfG7bddUD8FuwbBMx5c3Z3U8LAA+J/
    50A/kxuZoa6sRTb7gXfBxy2riechlOTL+5ut3wIDAQABAoIBAD8bm5wGEV7MuR1B
    +MPxbx4iBW3YiRMlwGPp8tlaDZ5u6onPG4c21+iY7du/4NL8zLHTOxy4uW02+9To
    w+sOzXoGejM+jk4nCaL0cueUjURqNO77aaSPfW4bSRP8ry/bci4Xmkr2N25sCtZ7
    WW5fyzM9NdqdSCqDs9jdXM6ShHGt4aG1w4Q38pfl2O2KUqgGYA8j8S7oEpcuApIj
    sNH8o2PIFaFuRoUBq6WxSZBY7YdvKM0xlE0NKiDMAUIeTIRqtm8GPo7ot8dV6VHU
    EglN7gaEve75XW0DAkK2lDDpGPlVHJwLgKGiSuW0qMh6lY+dKjsZ8wyz85DqTnyo
    +42ZI6kCgYEA36X4c4a/tlh0A6i+EaA1CqmN8jh3nNMYgZvovTnIezCvO+RuJJEG
    KQQjr8/z+E8FYobImrrZsuSL+UFs1trl/nSndWh22B7fQQbJBdHwhv39YWReS0tW
    7t3LJJG3oQnR/ChlqyTToHfS0WcdtYQ0cnFWSx73Hg/S+cu0vHtcUrUCgYEAzosH
    dXq1VGRgf3TIoI9s4xJt/SnH+VHtP4dvLKzY7NN4K76DIYdQIn1xQ1Y3705v/XG+
    xTNAaoOaH6hBnRxwxcv6GmCpICJ2C21puxA63RqCslab5fc23wvMv/wwoEWPtXhf
    3OOKZxszLR1vFqZaYTWzVmTxg+r5b2aNBB0MtsMCgYBAv+6Ek/ihNE6yWIJe3AE/
    SwOboxmOP9eSfq8NSdNvRxMUxffVgl9ENLyYRB6gP1CRy+/8TCiHEIAt8/Es60c3
    OlLZPRtbSuTcELjWhIecraBUOBjMt809bt1HgyCk8RDoblGxEQJsLQTON4p0aQg+
    Me4H5bkp7O7p/z4ea6C5GQKBgQCewy+QliocHKwwTMyK3rSMNvZky2DzvI3pb2l9
    pb95C3Qr691QQHrQiCwv3m5QfLKI1o4VdzfkqBQokWUeJ2ZoJEqzS+m00ch7MDc9
    m1Qj8OTVwM1FD6oV+TQBvxCBofa9PzIw0JbqenX0D9P8TRLb9jNMDXu4Mz5Y6zMq
    HkpPkQKBgQCgrcW7U0Q+081N25VfghCPRd/o+dtqP3udXgj0nIX6y3qeCJiPeamZ
    plMNqdZScaK37wMouAIPD0u5w1OCnlepuUxU3h5y55Lzx3PnDlU1H+yfBsTi1KL/
    sDjgs31j//w80krxJNj5/i2AeYzATmybPwyM/c/PVBv/hecreUbTlQ==
    -----END RSA PRIVATE KEY-----
    '
    
    test_payload='{
        "Id": 1,
        "Name": "Hello, world!"
    }'
    sign rs256 "$test_payload" "$rsa_secret"