Different types of keystore in Java -- JKS

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

JKS là Java Keystore, một loại kho khóa độc quyền được thiết kế cho Java. Nó có thể được sử dụng để lưu trữ khóa riêng và chứng chỉ được sử dụng cho giao tiếp SSL, tuy nhiên nó không thể lưu trữ khóa bí mật. Công cụ keytool được cung cấp với JDK không thể trích xuất khóa riêng được lưu trữ trên JKS. Loại kho khóa này thường có phần mở rộng là jks.

Tiếp theo chúng ta sẽ trình bày cách vận hành kho khóa JKS bằng mã Java thuần túy.

Tạo kho khóa JKS

Phương pháp đơn giản nhất để tạo kho khóa JKS là tạo một kho khóa trống. Chúng ta có thể lấy một thể hiện của KeyStore và sau đó tải một kho khóa null. Sau khi tải kho khóa null, chúng ta chỉ cần gọi KeyStore.store() với tên kho khóa và mật khẩu của kho khóa.

Dưới đây là một ví dụ đơn giản:

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

Sau khi thực thi lệnh trên, bạn sẽ thấy một kho khóa có tên mytestkey.jks trong thư mục làm việc hiện tại. Bây giờ kho khóa đang trống mà không có bất kỳ mục nào.

Lưu khóa riêng

Bây giờ chúng ta hãy lưu một khóa riêng và chuỗi chứng chỉ liên quan của nó vào kho khóa. Lưu ý rằng chúng ta không thể lưu trữ một khóa riêng mà không có chuỗi chứng chỉ liên quan vào kho khóa bằng JDK. Với một số thư viện khác hoặc thư viện gốc, bạn có thể lưu trữ một khóa riêng mà không cần chuỗi chứng chỉ liên quan.

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();
}

Đầu tiên, chúng ta sẽ tạo một khóa riêng và một chứng chỉ tự ký, sau đó gọi KeyStore.setKeyEntry() với bí danh đã chỉ định, khóa, mật khẩu cho khóa và chuỗi chứng chỉ liên quan của nó. Hãy nhớ rằng chúng ta cần gọi KeyStore.store() để lưu khóa vào kho khóa.

Bí danh là nhãn của mục để nó có thể được tìm thấy dễ dàng sau này.

Lưu chứng chỉ

Chúng ta có thể lưu chứng chỉ trên kho khóa JKS. Chứng chỉ cần lưu trữ phải là X509Certificate. Nó có thể được lưu trữ trong kho khóa mà không cần khóa riêng liên quan. Quá trình này tương tự như lưu trữ khóa riêng.

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();
}

Tải khóa riêng

Sau khi lưu trữ các khóa, chúng ta cũng có thể tải các mục bên trong kho khóa. Ở đây chúng ta đang nói đến việc tải khóa riêng, thực tế không phải như vậy, như chúng ta đã mô tả ở trên, khóa riêng không thể được trích xuất từ JKS bằng Java. Ở đây chúng ta thực sự trích xuất chuỗi chứng chỉ của khóa riêng.

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());   //Bạn sẽ nhận được một NullPointerException nếu bạn bỏ ghi chú dòng này
	
	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();
}

Lưu ý dòng được chú thích, khóa sẽ là null như mong đợi. Tuy nhiên, chúng ta có thể lấy chuỗi chứng chỉ như bình thường.

[
[
  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

]

Tải chứng chỉ

Điều này tương tự như tải khóa riêng, chúng ta cần truyền bí danh của chứng chỉ mà chúng ta muốn trích xuất.

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();
}

Kết quả đầu ra sẽ là:

[
[
  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.....

]

Nhập khóa và chứng chỉ

Quá trình này thực sự rất đơn giản, trước tiên chúng ta cần tải kho khóa nơi chứng chỉ cần được nhập. Sau đó, chúng ta cũng cần tải một kho khóa khác nơi chúng ta cần nhập chứng chỉ vào. Tiếp theo, chúng ta cần lấy chứng chỉ từ kho khóa nguồn và đặt nó vào kho khóa đích.

Vì chúng ta không thể trích xuất khóa riêng từ JKS, nên chúng ta chỉ có thể nhập chứng chỉ vào JKS. Tuy nhiên, chúng ta có thể trích xuất khóa riêng từ các loại kho khóa khác (PKCS12) và sau đó lưu chúng vào kho khóa JKS.

Một thông tin cuối cùng. Oracle cung cấp hai phiên bản kho khóa JKS: phân biệt chữ hoa chữ thườngkhông phân biệt chữ hoa chữ thường. Khi gọi KeyStore.getInstance("JKS"), một phiên bản kho JKS không phân biệt chữ hoa chữ thường được tạo ra, khi gọi KeyStore.getInstance("CaseExactJKS"), một phiên bản kho JKS phân biệt chữ hoa chữ thường sẽ được tạo ra. Thông thường, không phân biệt chữ hoa chữ thường được khuyến nghị vì người dùng nên phân biệt các mục khác nhau bằng các tên bí danh khác nhau thay vì các trường hợp tên bí danh khác nhau. Để biết thêm thông tin về phân biệt chữ hoa chữ thường, vui lòng tham khảo bài viết này.

Chúng ta sẽ đề cập đến các loại kho khóa khác trong các bài viết sau.

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

Robot as a Service

Is this what people's life is like in the future? Robot 4.0?