You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

额外优化与说明

  1. 如果环境有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')
    
  2. 自动匹配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}')
    
  3. 验证生成的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 13:47:41