首页 > 解决方案 > 为什么 openssl 生成的签名看起来与 pyjwt 生成的签名不同

问题描述

我正在尝试验证使用 PyJWT 创建的 JWT 签名,但它失败了。如果签名是使用 openssl 命令创建的,它就可以工作。

我有两个脚本。

使用 PyJWT 和 PyOpenSSL 创建 JWT 的 Python 脚本。如果成功,它会在标准输出上吐出 JWT。

执行两个实验的 Bash 脚本。

实验一成功,二失败。

我对类似问题的回答很少,并试图确保

使用 openssl 命令生成的签名验证成功。使用 PyJWT 生成的签名无法通过 openssl 验证。使用 PyJWT 创建的 JWT 使用 PyJWT 成功解码。

我已经没有关于调试这个的想法了。任何帮助,方向都非常感谢。

jwttest.py(需要 PyJwt、PyOpenSSL)

import sys
import base64
import jwt

'''
# Just in case keys are not available
privkey="-----BEGIN PRIVATE KEY-----\n" \
"MIIEuwIBADANBgkqhkiG9w0BAQEFAASCBKUwggShAgEAAoIBAQC8JuSavoT2vyZi\n" \
"2RsQ0BFBySQVbK2JJPXXi1O33D3JZhUjQhyRh4yaG6ubirdM/r0eZeADdD047T8i\n" \
"wXFxH/dCZun7AF1dBcXxx1/Jr1VsNiaymXPrnRUBSrCSjqNEJIEbRiKna/JY8i6q\n" \
"cXZRTOYead8fXrTIWJRUTx5F4MNAXqdZ5v/oAsxXc1E7il55vOBDgKZSW4rV7SRC\n" \
"W4zqQA/PE9FmkrygV2x4Kbzc3aObgjgMYR99o6vyOkIhqycBPivwvyLgymDah07r\n" \
"1OR/u2z2w1zX5wD9NyLx2wqbpFQypDr0bOW4CsXuHcPEo206g//6EYlkGOW16ZZr\n" \
"Rhv9t8ipAgMBAAECgf92U/9xUmBMzepWQDPFXxV7SgRndPGuTpBN/lGoT9qLzqd8\n" \
"hRdybsz+HmjOaW7d/Vbyxx8bDP9zzcDnGsE9Y90c5ZxBPvl4hyj15W1YaexPIb80\n" \
"k01T4HZVWaOyiAIl2M9ZV8JziG/hgG3Yw4KlnrcaaXrpP6ZyWULvwtJHIBOrZsei\n" \
"CIlF/cmmwS1uHmXLGTUmZyusOeMfhcQFIh1p3r6dw9Dt6r022FvuMWrqxfsdEAP0\n" \
"kCfhuyyczPIX50gMnVN07ApqH/aMh+77fa8z6pEhqqQeQTkM6ttVzz8l9bEdaHyd\n" \
"T3IXI0G2BfuokGKLAWEKCTFfJbEODDSaSacua9UCgYEA4x+POFom84xJhqgvQdAL\n" \
"ZbDjNadRA6avJ2lDz6PhPSAzq6lleyUj7wv6p1O+37MriPKqkwMXWfZuPXaYEApv\n" \
"VQRxNCofeLjYoueWRAxIts6//1L3ueW1KmQBqGAC5oq3+vy372EvIubmzAmr5hcb\n" \
"3jmzCBjsWzTS/z3s2DpVa78CgYEA1BLjb5zrpnrMP/JJ5SaE9JzGRZ2wmB6wph83\n" \
"U3YYm9j/Qtih24I48EON7zLz/utzGZstoHEwAe8d0F1mxFslsj0A0Y6nIE1rH0Za\n" \
"MAsjkxlU0e+iaZZ9WSVvg7D3i7+dYVKpvHW7SKKYcqvJWNs7u+qkGUqiGuDoEJPr\n" \
"JM5lhZcCgYBlFXa4recIHjfbJp9huyZXbBKznnQAG/94mvEDSPzGJ77Xd90iobUM\n" \
"f1hfgHZDOYr2uIoSRB3wfC00TcP/36UNQZzgip7XK+2/EzNdtdnnAr2Q9Wwr4IBx\n" \
"DXFvbsvbr4GSw0dZ0vcXoYy24tcO4NoWXbfAstb/ANOnpffzhILgIQKBgAw43nSz\n" \
"RX19vEG/M/UJ6EW0t1SRxvitZB7e07BysO5ibiurEoD1G1T1f7uWYyuA5ExIfjOt\n" \
"8kdaQYydpWuRmTWRgHeTUhxxecf+pPn52l4C6rmCpwiQzL6Tgr7DNzENpQNT4UZk\n" \
"PpvsCv8o2VzOnb2xwy1V+Mu1xIoYDEg9wOoXAoGBAN8jWNrtxMQCgCdjwc5gOgs5\n" \
"fRdGVa2yly4dXCzVJl6DN3sEmIOuIZfMXeSNuA40AR6qT36F72WcCVE63ZzKvqtX\n" \
"WPjy/jPyQ9pp1dBKKRQCtktt7Iovuqfkc/XtGg1vv5PCefR4f2GKPCvCL3mPKOEB\n" \
"QWCwIEiwAse5z50XfN10\n" \
"-----END PRIVATE KEY-----\n"

pubkey = "-----BEGIN PUBLIC KEY-----\n" \
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvCbkmr6E9r8mYtkbENAR\n" \
"QckkFWytiST114tTt9w9yWYVI0IckYeMmhurm4q3TP69HmXgA3Q9OO0/IsFxcR/3\n" \
"Qmbp+wBdXQXF8cdfya9VbDYmsplz650VAUqwko6jRCSBG0Yip2vyWPIuqnF2UUzm\n" \
"HmnfH160yFiUVE8eReDDQF6nWeb/6ALMV3NRO4peebzgQ4CmUluK1e0kQluM6kAP\n" \
"zxPRZpK8oFdseCm83N2jm4I4DGEffaOr8jpCIasnAT4r8L8i4Mpg2odO69Tkf7ts\n" \
"9sNc1+cA/Tci8dsKm6RUMqQ69GzluArF7h3DxKNtOoP/+hGJZBjltemWa0Yb/bfI\n" \
"qQIDAQAB\n" \
"-----END PUBLIC KEY-----\n"
'''

