简体   繁体   English

PHP JWT(JSON Web 令牌),带 RSA 签名,不带库

[英]PHP JWT (JSON Web Token) with RSA signature without library

Anyboby know some solution in PHP to using JWT (JSON Web Token) with RSA signature without library?任何人都知道 PHP 中的一些解决方案,以使用 JWT(JSON Web 令牌)和没有库的 RSA 签名? I tried so hard to find solutions without library or without using composer.我非常努力地寻找没有库或不使用作曲家的解决方案。 All the options that I found use some kind of library.我发现的所有选项都使用某种库。

I made my own solution using other examples on the way.我在途中使用其他示例制作了自己的解决方案。 Below we have 3 use cases and the specific PHP codes to anyone that want to use:下面我们有 3 个用例和特定的 PHP 代码供任何想要使用的人使用:

  1. Create RSA private and public keys for sign JWT using openssl library.使用 openssl 库为签名 JWT 创建 RSA 私钥和公钥。
  2. Create JWT token and sign using RSA private keys.创建 JWT 令牌并使用 RSA 私钥进行签名。
  3. Validate JWT's signed token using RSA public keys.使用 RSA 公钥验证 JWT 的签名令牌。
  4. Example of inverted encryptation.反向加密的示例。 Use public key encryptation => private key decryptation.使用公钥加密 => 私钥解密。

1 - Create RSA private and public keys for sign JWT using openssl library: 1 - 使用 openssl 库为签名 JWT 创建 RSA 私钥和公钥:

// ====================== OPENSSL KEY CREATE METHOD ==================

// URL of the source: https://8gwifi.org/docs/php-asym.jsp
// I tried all 3 types of keys, but only RSA type works.

$password = "password"; // Change it for what value you want

$config = array(  
    "digest_alg" => "sha512",  
    "private_key_bits" => 2048,  
    "private_key_type" => OPENSSL_KEYTYPE_RSA,
);  
 
// Create the keypair  
$res = openssl_pkey_new($config);  
// Get private key  
openssl_pkey_export($res, $privkey);  
// Get public key  
$pubkey = openssl_pkey_get_details($res);  
$pubkey = $pubkey["key"];  
  
echo "====PKCS1 RSA Key in Non Encrypted Format ====\n";  
var_dump($privkey);  
echo "====PKCS1 RSA Key in Encrypted Format====\n ";  
  
// Get private key in Encrypted Format  
openssl_pkey_export($res, $privkey,$password);  
// Get public key  
$pubkey = openssl_pkey_get_details($res);  
$pubkey = $pubkey["key"];  
var_dump($privkey);  
echo "RSA Public Key \n ";  
var_dump($pubkey); 

2 - Create JWT token and sign using RSA private keys and 3 - Validate JWT's signed token using RSA public keys: 2 - 创建 JWT 令牌并使用 RSA 私钥签名和 3 - 使用 RSA 公钥验证 JWT 的签名令牌:


// ====================== JWT WITH ENCRYPT AND DECRYPT ==================

// ===== Variables definition

$keyPrivatePassword = 'password';
$keyPrivatePath = "private.key";
$keyPublicPath = "public.key";
$cryptMaxCharsValue = 245; // There are char limitations on openssl_private_encrypt() and in the url below are explained how define this value based on openssl key format: https://www.php.net/manual/en/function.openssl-private-encrypt.php#119810

$debug = Array(
    'print-msgs' => true,
    'print-openssl-errors' => false,
    'print-openssl-crypt' => false,
    'print-key-details' => false,
);

// ##################### START DEFINITION OF JWT

// ===== Definition of header

$header = [
   'alg' => 'RSA',
   'typ' => 'JWT'
];

$header = json_encode($header);
$header = base64_encode($header);

// ===== Definition of payload

$payload = [
    'iss' => 'localhost', // The issuer of the token
    'sub' => 'test', // The subject of the token
    'aud' => 'private', // The audience of the token
    'exp' => '1300819380', // This will define the expiration in NumericDate value. The expiration MUST be after the current date/time.
    'data' => [ // Change it with use case data
        'name' => 'User',
        'email' => 'user@mail'
    ]
];

$payload = json_encode($payload);
$payload = base64_encode($payload);

// ===== START ENCRYPT SIGN JWT

$data = $header.".".$payload;

