JSON Web Signature 規範解析

JWS

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

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

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

JWS Compact Serialization,各部分以 '.' 分隔。算法

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

JWS Json Serialization 還能夠分爲兩種子格式:通用、扁平。json

通用格式,最外層爲 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,有完整性保護的頭參數。url

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

如下列出了 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",rn "exp":1300819380,rn "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

相關文章
相關標籤/搜索