data={}
'''
data=
{
  "iss": "http://localhost:8080/uaa/oauth/token",
  "user_name": "DBA",
  "nbf": 1592397960,
  "exp": 1621295999
}
'''

def padstr(str):
        padding = len(str) % 4
        if padding:
                data = str
                data += '=' * (4 - padding) 
                return data
        return str

if len(sys.argv) < 3 :
        print "Usage: " + sys.argv[0] + " <private key file>" + " <public key file>"
        exit(1)

with open(sys.argv[1], "r") as f:
        privkey=f.read()
#print privkey

with open(sys.argv[2], "r") as f:
        pubkey=f.read()
#print pubkey

encoded=jwt.encode(data, privkey, algorithm="RS256")
decoded=jwt.decode(encoded, pubkey, algorithms="RS256")

if( decoded != data ):
        print "decoded JWT does not match data"
        print "Data=" + str(data)
        print "Decoded=" + decoded
        exit(1)
else:
        print encoded
        exit(0)

sign_verification.sh

#!/bin/bash

function create_true_digest {
        data_file=$1
        digest_file=$2
        echo "Creating SHA256 digest for $data_file"
        ls -l $data_file
        cat $data_file
        echo
        openssl dgst -sha256 $data_file  | cut -d" " -f2 | xxd -r -p > $digest_file
        echo "digest stored in $digest_file"
        cat ${digest_file} | base64 -w 0 | sed 's/+/-/g' | sed 's/\//_/g' > ${digest_file}.base64
        echo "Base64 encoded digest stored in ${digest_file}.base64"
        ls -l ${digest_file}
        cat ${digest_file}.base64
        echo
        echo "............................................................"
}

function create_digest {
        create_true_digest $1 $2
}

