JSON Web Signature 規範解析

JWS 也就是 Json Web Signature,是構造 JWT 的基礎結構(JWT 其實涵蓋了 JWS 和 JWE 兩類,其中 JWT 的載荷還可以是嵌套的 JWT),包括三部分 JOSE Header、JWS Payload、JWS Signature。

這裏的 Signature 可以有兩種生成方式,一種是標準的簽名,使用非對稱加密,因爲私鑰的保密性,能夠確認簽名的主體,同時能保護完整性;另一種是消息認證碼 MAC(Message Authentication Code),使用對稱祕鑰,該祕鑰需要在簽發、驗證的多個主體間共享,因此無法確認簽發的主體,只能起到保護完整性的作用。

JWS 最終有兩種序列化的表現形式,一種是 JWS Compact Serialization,爲一串字符;另一種是 JWS JSON Serialization,是一個標準的 Json 對象,允許爲同樣的內容生成多個簽名/消息認證碼。
JWS

JWS Compact Serialization,各部分以 ‘.’ 分隔。

BASE64URL(UTF8(JWS Protected Header)) || ’.’ ||
BASE64URL(JWS Payload) || ’.’ ||
BASE64URL(JWS Signature)

JWS Json Serialization 還可以分爲兩種子格式:通用、扁平。

通用格式,最外層爲 payload、signatures。signatures 中可以包含多個 json 對象,內層的 json 對象由 protected、header、signature 組成。不同的 protected header 生成不同的 Signature。

{
    "payload": "<payload contents>",
    "signatures": 
    [
        {
            "protected": "<integrity-protected header 1 contents>",
            "header": "<non-integrity-protected header 1 contents>",
            "signature": "<signature 1 contents>"
        },
        ...
        {
            "protected": "<integrity-protected header N contents>",
            "header": "<non-integrity-protected header 1 contents>",
            "signature": "<signature N contents>"
        }
    ]
}

扁平格式,就是爲只有一個 signature/mac 準備的。

{
    "payload": "<payload contents>",
    "protected": "<integrity-protected header contents>",
    "header": "<non-integrity-protected header contents>",
    "signature": "<signature contents>"
}

JOSE Header:Json Object Signing and Encryption Header。描述加密行爲及其他用到的參數,是 JWS Protected/Unprotected Header 的合集。

JWS Protected Header,有完整性保護的頭參數。

JWS Unprotected Header,無完整性保護頭參數,僅出現在 JWS Json Serialization 格式中。

以下列出了 JOSE Header 中的規定參數,各參數詳細信息請參考 rfc7515

頭參數 全稱 解釋 必選
alg algorithm 指定簽名算法,爲 none 時,表示不使用簽名來保護完整性
jku JWK set URL 簽名所用 key 對應公匙所在的 URI
jwk json web key 簽名所用 key 對應的公匙
kid key id 簽名所用 key 的 id
typ Type 指明整個 jws 的媒體類型,JOSE 意味着爲compact,JOSE+JSON意味着爲json
cty Content Type 載荷的媒體類型
crit Critical 此字段列出的擴展頭參數必須被接收者理解並處理,否則該 jws 無效,該字段爲數組格式
x5u X.509 URL
x5c X.509 Certificate Chain
x5t X.509 Certificate SHA-1 Thumbprint
x5t#S256 X.509 Certificate SHA-256 Thumbprint
# crit 參數例子
{
    "alg":"ES256",
    "crit":["exp","iss"],
    "exp":1363284000,
    "iss":"test"
}

以下爲 rfc7515 中提供的一個案例,使用 HMAC_SHA256 生成 JWS Signature。

1. 頭部就是一個緊湊的字符串,不換行,也無空格。

Header = {“typ”:“JWT”,“alg”:“HS256”}

base64url(Header) = eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9

import base64
header_encoded = base64.urlsafe_b64encode(b'{"typ":"JWT","alg":"HS256"}')
print(header_encoded)

2. 載荷是一個包括了換行和空格的 json 對象,換行取 win 系統的 CRLF,且除第一行外,每一行開頭有一個空格,行尾無空格。

標準的JSON格式對象

Payload = {“iss”:“joe”,\r\n “exp”:1300819380,\r\n “http://example.com/is_root”:true}

base64url(Payload) = eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ

import base64
Payload = {"iss":"joe",\r\n "exp":1300819380,\r\n "http://example.com/is_root":true}
payload_encoded = (b'{"iss":"joe",\r\n "exp":1300819380,\r\n "http://example.com/is_root":true}')
print(payload_encoded)

3. 生成 Signature 時將頭部和載荷視爲一體

Message = ASCII(BASE64URL(UTF8(JWS Protected Header)) || ’.’ || BASE64URL(JWS Payload))

HMAC_SHA256 簽名時需要用到對稱密匙 Key,這裏的 Key 是預先商量好的

Key = AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow

因爲 Key 也是 base64url 編碼後的內容,所以要獲取 Key 的字節數組需要 base64url 解碼一把,解碼時由於 Key 的長度爲 86,86%4=2,需要添加 ‘==’ 後再進行解碼

# 也即解碼時用的是這個
Key = AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow==

Signature = base64url( HMAC_SHA256(Message, Key) )

最後得到簽名爲 dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk=

import hashlib
import hmac
import base64

message = bytes('eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ','ascii')

secret = base64.urlsafe_b64decode('AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow==')

signature = base64.urlsafe_b64encode(hmac.new(secret, message, digestmod=hashlib.sha256).digest())

print(signature)

去掉多餘的 ‘=’ ,就是最終的 Signature: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

這樣,最終的 JWS 就爲(換行是爲了方便看,實際就是一串)

eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9
.
eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ
.
dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

當然,實際中可能會選用 RSA 之類的算法來進行簽名,可參考 RFC 文檔中的案例。

參考文檔: rfc7515

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章