Skip to content

Commit d2e2c4c

Browse files
committedOct 2, 2023
8309667: TLS handshake fails because of ConcurrentModificationException in PKCS12KeyStore.engineGetEntry
Reviewed-by: djelinski, mullan
1 parent e25121d commit d2e2c4c

File tree

3 files changed

+257
-9
lines changed

3 files changed

+257
-9
lines changed
 

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

+12-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1999, 2023, 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
@@ -705,6 +705,7 @@ private void setKeyEntry(String alias, Key key,
705705
// set the alias
706706
entry.alias = alias.toLowerCase(Locale.ENGLISH);
707707
// add the entry
708+
populateAttributes(entry);
708709
entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
709710

710711
} catch (KeyStoreException kse) {
@@ -785,6 +786,7 @@ public synchronized void engineSetKeyEntry(String alias, byte[] key,
785786

786787
// add the entry
787788
privateKeyCount++;
789+
populateAttributes(entry);
788790
entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
789791
}
790792

@@ -988,6 +990,7 @@ private void setCertEntry(String alias, Certificate cert,
988990
new CertEntry((X509Certificate) cert, null, alias, AnyUsage,
989991
attributes);
990992
certificateCount++;
993+
populateAttributes(certEntry);
991994
entries.put(alias.toLowerCase(Locale.ENGLISH), certEntry);
992995

993996
if (debug != null) {
@@ -1264,7 +1267,7 @@ public Set<KeyStore.Entry.Attribute> engineGetAttributes(String alias) {
12641267
return super.engineGetAttributes(alias);
12651268
}
12661269
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
1267-
return Collections.unmodifiableSet(new HashSet<>(getAttributes(entry)));
1270+
return Collections.unmodifiableSet(new HashSet<>(entry.attributes));
12681271
}
12691272

12701273
/**
@@ -1313,7 +1316,7 @@ public KeyStore.Entry engineGetEntry(String alias,
13131316
}
13141317

13151318
return new KeyStore.TrustedCertificateEntry(
1316-
((CertEntry)entry).cert, getAttributes(entry));
1319+
((CertEntry)entry).cert, entry.attributes);
13171320
}
13181321
} else {
13191322
throw new UnrecoverableKeyException
@@ -1335,12 +1338,12 @@ public KeyStore.Entry engineGetEntry(String alias,
13351338
Certificate[] chain = engineGetCertificateChain(alias);
13361339

13371340
return new KeyStore.PrivateKeyEntry((PrivateKey)key, chain,
1338-
getAttributes(entry));
1341+
entry.attributes);
13391342

13401343
} else if (key instanceof SecretKey) {
13411344

13421345
return new KeyStore.SecretKeyEntry((SecretKey)key,
1343-
getAttributes(entry));
1346+
entry.attributes);
13441347
}
13451348
} else if (!engineIsKeyEntry(alias)) {
13461349
throw new UnsupportedOperationException
@@ -1429,9 +1432,9 @@ public synchronized void engineSetEntry(String alias, KeyStore.Entry entry,
14291432
}
14301433

14311434
/*
1432-
* Assemble the entry attributes
1435+
* Populate the entry with additional attributes used by the implementation.
14331436
*/
1434-
private Set<KeyStore.Entry.Attribute> getAttributes(Entry entry) {
1437+
private void populateAttributes(Entry entry) {
14351438

14361439
if (entry.attributes == null) {
14371440
entry.attributes = new HashSet<>();
@@ -1464,8 +1467,6 @@ private Set<KeyStore.Entry.Attribute> getAttributes(Entry entry) {
14641467
}
14651468
}
14661469
}
1467-
1468-
return entry.attributes;
14691470
}
14701471

14711472
/*
@@ -2522,6 +2523,7 @@ private void loadSafeContents(DerInputStream stream)
25222523
alias = getUnfriendlyName();
25232524
}
25242525
entry.alias = alias;
2526+
populateAttributes(entry);
25252527
entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
25262528

25272529
} else if (bagItem instanceof X509Certificate cert) {
@@ -2543,6 +2545,7 @@ private void loadSafeContents(DerInputStream stream)
25432545
CertEntry certEntry =
25442546
new CertEntry(cert, keyId, alias, trustedKeyUsage,
25452547
attributes);
2548+
populateAttributes(certEntry);
25462549
entries.put(alias.toLowerCase(Locale.ENGLISH), certEntry);
25472550
} else {
25482551
certEntries.add(new CertEntry(cert, keyId, alias));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* Copyright (c) 2023, 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 8309667
27+
* @library /test/lib
28+
* @modules java.base/sun.security.tools.keytool
29+
* java.base/sun.security.x509
30+
* @summary ensures attributes reading is correct
31+
*/
32+
import jdk.test.lib.Asserts;
33+
import sun.security.tools.keytool.CertAndKeyGen;
34+
import sun.security.x509.X500Name;
35+
36+
import javax.crypto.EncryptedPrivateKeyInfo;
37+
import javax.crypto.spec.SecretKeySpec;
38+
import java.io.ByteArrayInputStream;
39+
import java.io.ByteArrayOutputStream;
40+
import java.security.KeyStore;
41+
import java.security.PKCS12Attribute;
42+
import java.security.cert.Certificate;
43+
import java.security.cert.X509Certificate;
44+
import java.util.Set;
45+
46+
public class AttributesCorrectness {
47+
48+
static final char[] PASSWORD = "changeit".toCharArray();
49+
static final String LOCAL_KEY_ID = "1.2.840.113549.1.9.21";
50+
static final String TRUSTED_KEY_USAGE = "2.16.840.1.113894.746875.1.1";
51+
static final String FRIENDLY_NAME = "1.2.840.113549.1.9.20";
52+
53+
static CertAndKeyGen cag;
54+
static KeyStore ks;
55+
56+
public static void main(String[] args) throws Exception {
57+
58+
ks = KeyStore.getInstance("pkcs12");
59+
ks.load(null, null);
60+
cag = new CertAndKeyGen("Ed25519", "Ed25519");
61+
62+
cag.generate(-1);
63+
ks.setCertificateEntry("c", ss("c"));
64+
65+
cag.generate(-1);
66+
ks.setKeyEntry("d", cag.getPrivateKey(), PASSWORD, chain("d"));
67+
68+
cag.generate(-1);
69+
ks.setKeyEntry("e", new EncryptedPrivateKeyInfo(
70+
"PBEWithMD5AndDES", new byte[100]).getEncoded(), chain("e"));
71+
72+
var f = new KeyStore.SecretKeyEntry(new SecretKeySpec(new byte[16], "AES"),
73+
Set.of(new PKCS12Attribute("1.2.3", "456")));
74+
ks.setEntry("f", f, new KeyStore.PasswordProtection(PASSWORD));
75+
76+
cag.generate(-1);
77+
var g = new KeyStore.TrustedCertificateEntry(ss("g"),
78+
Set.of(new PKCS12Attribute("1.2.4", "456")));
79+
ks.setEntry("g", g, null);
80+
81+
cag.generate(-1);
82+
var h = new KeyStore.PrivateKeyEntry(cag.getPrivateKey(), chain("h"),
83+
Set.of(new PKCS12Attribute("1.2.5", "456")));
84+
ks.setEntry("h", h, new KeyStore.PasswordProtection(PASSWORD));
85+
86+
var i = new KeyStore.SecretKeyEntry(new SecretKeySpec(new byte[16], "AES"));
87+
ks.setEntry("i", i, new KeyStore.PasswordProtection(PASSWORD));
88+
89+
cag.generate(-1);
90+
var j = new KeyStore.TrustedCertificateEntry(ss("g"));
91+
ks.setEntry("j", j, null);
92+
93+
cag.generate(-1);
94+
var k = new KeyStore.PrivateKeyEntry(cag.getPrivateKey(), chain("h"));
95+
ks.setEntry("k", k, new KeyStore.PasswordProtection(PASSWORD));
96+
check();
97+
98+
var bout = new ByteArrayOutputStream();
99+
ks.store(bout, PASSWORD);
100+
ks.load(new ByteArrayInputStream(bout.toByteArray()), PASSWORD);
101+
check();
102+
}
103+
104+
static X509Certificate ss(String alias) throws Exception {
105+
return cag.getSelfCertificate(new X500Name("CN=" + alias), 100);
106+
}
107+
108+
static Certificate[] chain(String alias) throws Exception {
109+
return new Certificate[] { ss(alias) };
110+
}
111+
112+
static Void check() {
113+
checkAttributes("c", TRUSTED_KEY_USAGE);
114+
checkAttributes("d", LOCAL_KEY_ID);
115+
checkAttributes("e", LOCAL_KEY_ID);
116+
checkAttributes("f", LOCAL_KEY_ID, "1.2.3");
117+
checkAttributes("g", TRUSTED_KEY_USAGE, "1.2.4");
118+
checkAttributes("h", LOCAL_KEY_ID, "1.2.5");
119+
checkAttributes("i", LOCAL_KEY_ID);
120+
checkAttributes("j", TRUSTED_KEY_USAGE);
121+
checkAttributes("k", LOCAL_KEY_ID);
122+
return null;
123+
}
124+
125+
static void checkAttributes(String alias, String... keys) {
126+
try {
127+
var attrs = keys[0].equals(LOCAL_KEY_ID)
128+
? ks.getAttributes(alias)
129+
: ks.getEntry(alias, null).getAttributes();
130+
Asserts.assertEQ(attrs.size(), keys.length + 1);
131+
Asserts.assertTrue(
132+
attrs.contains(new PKCS12Attribute(FRIENDLY_NAME, alias)));
133+
for (var attr : attrs) {
134+
var name = attr.getName();
135+
if (name.equals(FRIENDLY_NAME)) continue;
136+
var found = false;
137+
for (var key : keys) {
138+
if (key.equals(name)) {
139+
found = true;
140+
break;
141+
}
142+
}
143+
Asserts.assertTrue(found, name);
144+
}
145+
} catch (Exception e) {
146+
throw new RuntimeException(e);
147+
}
148+
}
149+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright (c) 2023, 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 8309667
27+
* @library /test/lib
28+
* @modules java.base/sun.security.tools.keytool
29+
* java.base/sun.security.x509
30+
* @summary ensures attributes reading is thread safe
31+
*/
32+
import sun.security.tools.keytool.CertAndKeyGen;
33+
import sun.security.x509.X500Name;
34+
35+
import java.security.KeyStore;
36+
import java.security.PKCS12Attribute;
37+
import java.util.Set;
38+
import java.util.concurrent.Executors;
39+
import java.util.concurrent.TimeUnit;
40+
import java.util.concurrent.atomic.AtomicBoolean;
41+
42+
public class AttributesMultiThread {
43+
44+
static KeyStore ks;
45+
static AtomicBoolean ab = new AtomicBoolean();
46+
47+
public static void main(String[] args) throws Exception {
48+
49+
ks = KeyStore.getInstance("pkcs12");
50+
ks.load(null, null);
51+
var cak = new CertAndKeyGen("ed25519", "ed25519");
52+
cak.generate("ed25519");
53+
var c = cak.getSelfCertificate(new X500Name("CN=A"), 1000);
54+
Set<KeyStore.Entry.Attribute> ss = Set.of(
55+
new PKCS12Attribute("1.1.1.1", "b"),
56+
new PKCS12Attribute("1.1.1.2", "b"),
57+
new PKCS12Attribute("1.1.1.3", "b"),
58+
new PKCS12Attribute("1.1.1.4", "b"),
59+
new PKCS12Attribute("1.1.1.5", "b"),
60+
new PKCS12Attribute("1.1.1.6", "b"),
61+
new PKCS12Attribute("1.1.1.7", "b"),
62+
new PKCS12Attribute("1.1.1.8", "b"),
63+
new PKCS12Attribute("1.1.1.9", "b"),
64+
new PKCS12Attribute("1.1.1.10", "b"));
65+
ks.setEntry("a", new KeyStore.TrustedCertificateEntry(c, ss), null);
66+
67+
var x = Executors.newVirtualThreadPerTaskExecutor();
68+
for (int i = 0; i < 1000; i++) {
69+
x.submit(AttributesMultiThread::check);
70+
}
71+
x.shutdown();
72+
x.awaitTermination(1, TimeUnit.MINUTES);
73+
74+
if (ab.get()) {
75+
throw new RuntimeException();
76+
}
77+
}
78+
79+
static void check() {
80+
for (int i = 0; i < 100; i++) {
81+
var s = get();
82+
if (s.size() != 12) { // 10 presets and 2 added by PKCS12
83+
ab.set(true);
84+
throw new RuntimeException();
85+
}
86+
}
87+
}
88+
89+
static Set<?> get() {
90+
try {
91+
return ks.getAttributes("a");
92+
} catch (Exception e) {
93+
throw new RuntimeException(e);
94+
}
95+
}
96+
}

0 commit comments

Comments
 (0)
Please sign in to comment.