// ===== Print example header

if($debug['print-msgs']){
    echo "JWT CRYPT / DECRYPT EXAMPLE\n\n";
    echo "Value of header . payload: ".$data."\n";
}

// ===== Open private path and return this in string format

$fp = fopen($keyPrivatePath,"r");
$keyPrivateString = fread($fp,8192);
fclose($fp);

// ===== Open private key string and return 'resourse'

if(isset($keyPrivatePassword)){
    $resPrivateKey = openssl_get_privatekey($keyPrivateString,$keyPrivatePassword);
} else {
    $resPrivateKey = openssl_get_privatekey($keyPrivateString);
}

// ===== If any openssl error occurs, print it

$openSSLError = false;
if($debug['print-openssl-errors']){
    while($msg = openssl_error_string()){
        echo $msg . "\n";
        $openSSLError = true;
    }
}

// ===== See details of a private key

if($debug['print-key-details']){
    $keyPrivateDetails = openssl_pkey_get_details($resPrivateKey);

    echo "Private Key Details:\n";
    echo print_r($keyPrivateDetails,true)."\n";
}

// ===== Crypt data in parts if necessary. When char limit of data is upper than 'cryptMaxCharsValue'.

$rawDataSource = $data;

$countCrypt = 0;
$partialData = '';
$encodedData = '';
$split = str_split($rawDataSource , $cryptMaxCharsValue);
foreach($split as $part){
    openssl_private_encrypt($part, $partialData, $resPrivateKey);
    
    if($debug['print-openssl-crypt']){
        $countCrypt++;
        echo "CRYPT PART ".$countCrypt.": ".$partialData."\n";
    }
    
    $encodedData .= (strlen($encodedData) > 0 ? '.':'') . base64_encode($partialData);
}

// ===== If any openssl error occurs, print it

$openSSLError = false;
if($debug['print-openssl-errors']){
    while($msg = openssl_error_string()){
        echo $msg . "\n";
        $openSSLError = true;
    }
}

// ===== Print data encrypted

if($debug['print-msgs']){
    if($openSSLError) echo "\n";

    echo "Encrypted signature: ".$encodedData."\n";
}

// ===== Encode base64 again to remove dots (Dots are used in JWT syntaxe)

$encodedData = base64_encode($encodedData);

if($debug['print-msgs']){
    echo "Encrypted signature Base64: ".$encodedData."\n";
}

$signature = $encodedData;

// ===== FINISH JWT

$JWTToken = $header.".".$payload.".".$signature;

if($debug['print-msgs']){
    echo "\nJWT Token: ".$JWTToken."\n\n";
    echo "FINISH CREATE JWT!\n\n";
}

// ##################### START VALIDATE JWT

$token = $JWTToken;

$part = explode(".",$token);

$header = $part[0];
$payload = $part[1];
$signature = $part[2];

$encodedData = $signature;

// ===== Open public path and return this in string format

$fp = fopen($keyPublicPath,"r");
$chavePublicaString = fread($fp,8192);
fclose($fp);

// ===== Open public key string and return 'resourse'

$resPublicKey = openssl_get_publickey($chavePublicaString);

// ===== If any openssl error occurs, print it

$openSSLError = false;
if($debug['print-openssl-errors']){
    while($msg = openssl_error_string()){
        echo $msg . "\n";
        $openSSLError = true;
    }
}

// ===== See details of a public key

if($debug['print-key-details']){
    $keyPublicDetails = openssl_pkey_get_details($resPublicKey);

    echo "Public Key Details:\n";
    echo print_r($keyPublicDetails,true)."\n";
}

// ===== Decode base64 to reaveal dots (Dots are used in JWT syntaxe)

$encodedData = base64_decode($encodedData);

if($debug['print-msgs']){
    echo "Encrypted signature: ".$encodedData."\n";
}

// ===== Decrypt data in parts if necessary. Using dots as split separator.

$rawEncodedData = $encodedData;

$countCrypt = 0;
$partialDecodedData = '';
$decodedData = '';
$split2 = explode('.',$rawEncodedData);
foreach($split2 as $part2){
    $part2 = base64_decode($part2);
    
    if($debug['print-openssl-crypt']){
        $countCrypt++;
        echo "CRYPT PART ".$countCrypt.": ".$part2."\n";
    }
    
    openssl_public_decrypt($part2, $partialDecodedData, $resPublicKey);
    $decodedData .= $partialDecodedData;
}

