1 /*
2  * Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /*
25  * @test
26  * @bug 6958026 8242151
27  * @summary Problem with PKCS12 keystore
28  * @modules java.base/sun.security.pkcs
29  *          java.base/sun.security.tools.keytool
30  *          java.base/sun.security.util
31  *          java.base/sun.security.x509
32  * @compile -XDignore.symbol.file PKCS12SameKeyId.java
33  * @run main PKCS12SameKeyId
34  */
35 
36 import java.io.File;
37 import java.io.FileInputStream;
38 import java.io.FileOutputStream;
39 import java.security.AlgorithmParameters;
40 import java.security.KeyStore;
41 import java.security.cert.Certificate;
42 import java.security.cert.X509Certificate;
43 import javax.crypto.Cipher;
44 import javax.crypto.SecretKey;
45 import javax.crypto.SecretKeyFactory;
46 import javax.crypto.spec.PBEKeySpec;
47 import javax.crypto.spec.PBEParameterSpec;
48 import sun.security.pkcs.EncryptedPrivateKeyInfo;
49 import sun.security.util.ObjectIdentifier;
50 import sun.security.x509.AlgorithmId;
51 import sun.security.x509.X500Name;
52 
53 public class PKCS12SameKeyId {
54 
55     private static final String JKSFILE = "PKCS12SameKeyId.jks";
56     private static final String P12FILE = "PKCS12SameKeyId.p12";
57     private static final char[] PASSWORD = "changeit".toCharArray();
58     private static final int SIZE = 10;
59 
main(String[] args)60     public static void main(String[] args) throws Exception {
61 
62         // Prepare a JKS keystore with many entries
63         new File(JKSFILE).delete();
64         for (int i=0; i<SIZE; i++) {
65             System.err.print(".");
66             String cmd = "-keystore " + JKSFILE
67                     + " -storepass changeit -keypass changeit -keyalg rsa "
68                     + "-genkeypair -alias p" + i + " -dname CN=" + i;
69             sun.security.tools.keytool.Main.main(cmd.split(" "));
70         }
71 
72         // Prepare EncryptedPrivateKeyInfo parameters, copied from various
73         // places in PKCS12KeyStore.java
74         AlgorithmParameters algParams =
75                 AlgorithmParameters.getInstance("PBEWithSHA1AndDESede");
76         algParams.init(new PBEParameterSpec("12345678".getBytes(), 1024));
77         AlgorithmId algid = new AlgorithmId(
78                 ObjectIdentifier.of("1.2.840.113549.1.12.1.3"), algParams);
79 
80         PBEKeySpec keySpec = new PBEKeySpec(PASSWORD);
81         SecretKeyFactory skFac = SecretKeyFactory.getInstance("PBE");
82         SecretKey skey = skFac.generateSecret(keySpec);
83 
84         Cipher cipher = Cipher.getInstance("PBEWithSHA1AndDESede");
85         cipher.init(Cipher.ENCRYPT_MODE, skey, algParams);
86 
87         // Pre-calculated keys and certs and aliases
88         byte[][] keys = new byte[SIZE][];
89         Certificate[][] certChains = new Certificate[SIZE][];
90         String[] aliases = new String[SIZE];
91 
92         // Reads from JKS keystore and pre-calculate
93         KeyStore ks = KeyStore.getInstance("jks");
94         try (FileInputStream fis = new FileInputStream(JKSFILE)) {
95             ks.load(fis, PASSWORD);
96         }
97         for (int i=0; i<SIZE; i++) {
98             aliases[i] = "p" + i;
99             byte[] enckey = cipher.doFinal(
100                     ks.getKey(aliases[i], PASSWORD).getEncoded());
101             keys[i] = new EncryptedPrivateKeyInfo(algid, enckey).getEncoded();
102             certChains[i] = ks.getCertificateChain(aliases[i]);
103         }
104 
105         // Write into PKCS12 keystore. Use this overloaded version of
106         // setKeyEntry() to be as fast as possible, so that they would
107         // have same localKeyId.
108         KeyStore p12 = KeyStore.getInstance("pkcs12");
109         p12.load(null, PASSWORD);
110         for (int i=0; i<SIZE; i++) {
111             p12.setKeyEntry(aliases[i], keys[i], certChains[i]);
112         }
113         try (FileOutputStream fos = new FileOutputStream(P12FILE)) {
114             p12.store(fos, PASSWORD);
115         }
116 
117         // Check private keys still match certs
118         p12 = KeyStore.getInstance("pkcs12");
119         try (FileInputStream fis = new FileInputStream(P12FILE)) {
120             p12.load(fis, PASSWORD);
121         }
122         for (int i=0; i<SIZE; i++) {
123             String a = "p" + i;
124             X509Certificate x = (X509Certificate)p12.getCertificate(a);
125             X500Name name = (X500Name)x.getSubjectDN();
126             if (!name.getCommonName().equals(""+i)) {
127                 throw new Exception(a + "'s cert is " + name);
128             }
129         }
130     }
131 }
132