timjmasters/php-jws

一些用于创建和解码JWS令牌的工具

0.5 2021-11-05 04:30 UTC

This package is auto-updated.

Last update: 2024-09-05 10:43:20 UTC


README

Travis-ci build status License Code coverage

php-jws

PHP的一些JWS工具 - 由Timothy John Masters编写

这段代码更多的是作为一个学习/练习的练习,firebase/php-jwt 可能会更完整,功能更丰富。

目前仅支持无序列化JWS compact,没有实施JWS JSON序列化解决方案的意图。

安装

使用composer作为依赖项安装,然后使用composer自动加载器:composer require timjmasters/php-jws

用法

使用JWSUtil类创建和验证JWS令牌。

JWSUtil - 用于创建和验证JWS对象的实用类

  • JWSUtil::createFromPayload($payload, array $options) : JWS 使用指定的有效负载和选项创建JWS对象,包括编码部分和签名令牌
    • header:设置您需要的任何标题字段,默认为["alg" => "HS256", "typ" => "JWT"] (见下文,了解支持算法)
    • payload:有关有效负载的选项数组
      • encoding:可以是json_encodingas_string(默认:json_encoding)如果使用json_encoding,则将在设置对象之前将有效负载JSON编码,因此对象的getPayload()方法将返回JSON编码的字符串,除非提供了json_decode参数。如果使用as_string,则将在设置对象之前将有效负载转换为字符串。
      • encoding_options:传递给json_encode函数的选项,例如JSON_PRETTY_PRINT(默认:0)
    • secret:用于创建签名的密钥或密钥
  • JWSUtil::createFromEncoded(string $token, bool $json_decode) : JWS 从编码的JWS字符串创建JWS对象,如果$json_decode参数为true,则在设置之前将有效负载解码,如果无法解码有效负载,则抛出异常。签名设置为提供的签名,因此请确保在信任令牌之前验证令牌。
  • JWSUtil::verify(JWS $jws, $secret, array $allowed_algorithms) 验证JWS令牌的签名是否与其内容匹配。如果令牌签名未验证,则返回false。
    • 标题的算法不在提供的允许算法中(默认:["HS256", "RS256"])
    • 如果是HMAC
      • 使用提供的密钥将base64url编码的json编码的标题与单个句点连接,并与base64url编码的有效负载(在创建令牌时可选地进行了json编码)进行哈希,并将结果与令牌的签名进行比较
    • 如果是RSA
      • 使用openssl_verify函数验证令牌的签名是否有效,对于base64url编码的json编码的标题与单个句点连接,并与base64url编码的有效负载(在创建令牌时可选地进行了json编码)
      • 应在$secret参数中提供公钥,它可以是字符串或使用openssl函数创建的资源标识符。

JWS

该对象具有用于查看令牌中编码的数据的方法。不建议您直接使用设置器,而是使用JWSUtil类创建令牌。

  • $jws->getHeader()
    • 获取标题作为数组,通常类似于['alg' => 'RS256', 'typ' => 'JWT']
  • $jws->getPayload($json_decode = false)
    • 获取有效负载,可选的json_decode参数是一种方便,以防在创建令牌时未对有效负载进行编码(即使用'as_string'编码选项,但仍然是有效的json,您希望解码)。
    • 负载不一定是json字符串或数组,它可以是二进制数据
  • $jws->getSignature()
    • 获取未编码的签名,通常是一个哈希或二进制字符串
  • $jws->getHeaderEncoded()
    • 获取JWS头部作为base64url编码的json编码字符串
  • $jws->getPayloadEncoded()
    • 获取负载base64url编码
    • 当设置时,负载可以可选地进行编码
  • $jws->getSignatureEncoded()
    • 获取签名base64url编码
  • $jws->getEncoded()
    • 获取所有编码部分,用点连接。例如:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.WUphQgEfGvtdUCw4UntIh__bemKY6eDFjX2K2XCZP
    • 相当于$jws->getHeaderEncoded() . '.' . $jws->getPayloadEncoded() . '.' . $jws->getSignatureEncoded
  • $jws->setHeader(array $header)
    • 设置JWS头部
    • 编码值将更新,如果头部已更改,签名将不匹配
  • $jws->setPayload($payload, $json_encode = false)
    • 设置负载,可选地将其编码为json字符串
    • 编码值将更新,如果负载已更改,签名将不匹配
  • $jws->setSignature($signature)
    • 设置签名
    • 不会与头部和负载进行比较,不建议直接使用此方法

