json web token (jwt) คืออะไร

JSON Web Token (JWT) มาตรฐาน (RFC 7519) ใช้ระบุตัวตนโดยใช้กับ API รับส่งข้อมูลที่ secure ระหว่าง Third parties ด้วย JSON object. ข้อมูลเก็บแบ่งออกเป็น 3 ส่วนคือ HEADER, PAYLOAD และ SIGNATURE

HEADER

เก็บข้อมูล algorithm ที่ใช้ในการเข้ารหัส

PAYLOAD

ข้อมูลที่เป็นสาธารณะ (ไม่เป็นความลับหรืออันตราย เช่นพวก บัตรประชาชน)

SIGNATURE

ข้อมูลในส่วนนี้สำคัญที่สุด จะเข้ารหัสด้วยลายมือชื่อดิจิตอล(digitally signed) พวกบอกว่าบุคคลนี้เข้าสู่ระบบจากผู้ให้บริการนี้ โดยลายมือชื่อดิจิตอลนี้ใช้กระบวนการเข้ารหัสได้ 2 วิธีคือ
1. แบบ Symmetric Algorithm Hash-based Message Authentication Code (HMAC) algorithm โดยกำหนดรหัสลับขึ้นมาเอง
2. แบบ Asymmetric Algorithm ใช้ public/private key pair using RSA (Rivest–Shamir–Adleman). เช่น RS256
**โดยในตัวอย่างนี้จะใช้วิธีที่ 2.

แล้วเราจะใช้ Symmetric หรือ Asymmetric?

Symmetric จะใช้เฉพาะงานระหว่าง 2 parties ต่างจาก Asymmetric ใช้กับงานลักษณะ 1-to-many ยกตัวอย่างเช่น google ที่มี service มากมายเช่น google docs, google calendar, google adwords ฯลฯ เป็นต้น

คำสั่งในการสร้าง public/private key pair ด้วย RSA
1. OpenSSL เป็นไลบรารีสำหรับใช้ในการเข้ารหัสลับข้อมูลผ่านโพรโทคอล SSL และ TLS ซึ่งเป็นไลบรารีแบบ Open source

openssl genrsa -out key.rsa
openssl rsa -in key.rsa -pubout > key.rsa.pub