function sign_data {
        data_file=$1
        pvtkey_file=$2
        signature_file=$3
        echo "Signing $data_file with $pvtkey_file"
        ls -l $data_file
        openssl dgst -sha256 -sign $pvtkey_file -out $signature_file $data_file
        #openssl dgst -sha256 -sign $pvtkey_file -binary -out $signature_file $data_file
        echo "Signature stored in $signature_file"
        cat $signature_file | base64 -w 0 | sed 's/+/-/g' | sed 's/\//_/g' > ${signature_file}.base64
        echo "Base64 encoded signature stored in ${signature_file}.base64"
        ls -l ${signature_file} ${signature_file}.base64
        echo "............................................................"
}

function verify_signature {
        signature_file=$1
        pubkey_file=$2
        data_file=$3
        echo "Verifying signature $signature_file with $pubkey_file against $data_file"
        openssl dgst -sha256 -verify $pubkey_file -signature $signature_file $data_file
        echo "............................................................"
}

function experiment_1 {
        echo "Experiment 1: This experiment accepts a message to verify"
        echo "  It uses openssl commands to generate a signature and verifies it"
        echo

        message_to_verify=$1
        #message_to_verify="{\"alg\":\"RS256\",\"typ\":\"JWT\"}.{}"
        pvtkey_file=$2
        pubkey_file=$3

        data_file="sign_data.txt"
        rm -f $data_file
        echo -n $message_to_verify > $data_file
        echo "Data stored in $data_file"

        digest_file="data_digest.bin"
        rm -f $digest_file
        rm -f ${digest_file}.base64
        create_digest $data_file $digest_file

        signature_file="signature_r.bin"
        rm -f $signature_file
        rm -f ${signature_file}.base64
        sign_data $data_file $pvtkey_file $signature_file

        verify_signature $signature_file $pubkey_file $data_file

        echo "............................................................"
}

function experiment_2 {
        echo "Experiment 2: This experiment accepts a message to verify and signature to verify against"
        echo "  It uses openssl commands to verify the signature against the message"
        message_to_verify=$1
        signature=$2 # keep it encoded
        pubkey_file=$3

        data_file="sign_data.txt"
        rm -f $data_file
        echo -n $message_to_verify > $data_file
        echo "Data stored in $data_file"
        cat $data_file
        echo

        signature_file="signature_r.bin"
        rm -f $signature_file
        rm -f ${signature_file}.base64
        echo -n $signature | sed 's/-/+/g' | sed 's/_/\//g' | base64 -di >$signature_file
        echo "Signature stored in $signature_file"
        cat $signature_file | base64 -w 0 | sed 's/+/-/g' | sed 's/\//_/g' > ${signature_file}.base64
        echo "Base64 encoded signature stored in ${signature_file}.base64"
        ls -l ${signature_file} ${signature_file}.base64

        verify_signature $signature_file $pubkey_file $data_file

        echo "............................................................"
}

if [ $# -lt 2 ]
then
        echo "Usage: $0 <private key file> <public key file>"
        exit
fi

pvtkey_file=$1
pubkey_file=$2

# Get Base64URL encoded JWT
# Need python 2.7, PyJWT, PyOpenSSL
JWT=`/usr/local/software/python/python2/bin/python jwttest.py $pvtkey_file $pubkey_file`

# Split parts
echo "Ignore base64 warnings..."
header=`echo -n $JWT | cut -d"." -f1 | sed 's/-/+/g' | sed 's/_/\//g' | base64 -di`
payload=`echo -n $JWT | cut -d"." -f2 | sed 's/-/+/g' | sed 's/_/\//g' | base64 -di`
signature=`echo -n $JWT | cut -d"." -f3` # decoding will be done by routines
message_to_verify="${header}.${payload}"

experiment_1 $message_to_verify $pvtkey_file $pubkey_file
experiment_2 $message_to_verify $signature $pubkey_file

标签: openssljwt

解决方案


解决。愚蠢的错误。我假设验证签名的消息是“header.payload”。它是'b64_urlsafe(header).b64_urlsafe(payload)'。我重读了我所指的 JWT 文章,他们确实指出了这一点。很抱歉浪费了您的带宽。


推荐阅读