如何使用BASH将JSON Web密钥集(JWKS)公共密钥转换为PEM文件?

时间:2020-04-29 13:28:40

标签: bash amazon-web-services openssl jwt pem

假设您使用AWS,并且想通过Cognito使用基于ID的身份验证。

然后aws为您提供一个公共密钥,您可以使用该公共密钥来验证认知负载。

我们还假设您不希望或无法使用任何像jose这样的精美库,因为您被锁定在高度受限的环境中。

要走的路是使用BASH脚本,该脚本可以使旧的 Brian Kernighan 骄傲。 您必须先了解编码。 Base64Url需要转换为Base64。

这是通过使用填充字符=实现的 如果字符数可被4整除,则无需填充。 这涉及二进制数字表示。 处理完后,您可以将Base64转换为binary

但是如何仅使用BASH和BASH程序将JWKS/JWT转换为PEM文件呢?

2 个答案:

答案 0 :(得分:1)

这是我遵循下面列出的官方文档和资源提供的解决方案。

https://aws.amazon.com/premiumsupport/knowledge-center/decode-verify-cognito-json-token/

请修改输入网址或直接使用json令牌,并确保已安装jq

该功能以注释形式提供描述性帮助。

自2020年4月28日起在Ubuntu 18.04和AmazonLinux2(CentOS)上成功测试

#!/usr/bin/env bash
set -e

# FUNCTIONS
decodeBase64UrlUInt() { #input:base64UrlUnsignedInteger
    local binaryDigits paddedStr
    case $(( ${#1} % 4 )) in
        2) paddedStr="$1=="   ;;
        3) paddedStr="$1="    ;;
        *) paddedStr="$1"     ;;
    esac
    binaryDigits=$(             \
        echo -n "$paddedStr"    \
        | tr '_-' '/+'          \
        | openssl enc -d -a -A  \
        | xxd -b -g 0           \
        | cut -d ' ' -f 2       \
        | paste -s -d ''        \
    )
    echo "ibase=2; obase=A; $binaryDigits" | bc
    # openssl   enc:encoding; -d=decrypt; -a=-base64; -A=singleLineBuffer
    # xxd       "make-hexdump": -b=bits; -g=groupsize
    # cut       -d=delimiter; -f=field
    # paste     -s=serial|singleFile; -d=delimiter
}

base64UrlToHex() { #input:base64UrlString
    local hexStr paddedStr
    case $(( ${#1} % 4 )) in
        2) paddedStr="$1=="   ;;
        3) paddedStr="$1="    ;;
        *) paddedStr="$1"     ;;
    esac
    hexStr=$(                   \
        echo -n "$paddedStr"    \
        | tr '_-' '/+'          \
        | base64 -d             \
        | xxd -p -u             \
        | tr -d '\n'            \
    )
    echo "$hexStr"
    # base64    -d=decode
    # xxd       -p=-plain=continuousHexDump; -u=upperCase
    # tr        -d=delete
}

asn1Conf() { #input:hexStrPlainUpperCase
    local e="$1"
    local n="$2"
    echo "
        asn1 = SEQUENCE:pubkeyinfo
        [pubkeyinfo]
        algorithm = SEQUENCE:rsa_alg
        pubkey = BITWRAP,SEQUENCE:rsapubkey
        [rsa_alg]
        algorithm = OID:rsaEncryption
        parameter = NULL
        [rsapubkey]
        n = INTEGER:0x$n
        e = INTEGER:0x$e
    " | sed '/^$/d ; s/^ *//g'              \
    | openssl asn1parse                     \
        -genconf    /dev/stdin              \
        -out        /dev/stdout             \
    | openssl rsa                           \
        -pubin                              \
            -inform     DER                 \
            -outform    PEM                 \
            -in         /dev/stdin          \
            -out        /dev/
    # sed       /^$/d=removeEmptyLines; /^ */=removeLeadingSpaces
}

main() {
    local e n hexArr
    local jwksUrl="$1"
    local jwkJson=$(curl -sSSL $jwksUrl)
    local kidList=$(jq -r '.keys[].kid' <<< "$jwkJson")
    for keyId in $kidList; do
        n=$(jq -r ".keys[] | select(.kid == \"$keyId\") | .n" <<< "$jwkJson")
        e=$(jq -r ".keys[] | select(.kid == \"$keyId\") | .e" <<< "$jwkJson")
        echo -e "\n$keyId"
        # decodeBase64UrlUInt "$e"
        # decodeBase64UrlUInt "$n"
        asn1Conf $(base64UrlToHex "$e") $(base64UrlToHex "$n")
    done
}

# MAIN
main 'https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json'
exit 0

特别感谢:

Yury Oparin https://www.yuryoparin.com/2014/05/base64url-in-bash.html

塞德里克·德尔泰(CédricDeltheil) https://github.com/Moodstocks/moodstocks-api-clients/blob/master/bash/base64url.sh

唐vis https://gist.github.com/alvis/89007e96f7958f2686036d4276d28e47

答案 1 :(得分:0)

以下是一些选择:

要么忽略base64 -d抱怨输入被截断:

<<<'SGVsbG8geW91Cg' base64 -d 2>/dev/null ||:

或在解码之前使用Bash修复base64填充:

base64URL='SGVsbG8geW91Cg'

printf -v pad_space '%*s' $((${#base64URL}%4)) ''

padded_base64="$base64URL${pad_space// /=}"

<<<"$padded_base64" base64 -d