Anyboby know some solution in PHP to using JWT (JSON Web Token) with RSA signature without library? I tried so hard to find solutions without library or without using composer. All the options that I found use some kind of library.
Advertisement
Answer
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:
- Create RSA private and public keys for sign JWT using openssl library.
- Create JWT token and sign using RSA private keys.
- Validate JWT’s signed token using RSA public keys.
- Example of inverted encryptation. Use public key encryptation => private key decryptation.
1 – Create RSA private and public keys for sign JWT using openssl library:
// ====================== 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:
// ====================== 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 EXAMPLEnn"; 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."nn"; echo "FINISH CREATE JWT!nn"; } // ##################### 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!nn"; } if($header.".".$payload === $decodedData){ echo "VALID JWT!nn"; $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. 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";