注意事项

当前支持算法

  • HS256
    • HMAC SHA 256 - JWS令牌将使用秘密选项进行签名
  • RS256
    • RSA SHA 256 - JWS令牌将假设秘密选项是私钥进行签名

示例

从数组创建JWS对象,json编码它

use TimJMasters\JWS\JWSUtil;

$jws = JWSUtil::createFromPayload(
    // The payload
    [
        "foo" => "bar"
    ],
    [
        "secret" => "foobar123",
        "payload" => [
            "encoding" => JWSUtil::PAYLOAD_AS_JSON      //"json_encode"
        ]
    ]
);

print $jws . "\r\n"; 
// Will output eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.U_rA2byM9Nw_zrXNZfAqEOuyqCO75B9iHh6yO-Fjjgg
// You can also get the header or payload as an array using the $jws->getHeader() or $jws->getPayload() methods.

使用RS256创建JWS对象

use TimJMasters\JWS\JWSUtil;

$private_key = openssl_pkey_get_private("path/to/your/private/key.pem");
$public_key = openssl_pkey_get_public("path/to/your/public/key.pem");
// Or you could do:
// $private_key = file_get_contents("path/to/your/private/key.pem");
// $public_key = file_get_contents("path/to/your/public/key.pem");

// Options for creating the token
$options = [
    "header" => [
        "alg" => JWSUtil::RSA_SHA256, // 'RS256'
        "typ" => "JWT",
    ],
    "secret" => $private_key,
];

// Create the token
$jws = JWSUtil::createFromPayload(["foo" => "bar"], $options);

print $jws; // eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.SIGNATURE_ACCORDING_TO_YOUR_CERTIFICATES

var_export(JWSUtil::verify($jws, $public_key, ["RS256"])); // true

验证来自Google的令牌

use TimJMasters\JWS\JWSUtil;

// Assuming we have a token from google we can create a JWS object
$id_token = JWSUtil::createFromEncoded($google_token);

// Make sure you follow verification according to https://developers.google.com/identity/protocols/oauth2/openid-connect#validatinganidtoken as the library only checks the signature.

// Get the key location from the jwks_uri in the Discovery document, use an HTTP library or curl to make the requests to Google.
$jwks_uri = json_decode(http_get_request('https://#/.well-known/openid-configuration'), true)['jwsk_uri']; // Currently https://www.googleapis.com/oauth2/v3/certs as of 2021/03/04

$google_keys = json_decode(http_get_request($jwks_uri), true); // Gives an array of keys

// Search the array for the correct kid according to the value in the token header
$key_info = array_search($idToken->getHeader()['kid'], array_column($google_keys, "kid"));

// You should probably check the key algorithm matches the token algorithm, but I won't show that here as using the $key_info['alg'] value as the only allowed algorithm effectively does that.

// Google currently uses RSA keys, you need to get the public key based on the modulus and exponent provided.
// I won't show how to do this here, but you might use the phpseclib library, or the firebase/php-jwt source as a way of calculating it here: https://github.com/firebase/php-jwt/blob/f42c9110abe98dd6cfe9053c49bc86acc70b2d23/src/JWK.php#L116
$public_key = createKeyFrom($key_info["n"], $key_info["e"]);

var_export(JWSUtil::verify($id_token, $public_key, [$key_info['alg']])); // Prints true or false