[PHP] OPENID CONNECT #3 驗證id_token

URL Link //n.sfs.tw/11231

2017-06-08 15:11:54 By Axer

此文分為幾個部分:

[PHP] OPENID CONNECT #0 簡介及取得URL
[PHP] OPENID CONNECT #1 取得ACCESS TOKEN
[PHP] OPENID CONNECT #2 取得USERINFO及PROFILE
[PHP] OPENID CONNECT #3 驗證id_token
[PHP] OPENID CONNECT #4 程式下載及安裝

#1 取得ACCESS TOKEN #2 取得USERINFO及PROFILE兩篇文件中ACCESS TOKEN取回時也有得到 id_token的欄位,id_token的欄位是JSON Web Token(JWT)的格式

id_token介紹

1. BASE64 編碼

2. 分成三個部分,中間用 '.'作區分

3. 一個JWT範例。其中第1部分(青色)寫的是簽章方式,第2部分(綠色)是OP送出來部分的欄位CLAIMS,第3部分(黃色)是由第1、第2兩部分簽章後的結果,2進位內容

eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJjMWI2YjBiZS1iMGQzLTQ1MmEtOWRjMS03ODc5M2NjOTdlYWMiLCJhdWQiOiJkODE3MGMyODYxZDIwZGE3N2M1ODM5YmM3MWVhNDkzNyIsImlzcyI6Imh0dHBzOlwvXC9vaWRjLnRhbmV0LmVkdS50dyIsImV4cCI6MTQ5NjMyNTk0NiwiaWF0IjoxNDk2MzIyMzQ2fQ.g_0Aye-3oTlDXOMq90U2yke-K-7CeNUoVOi_70B9dYaShSmUYuEph72PAVvMrlqTkb0zFqIZhkTflfCqpzwqbsnlpMlU-BvUd7JpStPRmiKz_hDXOANEFRqSkKHhGHqcTF4TPFkDSAd8UOqmh_YH23i36RW_CwxbzyS5X2KdL_NNrdh5YorxDa3od4oit3QEFqrr1_HBbD1eub0bzE13c4XnI0pjDVaCW5jF3YJ8qq52_wf3l5DQ-CoBVrpCzMmEYwWNbeydEjKpyXNZ45132EqxMwvU9IUQLqaFyB9Y8fSvZv-qwJCIV8FJWpBrxIopuaQdVp6cgNQja1hrQ_l3sQ

 

經由BASE64 解碼後內容範例為:

{"alg":"RS256"}{"sub":"c1b6b0be-b0d3-452a-9dc1-78793cc97eac","aud":"d8170c2861d20da77c5839bc71ea4937","iss":"https:\/\/oidc.tanet.edu.tw","exp":1496325946,"iat":1496322346}4'·¡9C\ã*÷E6ÊGŠì'R…N‹½õÖJ¦Q‹„¦ö<o2¹jNFôÌZˆf~Wªœð©»'–“%PÔw²iJÓÑš"³„5ÎÑF¤¤(xF§„Ï@Òß:©¡`}·‹~‘X,1o<’å}Št³M­ØybŠñ ­èwŠ"·tªëÔp[W®oFó]ÜáyÈÒ˜ÃU –æ1w`Ÿ*«°yy ¨Zé3&Œ5·²tHʧ%ÍgŽußa*ÄÌ/SÒ@ºš }cÇÒ½›êÀˆWÁIZkÄŠ)¹¤Vžœ€Ô#kXkB]ì

4. 欄位說明

alg 簽章方式,採用 RS256方式簽章,對稱金鑰

sub subject 帳號唯一識別碼

aud audience 聽眾就是你的client_id

iss issuer id_token發行者

exp 有效時間 timestamp

iat issue at time 簽發時間 timestamp iat+3600=exp

產生公鑰

和SSL有關的,可用來驗證ID TOKEN的資料在OPENID CONNECT #0 簡介及取得URL此文中有:

"jwks_uri" : "https://oidc.tanet.edu.tw/oidc/v1/jwksets"

其內容為

