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,且除第一行外,每一行開頭有一個空格,行尾無空格。
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