1 /* 2 * Copyright (c) 2018, 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 8076190 8242151 27 * @library /test/lib 28 * @modules java.base/sun.security.pkcs 29 * java.base/sun.security.util 30 * @summary Customizing the generation of a PKCS12 keystore 31 */ 32 33 import jdk.test.lib.Asserts; 34 import jdk.test.lib.SecurityTools; 35 import jdk.test.lib.process.OutputAnalyzer; 36 37 import java.io.File; 38 import java.io.FileInputStream; 39 import java.io.FileOutputStream; 40 import java.io.IOException; 41 import java.io.InputStream; 42 import java.io.OutputStream; 43 import java.io.UncheckedIOException; 44 import java.nio.file.Files; 45 import java.nio.file.Path; 46 import java.security.KeyStore; 47 import java.util.Base64; 48 import java.util.Objects; 49 50 import static jdk.test.lib.security.DerUtils.*; 51 import sun.security.util.ObjectIdentifier; 52 import sun.security.util.KnownOIDs; 53 import static sun.security.pkcs.ContentInfo.*; 54 55 public class ParamsTest { 56 main(String[] args)57 public static void main(String[] args) throws Throwable { 58 59 // De-BASE64 textual files in ./params to `pwd` 60 Files.newDirectoryStream(Path.of(System.getProperty("test.src"), "params")) 61 .forEach(p -> { 62 try (InputStream is = Files.newInputStream(p); 63 OutputStream os = Files.newOutputStream(p.getFileName())){ 64 Base64.getMimeDecoder().wrap(is).transferTo(os); 65 } catch (IOException e) { 66 throw new UncheckedIOException(e); 67 } 68 }); 69 70 byte[] data; 71 72 // openssl -> keytool interop check 73 74 // os2. no cert pbe, no mac. 75 check("os2", "a", null, "changeit", true, true, true); 76 check("os2", "a", "changeit", "changeit", true, true, true); 77 // You can even load it with a wrong storepass, controversial 78 check("os2", "a", "wrongpass", "changeit", true, true, true); 79 80 // os3. no cert pbe, has mac. just like JKS 81 check("os3", "a", null, "changeit", true, true, true); 82 check("os3", "a", "changeit", "changeit", true, true, true); 83 // Cannot load with a wrong storepass, same as JKS 84 check("os3", "a", "wrongpass", "-", IOException.class, "-", "-"); 85 86 // os4. non default algs 87 check("os4", "a", "changeit", "changeit", true, true, true); 88 check("os4", "a", "wrongpass", "-", IOException.class, "-", "-"); 89 // no storepass no cert 90 check("os4", "a", null, "changeit", true, false, true); 91 92 // os5. strong non default algs 93 check("os5", "a", "changeit", "changeit", true, true, true); 94 check("os5", "a", "wrongpass", "-", IOException.class, "-", "-"); 95 // no storepass no cert 96 check("os5", "a", null, "changeit", true, false, true); 97 98 // keytool 99 100 // Current default pkcs12 setting 101 keytool("-importkeystore -srckeystore ks -srcstorepass changeit " 102 + "-destkeystore ksnormal -deststorepass changeit"); 103 data = Files.readAllBytes(Path.of("ksnormal")); 104 checkInt(data, "22", 100000); // Mac ic 105 checkAlg(data, "2000", oid(KnownOIDs.SHA_1)); // Mac alg 106 checkAlg(data, "110c010c01000", oid(KnownOIDs.PBEWithSHA1AndDESede)); // key alg 107 checkInt(data, "110c010c010011", 50000); // key ic 108 checkAlg(data, "110c10", ENCRYPTED_DATA_OID); 109 checkAlg(data, "110c110110", oid(KnownOIDs.PBEWithSHA1AndRC2_40)); // cert alg 110 checkInt(data, "110c1101111", 50000); // cert ic 111 112 check("ksnormal", "a", "changeit", "changeit", true, true, true); 113 check("ksnormal", "a", null, "changeit", true, false, true); 114 check("ksnormal", "a", "wrongpass", "-", IOException.class, "-", "-"); 115 116 // Add a new entry with password-less settings, still has a storepass 117 keytool("-keystore ksnormal -genkeypair -keyalg DSA " 118 + "-storepass changeit -alias b -dname CN=b " 119 + "-J-Dkeystore.pkcs12.certProtectionAlgorithm=NONE " 120 + "-J-Dkeystore.pkcs12.macAlgorithm=NONE"); 121 data = Files.readAllBytes(Path.of("ksnormal")); 122 checkInt(data, "22", 100000); // Mac ic 123 checkAlg(data, "2000", oid(KnownOIDs.SHA_1)); // Mac alg 124 checkAlg(data, "110c010c01000", oid(KnownOIDs.PBEWithSHA1AndDESede)); // key alg 125 checkInt(data, "110c010c010011", 50000); // key ic 126 checkAlg(data, "110c010c11000", oid(KnownOIDs.PBEWithSHA1AndDESede)); // new key alg 127 checkInt(data, "110c010c110011", 50000); // new key ic 128 checkAlg(data, "110c10", ENCRYPTED_DATA_OID); 129 checkAlg(data, "110c110110", oid(KnownOIDs.PBEWithSHA1AndRC2_40)); // cert alg 130 checkInt(data, "110c1101111", 50000); // cert ic 131 check("ksnormal", "b", null, "changeit", true, false, true); 132 check("ksnormal", "b", "changeit", "changeit", true, true, true); 133 134 // Different keypbe alg, no cert pbe and no mac 135 keytool("-importkeystore -srckeystore ks -srcstorepass changeit " 136 + "-destkeystore ksnopass -deststorepass changeit " 137 + "-J-Dkeystore.pkcs12.keyProtectionAlgorithm=PBEWithSHA1AndRC4_128 " 138 + "-J-Dkeystore.pkcs12.certProtectionAlgorithm=NONE " 139 + "-J-Dkeystore.pkcs12.macAlgorithm=NONE"); 140 data = Files.readAllBytes(Path.of("ksnopass")); 141 shouldNotExist(data, "2"); // no Mac 142 checkAlg(data, "110c010c01000", oid(KnownOIDs.PBEWithSHA1AndRC4_128)); 143 checkInt(data, "110c010c010011", 50000); 144 checkAlg(data, "110c10", DATA_OID); 145 check("ksnopass", "a", null, "changeit", true, true, true); 146 check("ksnopass", "a", "changeit", "changeit", true, true, true); 147 check("ksnopass", "a", "wrongpass", "changeit", true, true, true); 148 149 // Add a new entry with normal settings, still password-less 150 keytool("-keystore ksnopass -genkeypair -keyalg DSA " 151 + "-storepass changeit -alias b -dname CN=B"); 152 data = Files.readAllBytes(Path.of("ksnopass")); 153 shouldNotExist(data, "2"); // no Mac 154 checkAlg(data, "110c010c01000", oid(KnownOIDs.PBEWithSHA1AndRC4_128)); 155 checkInt(data, "110c010c010011", 50000); 156 checkAlg(data, "110c010c11000", oid(KnownOIDs.PBEWithSHA1AndDESede)); 157 checkInt(data, "110c010c110011", 50000); 158 checkAlg(data, "110c10", DATA_OID); 159 check("ksnopass", "a", null, "changeit", true, true, true); 160 check("ksnopass", "b", null, "changeit", true, true, true); 161 162 keytool("-importkeystore -srckeystore ks -srcstorepass changeit " 163 + "-destkeystore ksnewic -deststorepass changeit " 164 + "-J-Dkeystore.pkcs12.macIterationCount=5555 " 165 + "-J-Dkeystore.pkcs12.certPbeIterationCount=6666 " 166 + "-J-Dkeystore.pkcs12.keyPbeIterationCount=7777"); 167 data = Files.readAllBytes(Path.of("ksnewic")); 168 checkInt(data, "22", 5555); // Mac ic 169 checkAlg(data, "2000", oid(KnownOIDs.SHA_1)); // Mac alg 170 checkAlg(data, "110c010c01000", oid(KnownOIDs.PBEWithSHA1AndDESede)); // key alg 171 checkInt(data, "110c010c010011", 7777); // key ic 172 checkAlg(data, "110c110110", oid(KnownOIDs.PBEWithSHA1AndRC2_40)); // cert alg 173 checkInt(data, "110c1101111", 6666); // cert ic 174 175 // keypbe alg cannot be NONE 176 keytool("-keystore ksnewic -genkeypair -keyalg DSA " 177 + "-storepass changeit -alias b -dname CN=B " 178 + "-J-Dkeystore.pkcs12.keyProtectionAlgorithm=NONE") 179 .shouldContain("NONE AlgorithmParameters not available") 180 .shouldHaveExitValue(1); 181 182 // new entry new keypbe alg (and default ic), else unchanged 183 keytool("-keystore ksnewic -genkeypair -keyalg DSA " 184 + "-storepass changeit -alias b -dname CN=B " 185 + "-J-Dkeystore.pkcs12.keyProtectionAlgorithm=PBEWithSHA1AndRC4_128"); 186 data = Files.readAllBytes(Path.of("ksnewic")); 187 checkInt(data, "22", 5555); // Mac ic 188 checkAlg(data, "2000", oid(KnownOIDs.SHA_1)); // Mac alg 189 checkAlg(data, "110c010c01000", oid(KnownOIDs.PBEWithSHA1AndDESede)); // key alg 190 checkInt(data, "110c010c010011", 7777); // key ic 191 checkAlg(data, "110c010c11000", oid(KnownOIDs.PBEWithSHA1AndRC4_128)); // new key alg 192 checkInt(data, "110c010c110011", 50000); // new key ic 193 checkAlg(data, "110c110110", oid(KnownOIDs.PBEWithSHA1AndRC2_40)); // cert alg 194 checkInt(data, "110c1101111", 6666); // cert ic 195 196 // Check KeyStore loading multiple keystores 197 KeyStore ks = KeyStore.getInstance("pkcs12"); 198 try (FileInputStream fis = new FileInputStream("ksnormal"); 199 FileOutputStream fos = new FileOutputStream("ksnormaldup")) { 200 ks.load(fis, "changeit".toCharArray()); 201 ks.store(fos, "changeit".toCharArray()); 202 } 203 data = Files.readAllBytes(Path.of("ksnormaldup")); 204 checkInt(data, "22", 100000); // Mac ic 205 checkAlg(data, "2000", oid(KnownOIDs.SHA_1)); // Mac alg 206 checkAlg(data, "110c010c01000", oid(KnownOIDs.PBEWithSHA1AndDESede)); // key alg 207 checkInt(data, "110c010c010011", 50000); // key ic 208 checkAlg(data, "110c010c11000", oid(KnownOIDs.PBEWithSHA1AndDESede)); // new key alg 209 checkInt(data, "110c010c110011", 50000); // new key ic 210 checkAlg(data, "110c10", ENCRYPTED_DATA_OID); 211 checkAlg(data, "110c110110", oid(KnownOIDs.PBEWithSHA1AndRC2_40)); // cert alg 212 checkInt(data, "110c1101111", 50000); // cert ic 213 214 try (FileInputStream fis = new FileInputStream("ksnopass"); 215 FileOutputStream fos = new FileOutputStream("ksnopassdup")) { 216 ks.load(fis, "changeit".toCharArray()); 217 ks.store(fos, "changeit".toCharArray()); 218 } 219 data = Files.readAllBytes(Path.of("ksnopassdup")); 220 shouldNotExist(data, "2"); // no Mac 221 checkAlg(data, "110c010c01000", oid(KnownOIDs.PBEWithSHA1AndRC4_128)); 222 checkInt(data, "110c010c010011", 50000); 223 checkAlg(data, "110c010c11000", oid(KnownOIDs.PBEWithSHA1AndDESede)); 224 checkInt(data, "110c010c110011", 50000); 225 checkAlg(data, "110c10", DATA_OID); 226 227 try (FileInputStream fis = new FileInputStream("ksnewic"); 228 FileOutputStream fos = new FileOutputStream("ksnewicdup")) { 229 ks.load(fis, "changeit".toCharArray()); 230 ks.store(fos, "changeit".toCharArray()); 231 } 232 data = Files.readAllBytes(Path.of("ksnewicdup")); 233 checkInt(data, "22", 5555); // Mac ic 234 checkAlg(data, "2000", oid(KnownOIDs.SHA_1)); // Mac alg 235 checkAlg(data, "110c010c01000", oid(KnownOIDs.PBEWithSHA1AndDESede)); // key alg 236 checkInt(data, "110c010c010011", 7777); // key ic 237 checkAlg(data, "110c010c11000", oid(KnownOIDs.PBEWithSHA1AndRC4_128)); // new key alg 238 checkInt(data, "110c010c110011", 50000); // new key ic 239 checkAlg(data, "110c110110", oid(KnownOIDs.PBEWithSHA1AndRC2_40)); // cert alg 240 checkInt(data, "110c1101111", 6666); // cert ic 241 242 // Check keytool behavior 243 244 // ksnormal has password 245 246 keytool("-list -keystore ksnormal") 247 .shouldContain("WARNING WARNING WARNING") 248 .shouldContain("Certificate chain length: 0"); 249 250 SecurityTools.setResponse("changeit"); 251 keytool("-list -keystore ksnormal") 252 .shouldNotContain("WARNING WARNING WARNING") 253 .shouldContain("Certificate fingerprint"); 254 255 // ksnopass is password-less 256 257 keytool("-list -keystore ksnopass") 258 .shouldNotContain("WARNING WARNING WARNING") 259 .shouldContain("Certificate fingerprint"); 260 261 // -certreq prompts for keypass 262 SecurityTools.setResponse("changeit"); 263 keytool("-certreq -alias a -keystore ksnopass") 264 .shouldContain("Enter key password for <a>") 265 .shouldContain("-----BEGIN NEW CERTIFICATE REQUEST-----") 266 .shouldHaveExitValue(0); 267 268 // -certreq -storepass works fine 269 keytool("-certreq -alias a -keystore ksnopass -storepass changeit") 270 .shouldNotContain("Enter key password for <a>") 271 .shouldContain("-----BEGIN NEW CERTIFICATE REQUEST-----") 272 .shouldHaveExitValue(0); 273 274 // -certreq -keypass also works fine 275 keytool("-certreq -alias a -keystore ksnopass -keypass changeit") 276 .shouldNotContain("Enter key password for <a>") 277 .shouldContain("-----BEGIN NEW CERTIFICATE REQUEST-----") 278 .shouldHaveExitValue(0); 279 280 // -importkeystore prompts for srckeypass 281 SecurityTools.setResponse("changeit", "changeit"); 282 keytool("-importkeystore -srckeystore ksnopass " 283 + "-destkeystore jks3 -deststorepass changeit") 284 .shouldContain("Enter key password for <a>") 285 .shouldContain("Enter key password for <b>") 286 .shouldContain("2 entries successfully imported"); 287 288 // ksnopass2 is ksnopass + 2 cert entries 289 290 ks = KeyStore.getInstance(new File("ksnopass"), (char[])null); 291 ks.setCertificateEntry("aa", ks.getCertificate("a")); 292 ks.setCertificateEntry("bb", ks.getCertificate("b")); 293 try (FileOutputStream fos = new FileOutputStream("ksnopass2")) { 294 ks.store(fos, null); 295 } 296 297 // -importkeystore prompts for srckeypass for private keys 298 // and no prompt for certs 299 SecurityTools.setResponse("changeit", "changeit"); 300 keytool("-importkeystore -srckeystore ksnopass2 " 301 + "-destkeystore jks5 -deststorepass changeit") 302 .shouldContain("Enter key password for <a>") 303 .shouldContain("Enter key password for <b>") 304 .shouldNotContain("Enter key password for <aa>") 305 .shouldNotContain("Enter key password for <bb>") 306 .shouldContain("4 entries successfully imported"); 307 308 // ksonlycert has only cert entries 309 310 ks.deleteEntry("a"); 311 ks.deleteEntry("b"); 312 try (FileOutputStream fos = new FileOutputStream("ksonlycert")) { 313 ks.store(fos, null); 314 } 315 316 // -importkeystore does not prompt at all 317 keytool("-importkeystore -srckeystore ksonlycert " 318 + "-destkeystore jks6 -deststorepass changeit") 319 .shouldNotContain("Enter key password for <aa>") 320 .shouldNotContain("Enter key password for <bb>") 321 .shouldContain("2 entries successfully imported"); 322 323 // create a new password-less keystore 324 keytool("-keystore ksnopass -exportcert -alias a -file a.cert -rfc"); 325 326 // Normally storepass is prompted for 327 keytool("-keystore kscert1 -importcert -alias a -file a.cert -noprompt") 328 .shouldContain("Enter keystore password:"); 329 keytool("-keystore kscert2 -importcert -alias a -file a.cert -noprompt " 330 + "-J-Dkeystore.pkcs12.certProtectionAlgorithm=NONE") 331 .shouldContain("Enter keystore password:"); 332 keytool("-keystore kscert3 -importcert -alias a -file a.cert -noprompt " 333 + "-J-Dkeystore.pkcs12.macAlgorithm=NONE") 334 .shouldContain("Enter keystore password:"); 335 // ... but not if it's password-less 336 keytool("-keystore kscert4 -importcert -alias a -file a.cert -noprompt " 337 + "-J-Dkeystore.pkcs12.certProtectionAlgorithm=NONE " 338 + "-J-Dkeystore.pkcs12.macAlgorithm=NONE") 339 .shouldNotContain("Enter keystore password:"); 340 341 // still prompt for keypass for genkeypair and certreq 342 SecurityTools.setResponse("changeit", "changeit"); 343 keytool("-keystore ksnopassnew -genkeypair -keyalg DSA " 344 + "-alias a -dname CN=A " 345 + "-J-Dkeystore.pkcs12.certProtectionAlgorithm=NONE " 346 + "-J-Dkeystore.pkcs12.macAlgorithm=NONE") 347 .shouldNotContain("Enter keystore password:") 348 .shouldContain("Enter key password for <a>"); 349 keytool("-keystore ksnopassnew -certreq -alias a") 350 .shouldNotContain("Enter keystore password:") 351 .shouldContain("Enter key password for <a>"); 352 keytool("-keystore ksnopassnew -list -v -alias a") 353 .shouldNotContain("Enter keystore password:") 354 .shouldNotContain("Enter key password for <a>"); 355 356 // params only read on demand 357 358 // keyPbeIterationCount is used by -genkeypair 359 keytool("-keystore ksgenbadkeyic -genkeypair -keyalg DSA " 360 + "-alias a -dname CN=A " 361 + "-storepass changeit " 362 + "-J-Dkeystore.pkcs12.keyPbeIterationCount=abc") 363 .shouldContain("keyPbeIterationCount is not a number: abc") 364 .shouldHaveExitValue(1); 365 366 keytool("-keystore ksnopassnew -exportcert -alias a -file a.cert"); 367 368 // but not used by -importcert 369 keytool("-keystore ksimpbadkeyic -importcert -alias a -file a.cert " 370 + "-noprompt -storepass changeit " 371 + "-J-Dkeystore.pkcs12.keyPbeIterationCount=abc") 372 .shouldHaveExitValue(0); 373 374 // None is used by -list 375 keytool("-keystore ksnormal -storepass changeit -list " 376 + "-J-Dkeystore.pkcs12.keyPbeIterationCount=abc " 377 + "-J-Dkeystore.pkcs12.certPbeIterationCount=abc " 378 + "-J-Dkeystore.pkcs12.macIterationCount=abc") 379 .shouldHaveExitValue(0); 380 } 381 382 /** 383 * Check keystore loading and key/cert reading. 384 * 385 * @param keystore the file name of keystore 386 * @param alias the key/cert to read 387 * @param storePass store pass to try out, can be null 388 * @param keypass key pass to try, can not be null 389 * @param expectedLoad expected result of keystore loading, true if non 390 * null, false if null, exception class if exception 391 * @param expectedCert expected result of cert reading 392 * @param expectedKey expected result of key reading 393 */ check( String keystore, String alias, String storePass, String keypass, Object expectedLoad, Object expectedCert, Object expectedKey)394 private static void check( 395 String keystore, 396 String alias, 397 String storePass, 398 String keypass, 399 Object expectedLoad, 400 Object expectedCert, 401 Object expectedKey) { 402 KeyStore ks = null; 403 Object actualLoad, actualCert, actualKey; 404 String label = keystore + "-" + alias + "-" + storePass + "-" + keypass; 405 try { 406 ks = KeyStore.getInstance(new File(keystore), 407 storePass == null ? null : storePass.toCharArray()); 408 actualLoad = ks != null; 409 } catch (Exception e) { 410 e.printStackTrace(System.out); 411 actualLoad = e.getClass(); 412 } 413 Asserts.assertEQ(expectedLoad, actualLoad, label + "-load"); 414 415 // If not loaded correctly, skip cert/key reading 416 if (!Objects.equals(actualLoad, true)) { 417 return; 418 } 419 420 try { 421 actualCert = (ks.getCertificate(alias) != null); 422 } catch (Exception e) { 423 e.printStackTrace(System.out); 424 actualCert = e.getClass(); 425 } 426 Asserts.assertEQ(expectedCert, actualCert, label + "-cert"); 427 428 try { 429 actualKey = (ks.getKey(alias, keypass.toCharArray()) != null); 430 } catch (Exception e) { 431 e.printStackTrace(System.out); 432 actualKey = e.getClass(); 433 } 434 Asserts.assertEQ(expectedKey, actualKey, label + "-key"); 435 } 436 oid(KnownOIDs o)437 private static ObjectIdentifier oid(KnownOIDs o) { 438 return ObjectIdentifier.of(o); 439 } 440 keytool(String s)441 static OutputAnalyzer keytool(String s) throws Throwable { 442 return SecurityTools.keytool(s); 443 } 444 } 445