[php]public key(PEM)으로 jwt 검증하는 방법
composer require firebase/php-jwt 명령어를 2번 입력하면.. 설치와 로드?가 됩니다. 구글 소스같은데 좀 마음이 놓이네요.(https://github.com/firebase/php-jwt)
차례대로.. 사용한 코드를 하나한 보겠습니다. 애플 아이디로 로그인하기를 구현하면서 진행한 내용입니다.
<?php
require_once '/home/bitnami/vendor/autoload.php';
use CoderCat\JWKToPEM\JWKConverter;
use \Firebase\JWT\JWT;
use \Firebase\JWT\JWK;
$url = 'https://appleid.apple.com/auth/keys';
$jwkSetPure = json_decode(file_get_contents($url), true);//JSON -> Array
$jwkSet = array();
for($i=0; $i<count($jwkSetPure['keys']); $i++){
$jwkSet[] = $jwkSetPure['keys'][$i];
}
애플에서 제공하는 public keys를 몇 번의 변형을 거쳐야 합니다.
일단 JSON으로 리턴값이 오니 Array로 바꿉니다. 그 다음은 배열에서 ['keys'] 부분을 없애줍니다.
그러면 JWK(Json Web Key) 혹은 JWKS(Json Web Key Set)라고 불리는 것이 완성?됩니다.
$jwkConverter = new JWKConverter();
$PEMs = $jwkConverter->multipleToPem($jwkSet);
그리고 JWKS를 PEM형식?의 public key로 변형합니다.
print_r($PEMs)로 찍어보면 아래의 값이 나와야 합니다.
Array ( [0] => -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiGaLqP6y+SJCCBq5Hv6p GDbG/SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInq UvjJur++hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPyg jLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk+ILjv1bORSRl 8AK677+1T8isGfHKXGZ/ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl 4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw+zHL wQIDAQAB -----END PUBLIC KEY----- [1] => -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4dGQ7bQK8LgILOdLsYzf ZjkEAoQeVC/aqyc8GC6RX7dq/KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdD Nq1n52TpxQwI2EqxSk7I9fKPKhRt4F8+2yETlYvye+2s6NeWJim0KBtOVrk0gWvE Dgd6WOqJl/yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X+Tip84wqwyRpU lq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll+p/Dg8vAXxJLIJ4SNLcqgFeZe 4OfHLgdzMvxXZJnPp/VgmkcpUdRotazKZumj6dBPcXI/XID4Z4Z3OM1KrZPJNdUh xwIDAQAB -----END PUBLIC KEY----- ) |
list($header, $payload, $signature) = explode (".", $jwt);
$decoded_header = base64_decode($header);
$decoded_header_array = json_decode($decoded_header);
for($i=0; $i<count($jwkSet); $i++){
$jwk = $jwkSet[$i];
if($jwk['kid'] == $decoded_header_array->kid){
try{
$decoded = JWT::decode($jwt, $PEMs[$i], array('RS256'));
print_r($decoded);
} catch (Exception $ex) {
echo 'exception occurred.';
echo '<br>';
echo $ex->getMessage();
echo '<br>';
}
break;
}
}
현재 애플에서 제공되는 키가 2개이고.. 애플 로그인시 전달받는 identityToken(jwt)은 이 두 키중 하나로 생성이 됩니다.
일단 jwt가 어떤 key로 생성됐는지는 간단한 몇 가지 과정을 통해 알 수 있습니다.
핵심은 JWT는 단순히 base64 encoded 데이터라는 것입니다. 그래서 단순히 base64_decode를 하면.. 원래의 값을 알 수 있습니다.
base64_decode()에 의해 반환되는 값은 단순한 문자열인데 JSON형식입니다. 그래서 json_decode()를 통해.. 친숙한 배열로 변환시켜 줍니다.
그리곤 jwkSet을 돌면서.. jwkSet의 kid가.. jwt의 kid와 같은 경우를 발견하면 jwt 검증(verify, validate)를 위해 JWT::decode()함수를 사용합니다.
jwt에 문제가 있으면.. 문제에 따라 발생하는 exception이 다릅니다. 간단하게 exception 출력하도록 되어있습니다.
참고
jwt 디코딩 및 검증 가능한 곳: https://jwt.io/
댓글 영역