android jose4j lib หากพบ error InvalidKeyException: An RSA key of size 2048 bits or larger MUST be used with the all JOSE RSA algorithms (given key was only 512 bits.
ให้แก้ปัญหาตามนี้
https://stackoverflow.com/questions/31779585/verification-key-for-jose4j-jwtconsumer
2. ssh เป็นมาตรฐานการรักษาความปลอดภัยที่ถูกออกแบบมาสำหรับ SSH โปรโตคอล

ssh-keygen

2 วิธีข้างต้นนี้ โดยทั่วไปไม่ต่างกัน แต่จะต่างกันตรงที่
- key format default ของ ssh-keygen จะเป็น RFC4716 แต่ของ openssl จะเป็น PKCS8 (Public Key Cryptography Standard) ซึ่งจะมีผลกับบาง Application ที่รองรับ key format ต่างกัน
- ไฟล์ public key ที่สร้างขึ้นมา ของ ssh-keygen จะเป็น ssh format แต่ของ openssl จะเป็น PEM (Privacy-Enhanced Mail) format
ตัวอย่าง public key ssh format

ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAz+uZd6FjyvRDTztBlJ/DlkdU72qzbF9t+U0a5vwcZfhE8WrsjGNcIS19KBlkgJVBnvxisa3lYmvXzyQKZAIm0mK3Dj0FNIOmu44i4Y3GdufO6TNTapnJY9YBl/idd1lcUHzuCxcxqrYCdDStYN8BK5qD2/DGF23s+BoF0k9t90oJK3fPQdyG1BzYf5qSIQaGL2Y30XX3MY02vSB05ZDHC8Rg1SIh+V5y4Crwz45wvUPP/t28MRiC3Ui44aO/PTDcXKe2OoN99GygjYmN12KSfx5KHClNUqwrZH++wCb4U/D250N34GcIa55kkCCy1khZnbPb9y0oPpz4A4eJF78VqQ== Panupong_Kon@RS37-PANUPONG

ตัวอย่าง public key PEM format

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsuZiyK/IG+iuRuQpCDHF
wC7keKY/sFB6BfeW/KFZ/t3ISHuwUlwDEyiQMoDKpKPtIA5UBMN3Ys56FGfucFzI
wBjS+vyVd1p0Du02lJClNPFzuIjZ3okXDuP7IjoF9aPzStvxdgqfpDc2BjgnKa7U
v/O/yv6qzifAvUybNlToGL3y8mJYy4cQNMuafWvHdpXdNdTrNnG/BGg+MAhsHn+y
CjMLlkRsAUrRrR0J3pGpOxxB3PBKHr2tAG4kP3VvHb61ePmauAKz7z3tLxOtVu34
mUxLcItTcMh/mMTit0O8NqDNFstPT0bivL+umWk377G2QPvbVY62mxTp2zCSGFUK
fwIDAQAB
-----END PUBLIC KEY-----

เราสามารถ Convert public key ตาม format ได้
วิธี Convert public key จาก PEM เป็น ssh-rsa format

ssh-keygen -f private.pem -y > public.pub

วิธี Convert public key จาก ssh-rsa format เป็น PEM หรือจะเป็น PKCS8 ใน option -m

ssh-keygen -f id_rsa.pub -e -m PEM

วิธี Convert private key จาก format RFC4716 เป็น PKCS8 โดยที่ใช้ public key ตัวเดิม

openssl  pkcs8 -topk8 -inform PEM  -outform PEM -nocrypt -in input_rsa_rfc4716 -out id_rsa_pkcs8

https://security.stackexchange.com/questions/29876/what-are-the-differences-between-ssh-generated-keysssh-keygen-and-openssl-keys

https://stackoverflow.com/questions/8290435/convert-pem-traditional-private-key-to-pkcs8-private-key

ลักษณะโดยทั่วไปของ JWT

  • Compact: ลดขนาดการรับส่งข้อมูล ทำให้รับส่งไวขึ้น
  • Self-contained: เก็บข้อมูล user ไว้ใน payload โดย verified ด้วยลายมือชื่อที่ client ไม่ต้องเรียก server เพื่อ query database ทำให้ลด load จำนวนมหาศาล

เมื่อไรถึงจะควรใช้ JWT

  • Authentication: ระบบที่ใช้ Login แบบ SSO , Oauth
  • Information Exchange: ข้อมูลที่มีการแลกเปลี่ยนกันบ่อย เช่น API get profile , get info

SERVER จังหวะ Signing return id_token ด้วย public/private pair key

$header = array(
'algor' => 'RS256',
'jku' => 'https://www.googleapis.com/oauth2/v2/certs'  // example google public jwk uri
);

$payload = array(
'sub' => '134567899', //subject identify unique id
'name' => 'kongarn',
'aud' => '29' // client_id
);

$signature_input = base64_encode($header) . base64_encode($payload);

$signature = RSA_ENCRYPIT($signature_input) //signing signature_input with private_key (private_key จะอยู่ที่ server เท่านั้น client ถือเฉพาะ public key jwk uri

//Json web signature(JWS)
$id_token = signature_input . $signature;

CLIENT จังหวะ verify id_token หลักการจะยืนยันตัวตนด้วย public key นำลูกกุญแจนี้ ไปไข id_token ที่ถูกเข้ารหัสเป็นแม่กุญแจด้วย public/private pair key ตัวอย่างใช้ php lib จะมีฟังก์ชั่น jwt_verify โดยจะนำ public jwk uri เลือก kid(key id) และ n ไปทำ public certificate key และนำมา verify กับ id_token

ปัจจุบันมี Client libraries แทบจะทุกภาษา
1. json web token for php library
2. json web token for java library
3. json web token for jquery

if(jwt_verify($id_token)){//verify jwt_token with json web token public key (jwk)
echo 'correct';
}else{
echo 'wrong';
}

Test decode/verify signature id_token ได้ที่ https://jwt.io/

โดย signature จะ verify ด้วย public certificate สามารถ debug เอา public certificate ได้ใน local lib ของเราเอง ถ้าใน php นั้น เข้าไปใน method jwt_verify จะมีจุดให้ debug หา public certificate

Related posts:

This entry was posted in json web token (jwt). Bookmark the permalink.