{"keys":[{"kty":"RSA","e":"AQAB","use":"sig","kid":"MOESIGN","alg":"RS256","n":"mzzA8UMrmiFxqA4fCQWx_BGwxkpZ5QrTBkQkHBpQ0piISte7VS-xbvcgFdrT6Gzsq-f36HgSO8pwHPkioJaklo_4ro_VgIWStBd2Jjd6kDEa5Kzbyc_9_FRDtjXA_giiMwTIqHQwYsAWgOrNw7a_J9DGOPhtwfnHNRu30iAtWMga3iGh1vcWOVSZyl_xzFBuLb0hYdaKG8beJ-qp-hiSQqxCAcEapeXoMwAVomlpaQDsHwyNigW-CxTVSNfbZxLCmMabEPqBJSnAkiHaSrv8_ATBHi-LAF3JIIzD5qGUgp1vVDVKEm9HrlIvOO7spyyCw_I4CdSZXS2vERyMSTitvQ"},{"kty":"RSA","e":"AQAB","use":"enc","kid":"MOEENC","alg":"RS256","n":"n7ZK5I46lbjGUFX6tCOEXyM6tW-ipAMI4eUzMpCKYxH-Qp4RJdCJOLNit7mt_pJsBIgT5wryFPJliFY-jVPD30OjlLbKQ73Ztv75yrPc1iX0EgwZejupz_WD_hmdZ5YmNu5XPEv45HrHAT2xhu_GDqtivr132m7cxfBg3ahqlnnPjaBC5qUlbhyep_dRmf0G-SJHoU2KMyZrbjM-Ecp5Zf5o7-WmtE9toME9YjC0yxEbCCRwZJakbH0xwwUTYT8-jY_YTHMlCNtlCfz-Ev3fQL8sphTZqFmUuTrO-kdbpaqQ8x4DrjkuZYhCYF7VHPtTrQbYETfR19UjVKQxvcvaXw"}]}

取其中的 use:"sig"的key值module(n)和exponents(e)來演算公錀(public key)

PHP

使用modulus(n)和exponent(e)來產生金鑰, 需要安裝套件phpseclib,程式碼

<?php
use phpseclib\Crypt\RSA;
use phpseclib\Math\BigInteger;

//取回access token
include "config.php";
include "library.class.php";
include('./vendor/autoload.php'); //驗證 id_token 所需套件

$obj= new openid();

$jwks_uri=JWKS_URI;
if(DYNAMICAL_ENDPOINT){
   $jwks_uri=$ep->getEndPoint()->jwks_uri;
}
$jd = $obj->getModnExp($jwks_uri);
if( !$jd) {
  die ("無法取得 USER INFO");
}
$e= $jd['keys'][0]['e'];
$n= $jd['keys'][0]['n'];

// 取得public key,需安裝套件
// https://github.com/phpseclib/phpseclib
$rsa = new RSA();
$nx = new BigInteger($obj->urlsafeB64Decode($n), 256);
$ex = new BigInteger($obj->urlsafeB64Decode($e), 256);

$rsa->loadKey(
    array(
    'e' => $ex,
    'n' => $nx
    )
);

$pubkey= $rsa->getPublicKey();

其中 $pubkey輸出結果

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmzzA8UMrmiFxqA4fCQWx
/BGwxkpZ5QrTBkQkHBpQ0piISte7VS+xbvcgFdrT6Gzsq+f36HgSO8pwHPkioJak
lo/4ro/VgIWStBd2Jjd6kDEa5Kzbyc/9/FRDtjXA/giiMwTIqHQwYsAWgOrNw7a/
J9DGOPhtwfnHNRu30iAtWMga3iGh1vcWOVSZyl/xzFBuLb0hYdaKG8beJ+qp+hiS
QqxCAcEapeXoMwAVomlpaQDsHwyNigW+CxTVSNfbZxLCmMabEPqBJSnAkiHaSrv8
/ATBHi+LAF3JIIzD5qGUgp1vVDVKEm9HrlIvOO7spyyCw/I4CdSZXS2vERyMSTit
vQIDAQAB
-----END PUBLIC KEY-----

接下來要由public key來驗證 id_token

驗證id_token

取得 public key後就可驗證 id_token, 需要安裝套件namshi/jose,這個套件對於PHP>=7.0有支持上的問題,會出現錯誤:

Fatal error: Uncaught InvalidArgumentException: phpseclib 1.0.0(LTS), even the latest 2.0.0, doesn't support PHP7 yet in /.../vendor/namshi/jose/src/Namshi/JOSE/JWS.php:xxx

需要修改原始碼 JWS.php把限制改掉

修改 ./vendor/namshi/jose/src/Namshi/JOSE/JWS.php 把第41-43行註解

PHP

驗證 id_token

// 驗證id token,需安裝套件
// composer require namshi/jose
$id_token = $_SESSION['id_token'];
$JWS= new JWS(array(),'SecLib');
$jws = $JWS->load($id_token);
$p=$jws->getPayload();;  //取得payload

if ($jws->verify($pubkey, 'RS256')) {
 echo sprintf("ID_TOKEN驗證通過,USER UNIQUE ID= #%s", $p['sub']);
}else{
 echo "ID_TOKEN驗證失敗";
}

執行結果

下一篇:OPENID CONNECT #4 程式下載及安裝

參考資料

[1] 驗證JWT https://jwt.io/

[2] https://jwt.io/introduction/

[3] http://phpseclib.sourceforge.net/rsa/intro.html

[4] https://stackoverflow.com/questions/16993838/openssl-how-can-i-get-public-key-from-modulus

[5] https://hotexamples.com/examples/-/phpseclib%255CCrypt%255CRSA/setPublicKey/php-phpseclib%255ccrypt%255crsa-setpublickey-method-examples.html