Different types of keystore in Java -- JKS

  Pi Ke        2014-09-05 20:21:51       47,013        3          English  简体中文  繁体中文  ภาษาไทย  Tiếng Việt 

JKS คือ Java Keystore ซึ่งเป็นประเภท keystore ที่เป็นกรรมสิทธิ์ ออกแบบมาสำหรับ Java สามารถใช้เพื่อเก็บ private key และใบรับรองที่ใช้สำหรับการสื่อสาร SSL แต่ไม่สามารถเก็บ secret key ได้ อย่างไรก็ตาม keytool ที่มาพร้อมกับ JDKs ไม่สามารถดึง private key ที่เก็บไว้ใน JKS ออกมาได้ Keystore ประเภทนี้มักมีนามสกุลเป็น jks

ต่อไปเราจะแสดงวิธีการใช้งาน JKS keystore ด้วยโค้ด Java บริสุทธิ์

สร้าง JKS keystore

วิธีที่ง่ายที่สุดในการสร้าง JKS keystore คือการสร้าง keystore ว่างเปล่า ก่อนอื่นเราสามารถรับ instance ของ KeyStore และโหลด keystore ที่เป็น null จากนั้นหลังจากโหลด keystore ที่เป็น null แล้ว เราเพียงแค่ต้องเรียก KeyStore.store() ด้วยชื่อ keystore และรหัสผ่านของ keystore

ด้านล่างนี้เป็นตัวอย่างง่ายๆ:

try{
	KeyStore keyStore = KeyStore.getInstance("JKS");
	keyStore.load(null,null);
	
	keyStore.store(new FileOutputStream("mytestkey.jks"), "password".toCharArray());
}catch(Exception ex){
	ex.printStackTrace();
}

หลังจากการเรียกใช้ข้างต้น คุณจะเห็น keystore ชื่อ mytestkey.jks ในไดเร็กทอรีปัจจุบัน ตอนนี้ keystore ว่างเปล่าโดยไม่มีรายการใดๆ

เก็บ private key

ตอนนี้เรามาเก็บ private key หนึ่งตัวและ certificate chain ที่เกี่ยวข้องลงใน keystore โปรดทราบว่าเราไม่สามารถเก็บ private key โดยไม่มี certificate chain ที่เกี่ยวข้องลงใน keystore โดยใช้ JDK ได้ อย่างไรก็ตามด้วยไลบรารีอื่นหรือไลบรารีเนทีฟ คุณอาจสามารถเก็บ private key โดยไม่มี certificate chain ที่เกี่ยวข้องได้

try{
	KeyStore keyStore = KeyStore.getInstance("JKS");
	keyStore.load(new FileInputStream("mytestkey.jks"),"password".toCharArray());
	
	CertAndKeyGen gen = new CertAndKeyGen("RSA","SHA1WithRSA");
	gen.generate(1024);
	
	Key key=gen.getPrivateKey();
	X509Certificate cert=gen.getSelfCertificate(new X500Name("CN=ROOT"), (long)365*24*3600);
	
	X509Certificate[] chain = new X509Certificate[1];
	chain[0]=cert;
	
	keyStore.setKeyEntry("mykey", key, "password".toCharArray(), chain);
	
	keyStore.store(new FileOutputStream("mytestkey.jks"), "password".toCharArray());
}catch(Exception ex){
	ex.printStackTrace();
}

ก่อนอื่น เราจะสร้าง private key และ self signed certificate จากนั้นเรียก KeyStore.setKeyEntry() ด้วย alias ที่ระบุ key รหัสผ่านสำหรับ key และ certificate chain ที่เกี่ยวข้อง โปรดจำไว้ว่าเราต้องเรียก KeyStore.store() เพื่อเก็บ key ลงใน keystore

Alias คือป้ายกำกับของรายการเพื่อให้สามารถค้นหาได้ง่ายในภายหลัง

เก็บใบรับรอง

เราสามารถเก็บใบรับรองบน JKS keystore ได้ ใบรับรองที่จะเก็บควรเป็น X509Certificate สามารถเก็บไว้ใน keystore โดยไม่มี private key ที่เกี่ยวข้อง กระบวนการนี้คล้ายกับการเก็บ private key

