PKCS12 là một định dạng tập tin hoạt động để lưu trữ các đối tượng mật mã học dưới dạng một tệp duy nhất. Nó có thể được sử dụng để lưu trữ khóa bí mật, khóa riêng và chứng chỉ. Đây là một định dạng chuẩn được công bố bởi RSA Laboratories, có nghĩa là nó có thể được sử dụng không chỉ trong Java mà còn trong các thư viện khác ở C, C++ hoặc C# v.v. Định dạng tệp này thường được sử dụng để nhập và xuất các mục từ hoặc đến các loại kho khóa khác.
Tiếp theo, chúng ta sẽ giải thích các thao tác có thể được thực hiện trên kho khóa PKCS12.
Tạo kho khóa PKCS12
Trước khi lưu một mục vào kho khóa PKCS12, kho khóa phải được tải trước. Điều này có nghĩa là chúng ta phải có một kho khóa được tạo trước. Cách đơn giản nhất để tạo kho khóa PKCS12 là:
try{ KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(null, null); keyStore.store(new FileOutputStream("output.p12"), "password".toCharArray()); } catch (Exception ex){ ex.printStackTrace(); }
Lưu ý, khi gọi keyStore.load(null, null), hai giá trị null được truyền làm luồng kho khóa và mật khẩu đầu vào. Điều này là bởi vì chúng ta chưa có kho khóa sẵn sàng. Sau khi chạy chương trình này, sẽ có một tệp kho khóa có tên output.p12 trong thư mục làm việc hiện tại.
Lưu khóa bí mật
PKCS12 cho phép lưu trữ các khóa bí mật trên cơ sở hạn chế. Khóa bí mật thường được sử dụng để mã hóa/giải mã dữ liệu. Để chuyển các khóa một cách thuận tiện, chúng có thể được lưu trữ trong một kho khóa như PKCS12 và được chuyển.
try{ KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(null, null); KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(128); Key key = keyGen.generateKey(); keyStore.setKeyEntry("secret", key, "password".toCharArray(), null); keyStore.store(new FileOutputStream("output.p12"), "password".toCharArray()); } catch (Exception ex){ ex.printStackTrace(); }
Một số khóa bí mật với thuật toán AES được lưu trữ trên kho khóa PKCS12 không thể được trích xuất trong Java. Vì PKCS12 là một tiêu chuẩn di động, các thư viện khác có thể hỗ trợ trích xuất khóa bí mật.
Lưu khóa riêng
Khóa riêng và chuỗi chứng chỉ liên quan của nó có thể được lưu trữ trong kho khóa PKCS12. Kho khóa chứa khóa riêng và chứng chỉ có thể được sử dụng trong giao tiếp SSL trên web.
try{ KeyStore keyStore = KeyStore.getInstance("PKCS12"); // keyStore.load(new FileInputStream("output.p12"),"password".toCharArray()); keyStore.load(null, null);; 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("private", key, "password".toCharArray(), chain); keyStore.store(new FileOutputStream("output.p12"), "password".toCharArray()); }catch(Exception ex){ ex.printStackTrace(); }
Một khóa riêng RSA được tạo bằng CertAndKeyGen và chứng chỉ liên quan cũng được tạo. Sau đó, mục khóa được lưu trữ trong keyStore bằng cách gọi keyStore.setEntry(). Đừng quên lưu keyStore bằng cách gọi keyStore.store(), nếu không mục sẽ bị mất khi chương trình kết thúc.
Lưu chứng chỉ
Kho khóa PKCS12 cũng cho phép lưu trữ chứng chỉ một mình mà không cần lưu trữ khóa riêng tương ứng. Để lưu trữ chứng chỉ, có thể gọi KeyStore.setCertificateEntry().
try{ KeyStore keyStore = KeyStore.getInstance("PKCS12"); // keyStore.load(new FileInputStream("output.p12"),"password".toCharArray()); keyStore.load(null, null);; CertAndKeyGen gen = new CertAndKeyGen("RSA","SHA1WithRSA"); gen.generate(1024); X509Certificate cert=gen.getSelfCertificate(new X500Name("CN=ROOT"), (long)365*24*3600); keyStore.setCertificateEntry("cert", cert); keyStore.store(new FileOutputStream("output.p12"), "password".toCharArray()); }catch(Exception ex){ ex.printStackTrace(); }
Chứng chỉ được lưu trữ có thể được trích xuất bằng cách gọi KeyStore.getCertificate() với bí danh được cung cấp. Ví dụ:
Certificate cert = keyStore.getCertificate("cert");
Tải khóa riêng
Một điểm khác biệt giữa kho khóa PKCS12 và các kho khóa khác như JKS là khóa riêng của PKCS12 có thể được trích xuất mà không có NullPointerException. Khóa riêng có thể được trích xuất chính xác với mật khẩu chính xác được cung cấp.
try{ KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(new FileInputStream("output.p12"), "password".toCharArray()); Key pvtKey = keyStore.getKey("private", "password".toCharArray()); System.out.println(pvtKey.toString()); } catch (Exception ex){ ex.printStackTrace(); }
Kết quả đầu ra của mã trên là:
sun.security.rsa.RSAPrivateCrtKeyImpl@ffff2466
Tải chuỗi chứng chỉ
Nếu một chuỗi chứng chỉ được lưu trữ trong một kho khóa, nó có thể được tải bằng cách gọi KeyStore.getCertificateChain(). Mã bên dưới được sử dụng để trích xuất chuỗi chứng chỉ liên quan cho khóa riêng liên quan.
try{ KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(new FileInputStream("output.p12"), "password".toCharArray()); Key pvtKey = keyStore.getKey("private", "password".toCharArray()); System.out.println(pvtKey.toString()); java.security.cert.Certificate[] chain = keyStore.getCertificateChain("private"); for(java.security.cert.Certificate cert:chain){ System.out.println(cert.toString()); } } catch (Exception ex){ ex.printStackTrace(); }
Kết quả đầu ra là:
[ [ Version: V3 Subject: CN=ROOT Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5 Key: Sun RSA public key, 1024 bits modulus: 107262652552256813768678166856978781385254195794582600239703451044252881438814396239031781495369251659734172714120481593881055888193254336293673302267462500060447786562885955334870856482264000504019061160524587434562257067298291769329550807938162702640388267016365640782567817416484577163775446236245223552189 public exponent: 65537 Validity: [From: Mon Jan 05 13:03:29 SGT 2015, To: Tue Jan 05 13:03:29 SGT 2016] Issuer: CN=ROOT SerialNumber: [ 5e5ca8a4] ] Algorithm: [SHA1withRSA] Signature: 0000: 22 21 BF 73 A6 6D 12 9B F7 49 6C 0E B3 50 6A 9D "!.s.m...Il..Pj. 0010: FA 30 43 22 32 FF 54 95 80 2E B3 8B 6F 59 D4 B5 .0C"2.T.....oY.. 0020: 6C A6 AE 89 B7 18 9A A8 35 7D 65 37 BF ED A3 F4 l.......5.e7.... 0030: E7 DB 5D 5F 9B DA 4B FA 39 04 9B 4D DB C2 3E FA ..]_..K.9..M..>. 0040: 3B C2 63 F8 1E BE 03 F3 BD 1C D4 8A 8E 3C 51 68 ;.c..........
Để hiểu cách tạo chuỗi chứng chỉ trong Java, vui lòng tham khảo Tạo chứng chỉ trong Java -- Chuỗi chứng chỉ.
Tải chứng chỉ
Việc tải chứng chỉ cũng đơn giản bằng cách gọi KeyStore.getCertificate(), nếu bí danh được cung cấp đang ánh xạ đến một chuỗi chứng chỉ, chỉ chứng chỉ lá sẽ được trả về.
try{ KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(new FileInputStream("output.p12"), "password".toCharArray()); java.security.cert.Certificate cert = keyStore.getCertificate("private"); System.out.println(cert); } catch (Exception ex){ ex.printStackTrace(); }
Kết quả đầu ra trông như thế này:
[ [ Version: V3 Subject: CN=ROOT Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5 Key: Sun RSA public key, 1024 bits modulus: 107262652552256813768678166856978781385254195794582600239703451044252881438814396239031781495369251659734172714120481593881055888193254336293673302267462500060447786562885955334870856482264000504019061160524587434562257067298291769329550807938162702640388267016365640782567817416484577163775446236245223552189 public exponent: 65537 Validity: [From: Mon Jan 05 13:03:29 SGT 2015, To: Tue Jan 05 13:03:29 SGT 2016] Issuer: CN=ROOT SerialNumber: [ 5e5ca8a4] ] Algorithm: [SHA1withRSA] Signature: 0000: 22 21 BF 73 A6 6D 12 9B F7 49 6C 0E B3 50 6A 9D "!.s.m...Il..Pj. 0010: FA 30 43 22 32 FF 54 95 80 2E B3 8B 6F 59 D4 B5 .0C"2.T.....oY.. 0020: 6C A6 AE 89 B7 18 9A A8 35 7D 65 37 BF ED A3 F4 l.......5.e7.... 0030: E7 DB 5D 5F 9B DA 4B FA 39 04 9B 4D DB C2 3E FA ..]_..K.9..M..>. 0040: 3B C2 63 F8 1E BE 03 F3 BD 1C D4 8A 8E 3C 51 68 ;.c..........
Nhập và xuất khóa và chứng chỉ
Kho khóa PKCS12 có thể được sử dụng để nhập và xuất khóa và chứng chỉ. Vì khóa riêng có thể được trích xuất từ kho khóa PKCS12, nên các mục có thể được xuất từ kho khóa PKCS12 và sau đó được nhập vào các loại kho khóa khác như JKS.
Đoạn mã bên dưới minh họa việc xuất một mục khóa riêng từ PKCS12 và nhập nó vào kho khóa JSK:
try{ KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(new FileInputStream("output.p12"), "password".toCharArray()); Key pvtKey = keyStore.getKey("private", "password".toCharArray()); java.security.cert.Certificate[] chain = keyStore.getCertificateChain("private"); KeyStore jksStore = KeyStore.getInstance("JKS"); jksStore.load(null, null);; jksStore.setKeyEntry("jksPrivate", pvtKey, "newpassword".toCharArray(), chain); jksStore.store(new FileOutputStream("output.jks"), "password".toCharArray()); } catch (Exception ex){ ex.printStackTrace(); }
Kho khóa PKCS12 là loại kho khóa được khuyến nghị nếu bạn muốn có một loại kho khóa di động và bạn có thể muốn sử dụng nó với các thư viện không phải Java khác trong tương lai.
Để biết thêm thông tin về các kho khóa khác, vui lòng tham khảo Các loại kho khóa khác nhau trong Java -- Tổng quan
How can I choose the extension, p12 or pfx, for a PKCS12 keystore? Is there a mandatory rule?
Another question about the KeyStore.setXXX() method. Why does it use setXXX() method name instead of addXXX() to add new entry? Any special thought?