如何使用BASH将JWKS公钥转换为PEM文件(AWS Cognito场景)
用纯Bash将AWS Cognito的JWKS转换为PEM公钥
我完全懂在高度受限环境下只能靠原生Bash工具的痛苦——不能用jose这类第三方库,只能啃基础命令。下面我一步步拆解如何从AWS Cognito的JWKS里提取公钥并转成PEM格式,全程只用Bash和系统默认自带的工具(比如base64、openssl、awk,这些绝大多数Linux系统都会预装)。
核心逻辑梳理
AWS Cognito的JWKS里的公钥是Base64Url编码的,我们要做的是:先把Base64Url转成标准Base64,解码成二进制,再包装成OpenSSL能识别的DER结构,最后转成PEM格式。
第一步:实现Base64Url到标准Base64的转换
Base64Url和标准Base64的差异很小,只需做三个调整:
- 把
-替换成+ - 把
_替换成/ - 补全
=符号,让字符串长度是4的倍数
用Bash函数实现这个转换:
function base64url_to_base64() { local input="$1" # 替换特殊字符 input="${input//-/}" input="${input//_//}" # 计算需要补充的=数量 local padding=$(( (4 - ${#input} % 4) % 4 )) if [ $padding -gt 0 ]; then input="${input}$(printf '%.0s=' $(seq 1 $padding))" fi echo "$input" }
第二步:从JWKS提取公钥并生成PEM
假设你已经拿到了AWS Cognito的JWKS内容(如果受限环境连curl都没有,就手动把JWKS内容复制到变量里),下面的脚本会提取指定kid的公钥,完成格式转换后输出PEM:
# 替换成你实际的Cognito JWKS内容 JWKS='{"keys":[{"kty":"RSA","e":"AQAB","use":"sig","kid":"your-cognito-key-id","alg":"RS256","n":"your-base64url-encoded-n-value"}]}' # 指定要匹配的密钥ID(kid) TARGET_KID="your-cognito-key-id" # 用awk提取对应kid的n和e值(如果环境有jq,用jq会更简单,后面会补充) N=$(echo "$JWKS" | awk -v kid="$TARGET_KID" 'BEGIN{FS="\"n\":\"|\""} /"kid":"'$TARGET_KID'"/{print $2}') E=$(echo "$JWKS" | awk -v kid="$TARGET_KID" 'BEGIN{FS="\"e\":\"|\""} /"kid":"'$TARGET_KID'"/{print $2}') # 把n和e转成标准Base64 N_B64=$(base64url_to_base64 "$N") E_B64=$(base64url_to_base64 "$E") # 解码成二进制数据 N_DER=$(echo "$N_B64" | base64 -d) E_DER=$(echo "$E_B64" | base64 -d) # 手动构造RSA公钥的DER结构(OpenSSL需要特定的ASN.1格式) function build_rsa_pubkey_der() { local n_der="$1" local e_der="$2" # 计算各部分字节长度 local n_len=${#n_der} local e_len=${#e_der} # 构造内部SEQUENCE(n, e) local inner_seq="30$(printf "%02x" $((n_len + e_len + 2)))" inner_seq+="02$(printf "%02x" $n_len)$n_der" inner_seq+="02$(printf "%02x" $e_len)$e_der" # 构造RSA算法标识的ASN.1结构 local alg_id="300d06092a864886f70d0101010500" # 构造最终的公钥SEQUENCE local pubkey_seq="30$(printf "%02x" $(( ${#alg_id}/2 + ${#inner_seq}/2 + 1 )))" pubkey_seq+="$alg_id" pubkey_seq+="03$(printf "%02x" $(( ${#inner_seq}/2 + 1 )))" pubkey_seq+="00$inner_seq" # 十六进制转二进制 echo "$pubkey_seq" | xxd -r -p } # 生成DER格式的公钥 PUBKEY_DER=$(build_rsa_pubkey_der "$N_DER" "$E_DER") # 转换为PEM格式并输出 echo "$PUBKEY_DER" | openssl rsa -pubin -inform der -outform pem
额外优化与说明
如果环境有jq,提取n/e更简单:
上面用awk是为了兼容没有jq的极端环境,如果你的系统有jq,可以替换成更简洁的命令:N=$(echo "$JWKS" | jq -r --arg kid "$TARGET_KID" '.keys[] | select(.kid == $kid) | .n') E=$(echo "$JWKS" | jq -r --arg kid "$TARGET_KID" '.keys[] | select(.kid == $kid) | .e')自动匹配JWT对应的公钥:
如果你手里已经有Cognito签发的JWT,可以先从JWT的Header里提取kid,再自动匹配JWKS里的公钥:# 替换成你的JWT内容 JWT="your-cognito-jwt-token" # 提取JWT的Header部分 JWT_HEADER=$(echo "$JWT" | cut -d. -f1) # 解码Header成JSON HEADER_JSON=$(echo "$JWT_HEADER" | base64url_to_base64 | base64 -d) # 提取kid(用awk) TARGET_KID=$(echo "$HEADER_JSON" | awk 'BEGIN{FS="\"kid\":\"|\""} {print $2}')验证生成的PEM是否有效:
可以用生成的PEM验证JWT的签名:# 提取JWT的Payload和Signature JWT_PAYLOAD=$(echo "$JWT" | cut -d. -f2) JWT_SIGNATURE=$(echo "$JWT" | cut -d. -f3) # 转换Signature为二进制 SIG_BIN=$(echo "$JWT_SIGNATURE" | base64url_to_base64 | base64 -d) # 验证签名 echo -n "${JWT_HEADER}.${JWT_PAYLOAD}" | openssl dgst -sha256 -verify pubkey.pem -signature <(echo "$SIG_BIN")
内容的提问来源于stack exchange,提问作者gph
相关产品推荐
相关产品推荐