try{
	KeyStore keyStore = KeyStore.getInstance("JKS");
	keyStore.load(new FileInputStream("mytestkey.jks"),"password".toCharArray());
	
	CertAndKeyGen gen = new CertAndKeyGen("RSA","SHA1WithRSA");
	gen.generate(1024);
	
	X509Certificate cert=gen.getSelfCertificate(new X500Name("CN=SINGLE_CERTIFICATE"), (long)365*24*3600);
	
	keyStore.setCertificateEntry("single_cert", cert);
	
	keyStore.store(new FileOutputStream("mytestkey.jks"), "password".toCharArray());
}catch(Exception ex){
	ex.printStackTrace();
}

โหลด private key

หลังจากเก็บ key แล้ว เราสามารถโหลดรายการภายใน keystore ได้ ที่นี่เรากำลังพูดถึงการโหลด private key จริงๆแล้วไม่ใช่กรณีนี้ อย่างที่เราอธิบายไว้ก่อนหน้านี้ private key ไม่สามารถดึงออกมาจาก JKS โดยใช้ Java ได้ ที่นี่เราจริงๆแล้วดึง certificate chain ของ private key

try{
	KeyStore keyStore = KeyStore.getInstance("JKS");
	keyStore.load(new FileInputStream("mytestkey.jks"),"password".toCharArray());
	
	Key key = keyStore.getKey("mykey", "password".toCharArray());
//			System.out.println("Private key : "+key.toString());   //คุณจะได้รับ NullPointerException ถ้าคุณยกเลิกการแสดงความคิดเห็นบรรทัดนี้
	
	java.security.cert.Certificate[] chain =  keyStore.getCertificateChain("mykey");
	for(java.security.cert.Certificate cert:chain){
		System.out.println(cert.toString());
	}
}catch(Exception ex){
	ex.printStackTrace();
}

โปรดทราบว่าบรรทัดที่แสดงความคิดเห็น key จะเป็น null ตามที่คาดไว้ เราสามารถรับ certificate chain ได้ตามปกติ