// ===== Print data decrypted

if($debug['print-msgs']){
    echo "Decrypted signature: ".$decodedData."\n";
}

// ===== If any openssl error occurs, print it

$openSSLError = false;
if($debug['print-openssl-errors']){
    while($msg = openssl_error_string()){
        echo $msg . "\n";
        $openSSLError = true;
    }
}

// ===== Validate JWT

if($debug['print-msgs']){
    echo "\nFINISH VALIDATE JWT!\n\n";
}

if($header.".".$payload === $decodedData){
    echo "VALID JWT!\n\n";
    
    $payload = base64_decode($payload);
    $payload = json_decode($payload,true);
    
    echo "Payload:\n";
    echo print_r($payload,true);
} else {
    echo "INVALID JWT!";
}

4 - Example of inverted encryptation. 4 - 反向加密的示例。 Use public key encryptation => private key decryptation:使用公钥加密 => 私钥解密:

// ====================== ENCRYPTATION INVERSE ==================
// If want to change Private Key Encryptation -> Public Key Decryptation to Public Key Encryptation -> Private Key Decryptation this example can help.

$keyPrivatePassword = 'password';
$keyPrivatePath = "private.key";
$keyPublicPath = "public.key";

// ===== Open private path and return this in string format

$fp = fopen($keyPrivatePath,"r");
$keyPrivateString = fread($fp,8192);
fclose($fp);

// ===== Open public path and return this in string format

$fp = fopen($keyPublicPath,"r");
$keyPublicString = fread($fp,8192);
fclose($fp);

// ===== Test of encryptation

$data = 'Data to encrypt';

$resPrivateKey = openssl_get_privatekey($keyPrivateString,$keyPrivatePassword);
$resPublicKey = openssl_get_publickey($keyPublicString);

echo 'Data: '.$data."\n";

openssl_public_encrypt($data, $encData, $resPublicKey);

echo 'encData: '.$encData."\n";

openssl_private_decrypt($encData, $decData, $resPrivateKey);

echo 'decData: '.$decData."\n";

A slightly cleaner and refactor version could be something like this, you will need to have ext-openssl extension installed in your PHP binary which is most likely already installed.稍微清洁和重构的版本可能是这样的,您需要在 PHP 二进制文件中安装ext-openssl扩展,该二进制文件很可能已经安装。

You can create the JWT token like this:您可以像这样创建 JWT 令牌:

$header = json_encode([
    'typ' => 'JWT', '
    alg'  => 'RS256',
]);

$payload = json_encode([
   'iss'   => '12345678-1234-1234-1234-123456123456',
   'sub'   => '12345678-1234-1234-1234-123456123456',
   'aud'   => 'your-awesome-url.com',
   'iat'   => 1663792210,
   'exp'   => 1979411410,
   'scope' => 'everything',
]);

$base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header));

$base64UrlPayload = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($payload));

$data = $base64UrlHeader . "." . $base64UrlPayload;

$privateKey = <<<PRIVATE_KEY
-----BEGIN RSA PRIVATE KEY-----
YOUR_SUPER_SECRET_PRIVATE_KEY
-----END RSA PRIVATE KEY-----
PRIVATE_KEY;

openssl_sign($data, $signature, $privateKey, OPENSSL_ALGO_SHA256);

$base64UrlSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature));

// Your JWT signed with supplied private key
$jwt = $data . "." . $base64UrlSignature;

You can also validate the signature like so:您还可以像这样验证签名:

$publicKey = <<<PUBLIC_KEY
-----BEGIN PUBLIC KEY-----
YOUR_LOVELY_PUBLIC_KEY
-----END PUBLIC KEY-----
PUBLIC_KEY;

$verify = openssl_verify($data, $signature, $publicKey, "sha256WithRSAEncryption");

I have used Otávio's answer along with following links:我使用了 Otávio 的答案以及以下链接:

  1. https://dev.to/robdwaller/how-to-create-a-json-web-token-using-php-3gml https://dev.to/robdwaller/how-to-create-a-json-web-token-using-php-3gml
  2. https://www.php.net/manual/en/function.openssl-sign.php https://www.php.net/manual/en/function.openssl-sign.php

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM