Skip to content

Commit 7049c13

Browse files
committed
8231107: Allow store password to be null when saving a PKCS12 KeyStore
Reviewed-by: mullan
1 parent ab867f6 commit 7049c13

File tree

4 files changed

+54
-33
lines changed

4 files changed

+54
-33
lines changed

src/java.base/share/classes/java/security/KeyStore.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1384,7 +1384,9 @@ public final String getCertificateAlias(Certificate cert)
13841384
* integrity with the given password.
13851385
*
13861386
* @param stream the output stream to which this keystore is written.
1387-
* @param password the password to generate the keystore integrity check
1387+
* @param password the password to generate the keystore integrity check.
1388+
* May be {@code null} if the keystore does not support
1389+
* or require an integrity check.
13881390
*
13891391
* @throws KeyStoreException if the keystore has not been initialized
13901392
* (loaded).

src/java.base/share/classes/java/security/KeyStoreSpi.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,9 @@ public abstract void engineDeleteEntry(String alias)
289289
* integrity with the given password.
290290
*
291291
* @param stream the output stream to which this keystore is written.
292-
* @param password the password to generate the keystore integrity check
292+
* @param password the password to generate the keystore integrity check.
293+
* May be {@code null} if the keystore does not support
294+
* or require an integrity check.
293295
*
294296
* @throws IOException if there was an I/O problem with data
295297
* @throws NoSuchAlgorithmException if the appropriate data integrity

src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,14 +1259,20 @@ public synchronized void engineStore(OutputStream stream, char[] password)
12591259
" certificate(s) in a PKCS#7 encryptedData");
12601260
}
12611261

1262-
byte[] encrData = createEncryptedData(password);
1263-
if (!certProtectionAlgorithm.equalsIgnoreCase("NONE")) {
1262+
byte[] certsData = getCertificateData();
1263+
if (password != null && !certProtectionAlgorithm.equalsIgnoreCase("NONE")) {
1264+
// -- SEQUENCE of EncryptedData
1265+
DerOutputStream encrData = new DerOutputStream();
1266+
encrData.putInteger(0);
1267+
encrData.write(encryptContent(certsData, password));
1268+
DerOutputStream encrDataContent = new DerOutputStream();
1269+
encrDataContent.write(DerValue.tag_Sequence, encrData);
12641270
ContentInfo encrContentInfo =
12651271
new ContentInfo(ContentInfo.ENCRYPTED_DATA_OID,
1266-
new DerValue(encrData));
1272+
new DerValue(encrDataContent.toByteArray()));
12671273
encrContentInfo.encode(authSafeContentInfo);
12681274
} else {
1269-
ContentInfo dataContentInfo = new ContentInfo(encrData);
1275+
ContentInfo dataContentInfo = new ContentInfo(certsData);
12701276
dataContentInfo.encode(authSafeContentInfo);
12711277
}
12721278
}
@@ -1289,7 +1295,7 @@ public synchronized void engineStore(OutputStream stream, char[] password)
12891295
if (macIterationCount < 0) {
12901296
macIterationCount = defaultMacIterationCount();
12911297
}
1292-
if (!macAlgorithm.equalsIgnoreCase("NONE")) {
1298+
if (password != null && !macAlgorithm.equalsIgnoreCase("NONE")) {
12931299
byte[] macData = calculateMac(password, authenticatedSafe);
12941300
pfx.write(macData);
12951301
}
@@ -1704,12 +1710,11 @@ private byte[] getBagAttributes(String alias, byte[] keyId,
17041710
}
17051711

17061712
/*
1707-
* Create EncryptedData content type, that contains EncryptedContentInfo.
1708-
* Includes certificates in individual SafeBags of type CertBag.
1709-
* Each CertBag may include pkcs12 attributes
1713+
* Create Data content type, includes certificates in individual
1714+
* SafeBags of type CertBag. Each CertBag may include pkcs12 attributes
17101715
* (see comments in getBagAttributes)
17111716
*/
1712-
private byte[] createEncryptedData(char[] password)
1717+
private byte[] getCertificateData()
17131718
throws CertificateException, IOException
17141719
{
17151720
DerOutputStream out = new DerOutputStream();
@@ -1803,22 +1808,7 @@ private byte[] createEncryptedData(char[] password)
18031808
// wrap as SequenceOf SafeBag
18041809
DerOutputStream safeBagValue = new DerOutputStream();
18051810
safeBagValue.write(DerValue.tag_SequenceOf, out);
1806-
byte[] safeBagData = safeBagValue.toByteArray();
1807-
1808-
// encrypt the content (EncryptedContentInfo)
1809-
if (!certProtectionAlgorithm.equalsIgnoreCase("NONE")) {
1810-
byte[] encrContentInfo = encryptContent(safeBagData, password);
1811-
1812-
// -- SEQUENCE of EncryptedData
1813-
DerOutputStream encrData = new DerOutputStream();
1814-
DerOutputStream encrDataContent = new DerOutputStream();
1815-
encrData.putInteger(0);
1816-
encrData.write(encrContentInfo);
1817-
encrDataContent.write(DerValue.tag_Sequence, encrData);
1818-
return encrDataContent.toByteArray();
1819-
} else {
1820-
return safeBagData;
1821-
}
1811+
return safeBagValue.toByteArray();
18221812
}
18231813

18241814
/*

test/jdk/sun/security/pkcs12/EmptyPassword.java

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -23,11 +23,11 @@
2323

2424
/*
2525
* @test
26-
* @bug 8202299
26+
* @bug 8202299 8231107
2727
* @modules java.base/sun.security.tools.keytool
2828
* java.base/sun.security.x509
2929
* @library /test/lib
30-
* @summary Java Keystore fails to load PKCS12/PFX certificates created in WindowsServer2016
30+
* @summary Testing empty (any of null, "", "\0") password behaviors
3131
*/
3232

3333
import jdk.test.lib.Asserts;
@@ -38,6 +38,7 @@
3838
import java.io.FileOutputStream;
3939
import java.security.KeyStore;
4040
import java.security.cert.Certificate;
41+
import java.util.Arrays;
4142

4243
public class EmptyPassword {
4344

@@ -52,13 +53,39 @@ public static void main(String[] args) throws Exception {
5253
new Certificate[] {
5354
gen.getSelfCertificate(new X500Name("CN=Me"), 100)
5455
});
55-
try (FileOutputStream fos = new FileOutputStream("p12")) {
56-
ks.store(fos, new char[1]);
57-
}
56+
57+
// 8202299: interop between new char[0] and new char[1]
58+
store(ks, "p12", new char[1]);
5859

5960
// It can be loaded with password "".
6061
ks = KeyStore.getInstance(new File("p12"), new char[0]);
6162
Asserts.assertTrue(ks.getKey("a", new char[0]) != null);
6263
Asserts.assertTrue(ks.getCertificate("a") != null);
64+
65+
ks = KeyStore.getInstance(new File("p12"), new char[1]);
66+
Asserts.assertTrue(ks.getKey("a", new char[1]) != null);
67+
Asserts.assertTrue(ks.getCertificate("a") != null);
68+
69+
// 8231107: Store with null password makes it password-less
70+
store(ks, "p00", null);
71+
72+
// Can read cert and key with any password
73+
for (char[] pass: new char[][] {
74+
new char[0], // password actually used before 8202299
75+
new char[1], // the interoperability before 8202299
76+
null, // password-less after 8202299
77+
"whatever".toCharArray()
78+
}) {
79+
System.out.println("with password " + Arrays.toString(pass));
80+
ks = KeyStore.getInstance(new File("p00"), pass);
81+
Asserts.assertTrue(ks.getKey("a", new char[1]) != null);
82+
Asserts.assertTrue(ks.getCertificate("a") != null);
83+
}
84+
}
85+
86+
static void store(KeyStore ks, String file, char[] pass) throws Exception {
87+
try (FileOutputStream fos = new FileOutputStream(file)) {
88+
ks.store(fos, pass);
89+
}
6390
}
6491
}

0 commit comments

Comments
 (0)