[
[
  Version: V3
  Subject: CN=ROOT
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

  Key:  Sun RSA public key, 1024 bits
  modulus: 90980299845597512779139009881469177009407272139633139241921529845092210461181243924599150259446249079941561941533303439718936138867375776965995893255358889228584415558006141961051402385279285497775776996780406808976543439543789816486513982581378223575354716191394304768315366544413052547926792470794374067383
  public exponent: 65537
  Validity: [From: Sat Sep 06 09:57:28 CST 2014,
               To: Sun Sep 06 09:57:28 CST 2015]
  Issuer: CN=ROOT
  SerialNumber: [    206b697b]

]
  Algorithm: [SHA1withRSA]
  Signature:
0000: 53 6A FD FE E6 3A 5E 6E   A6 43 C4 F4 D1 56 D4 08  Sj...:^n.C...V..
0010: 7E 3B 8B 73 68 71 56 AB   96 FE 24 E7 2D DC 04 BB  .;.shqV...$.-...
0020: 14 B0 C6 71 8D F0 3E EC   FE D8 5B BB 8C 0F 55 63  ...q..>...[...Uc
0030: 2B 38 8E 45 F1 2D F0 BB   8C 6D 13 A8 11 37 E1 FA  +8.E.-...m...7..
0040: 77 AF C7 73 72 2B 40 4F   74 32 F6 3C 24 E6 AB ED  w..sr+@Ot2.<$...
0050: 2C 6F 19 2E DC 58 5F CB   75 62 40 2F 3E BE 59 99  ,o...X_.ub@/>.Y.
0060: C0 1F 7A 70 15 AF C3 66   B3 4F C9 11 C3 45 59 EF  ..zp...f.O...EY.
0070: 36 F4 1C C9 9B FA 5E 43   A0 28 DB 07 0D F2 53 6E  6.....^C.(....Sn

]

โหลดใบรับรอง

คล้ายกับการโหลด private key เราต้องส่ง alias ของใบรับรองที่เราต้องการดึงออกมา

try{
	KeyStore keyStore = KeyStore.getInstance("JKS");
	keyStore.load(new FileInputStream("mytestkey.jks"),"password".toCharArray());
	
	java.security.cert.Certificate cert = keyStore.getCertificate("single_cert");
	
	System.out.println(cert.toString());
}catch(Exception ex){
	ex.printStackTrace();
}

ผลลัพธ์จะเป็น:

[
[
  Version: V3
  Subject: CN=SINGLE_CERTIFICATE
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

  Key:  Sun RSA public key, 1024 bits
  modulus: 99756834215197288877309915243024788596281418171661241282881476656110879586349799740269767889529808199104172091786860877280382867461569439907754755558759387462421169749111354565793974372777424046360810758009149155148290676527032833774084635148674232352006810533640038723102562578516643345287042787777951043863
  public exponent: 65537
  Validity: [From: Sat Sep 06 10:14:33 CST 2014,
               To: Sun Sep 06 10:14:33 CST 2015]
  Issuer: CN=SINGLE_CERTIFICATE
  SerialNumber: [    6943e549]

]
  Algorithm: [SHA1withRSA]
  Signature:
0000: 35 58 70 96 F4 35 82 2A   95 9F BB 31 02 6E 7C 29  5Xp..5.*...1.n.)
0010: 4A FE AF EB 2D B5 3A A7   C7 9D 4C 9A 34 2C 5C 46  J...-.:...L.4,\F
0020: C2 82 A8 AC 1A C0 98 A5   67 21 74 7B 1E E2 E5 AC  ........g!t.....
0030: DE B2 1D 87 BE 16 45 9B   D0 2A D3 2B F6 E1 4B 35  ......E..*.+..K5
0040: 27 8B A7 0A EF F2 07 41   90 A6 69 07 BE 87 C5 B1  '......A..i.....
0050: 54 DE DB A2 5A 41 47 3B   3F A7 74 6F 5C C8 8D B4  T...ZAG;?.to\...
0060: C8 65 2B 0F 8E 94 A8 80   C7 8B B5 78 FA C2 9C ED  .e+........x....
0070: 8E EC 28 E4 8E 62 A1 59   6A BC 37 7B 0D FC C7 AF  ..(..b.Yj.7.....

]

นำเข้า key และใบรับรอง

กระบวนการนี้ง่ายมาก ก่อนอื่นเราต้องโหลด keystore ที่มีใบรับรองที่จะนำเข้า จากนั้นเราต้องโหลด keystore อีกอันหนึ่งที่เราต้องการนำเข้าใบรับรอง ต่อไป เราต้องรับใบรับรองจาก keystore แหล่งที่มาและใส่ลงใน keystore ปลายทาง

เนื่องจากเราไม่สามารถดึง private key ออกจาก JKS ได้ ดังนั้นเราจึงสามารถนำเข้าใบรับรองไปยัง JKS ได้เท่านั้น อย่างไรก็ตามเราสามารถดึง private key ออกจาก keystore ประเภทอื่น (PKCS12) และจากนั้นเก็บไว้ใน JKS keystore

ข้อมูลชิ้นสุดท้าย Oracle ให้ keystore JKS สองเวอร์ชัน: 區分大小寫 และ ไม่區分大小寫 เมื่อเรียก KeyStore.getInstance("JKS") จะสร้าง instance JKS เวอร์ชันไม่區分大小寫 เมื่อเรียก KeyStore.getInstance("CaseExactJKS") จะสร้าง instance JKS เวอร์ชัน區分大小寫 โดยปกติแล้วแนะนำให้ใช้ไม่區分大小寫 เนื่องจากผู้ใช้ควรแยกแยะรายการต่างๆ ด้วยชื่อ alias ที่แตกต่างกัน แทนที่จะเป็นกรณีชื่อ alias ที่แตกต่างกัน สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการ區分大小寫 โปรดดู โพสต์นี้

เราจะครอบคลุม keystore ประเภทอื่นในโพสต์ต่อไป

DEMO  EXAMPLE  KEYSTORE  JKS 

       

  RELATED


  3 COMMENTS


lintao [Reply]@ 2015-01-12 03:43:48

Pi Ke,

I have a comment about this code in  "Loading private key" part:

Key key = keyStore.getKey("alias", "password".toCharArray());

By using "alias", I think you're trying to say private key should be extract by its alias. But, in "Store Private Key" part, the private key has been stored as "mykey". So, to be consistent, I think the upper code should use "mykey"  although the value is null too due to JKS type.  Is my understanding right?  Thanks!

NightCat [Reply]@ 2015-01-12 07:11:36

Yes. You are correct. The alias name here actually doesn't matter. But to be consistent, I have updated it to mykey. Thank you for pointing this out.

Cat Mucius [Reply]@ 2020-02-21 15:52:41

If value of the private key cannot be extracted from a JKS keystore, then how can Cipher object of any provider use it?

Is there some "backdoor" function utilized?



  RANDOM FUN

How Chinese learn to drive

A Chinese lady is learning how to drive secirouly with equipments found in her own house. She is so creative.