Skip to content

Commit 4445e5c

Browse files
author
Paul Hohensee
committed
8263404: RsaPrivateKeySpec is always recognized as RSAPrivateCrtKeySpec in RSAKeyFactory.engineGetKeySpec
Reviewed-by: simonis, mbalao, andrew Backport-of: a5d7de235101696463dba22792703c6809ff7fc4
1 parent f1c1f96 commit 4445e5c

File tree

5 files changed

+285
-57
lines changed

5 files changed

+285
-57
lines changed

jdk/src/share/classes/sun/security/pkcs11/P11RSAKeyFactory.java

Lines changed: 42 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -282,54 +282,51 @@ <T extends KeySpec> T implGetPublicKeySpec(P11Key key, Class<T> keySpec,
282282

283283
<T extends KeySpec> T implGetPrivateKeySpec(P11Key key, Class<T> keySpec,
284284
Session[] session) throws PKCS11Exception, InvalidKeySpecException {
285+
if (key.sensitive || !key.extractable) {
286+
throw new InvalidKeySpecException("Key is sensitive or not extractable");
287+
}
288+
// If the key is both extractable and not sensitive, then when it was converted into a P11Key
289+
// it was also converted into subclass of RSAPrivateKey which encapsulates all of the logic
290+
// necessary to retrieve the attributes we need. This sub-class will also cache these attributes
291+
// so that we do not need to query them more than once.
292+
// Rather than rewrite this logic and make possibly slow calls to the token, we'll just use
293+
// that existing logic.
285294
if (keySpec.isAssignableFrom(RSAPrivateCrtKeySpec.class)) {
286-
session[0] = token.getObjSession();
287-
CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
288-
new CK_ATTRIBUTE(CKA_MODULUS),
289-
new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT),
290-
new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT),
291-
new CK_ATTRIBUTE(CKA_PRIME_1),
292-
new CK_ATTRIBUTE(CKA_PRIME_2),
293-
new CK_ATTRIBUTE(CKA_EXPONENT_1),
294-
new CK_ATTRIBUTE(CKA_EXPONENT_2),
295-
new CK_ATTRIBUTE(CKA_COEFFICIENT),
296-
};
297-
long keyID = key.getKeyID();
298-
try {
299-
token.p11.C_GetAttributeValue(session[0].id(), keyID, attributes);
300-
} finally {
301-
key.releaseKeyID();
302-
}
295+
// All supported keyspecs (other than PKCS8EncodedKeySpec) descend from RSAPrivateCrtKeySpec
296+
if (key instanceof RSAPrivateCrtKey) {
297+
RSAPrivateCrtKey crtKey = (RSAPrivateCrtKey)key;
298+
return keySpec.cast(new RSAPrivateCrtKeySpec(
299+
crtKey.getModulus(),
300+
crtKey.getPublicExponent(),
301+
crtKey.getPrivateExponent(),
302+
crtKey.getPrimeP(),
303+
crtKey.getPrimeQ(),
304+
crtKey.getPrimeExponentP(),
305+
crtKey.getPrimeExponentQ(),
306+
crtKey.getCrtCoefficient(),
307+
crtKey.getParams()
308+
));
309+
} else { // RSAPrivateKey (non-CRT)
310+
if (!keySpec.isAssignableFrom(RSAPrivateKeySpec.class)) {
311+
throw new InvalidKeySpecException
312+
("RSAPrivateCrtKeySpec can only be used with CRT keys");
313+
}
303314

304-
KeySpec spec = new RSAPrivateCrtKeySpec(
305-
attributes[0].getBigInteger(),
306-
attributes[1].getBigInteger(),
307-
attributes[2].getBigInteger(),
308-
attributes[3].getBigInteger(),
309-
attributes[4].getBigInteger(),
310-
attributes[5].getBigInteger(),
311-
attributes[6].getBigInteger(),
312-
attributes[7].getBigInteger()
313-
);
314-
return keySpec.cast(spec);
315-
} else if (keySpec.isAssignableFrom(RSAPrivateKeySpec.class)) {
316-
session[0] = token.getObjSession();
317-
CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
318-
new CK_ATTRIBUTE(CKA_MODULUS),
319-
new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT),
320-
};
321-
long keyID = key.getKeyID();
322-
try {
323-
token.p11.C_GetAttributeValue(session[0].id(), keyID, attributes);
324-
} finally {
325-
key.releaseKeyID();
326-
}
315+
if (!(key instanceof RSAPrivateKey)) {
316+
// We should never reach here as P11Key.privateKey() should always produce an instance
317+
// of RSAPrivateKey when the RSA key is both extractable and non-sensitive.
318+
throw new InvalidKeySpecException
319+
("Key must be an instance of RSAPrivateKeySpec. Was " + key.getClass());
320+
}
327321

328-
KeySpec spec = new RSAPrivateKeySpec(
329-
attributes[0].getBigInteger(),
330-
attributes[1].getBigInteger()
331-
);
332-
return keySpec.cast(spec);
322+
// fall through to RSAPrivateKey (non-CRT)
323+
RSAPrivateKey rsaKey = (RSAPrivateKey) key;
324+
return keySpec.cast(new RSAPrivateKeySpec(
325+
rsaKey.getModulus(),
326+
rsaKey.getPrivateExponent(),
327+
rsaKey.getParams()
328+
));
329+
}
333330
} else { // PKCS#8 handled in superclass
334331
throw new InvalidKeySpecException("Only RSAPrivate(Crt)KeySpec "
335332
+ "and PKCS8EncodedKeySpec supported for RSA private keys");

jdk/src/share/classes/sun/security/rsa/RSAKeyFactory.java

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,7 @@ protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
421421
if (keySpec.isAssignableFrom(PKCS8_KEYSPEC_CLS)) {
422422
return keySpec.cast(new PKCS8EncodedKeySpec(key.getEncoded()));
423423
} else if (keySpec.isAssignableFrom(RSA_PRIVCRT_KEYSPEC_CLS)) {
424+
// All supported keyspecs (other than PKCS8_KEYSPEC_CLS) descend from RSA_PRIVCRT_KEYSPEC_CLS
424425
if (key instanceof RSAPrivateCrtKey) {
425426
RSAPrivateCrtKey crtKey = (RSAPrivateCrtKey)key;
426427
return keySpec.cast(new RSAPrivateCrtKeySpec(
@@ -434,17 +435,20 @@ protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
434435
crtKey.getCrtCoefficient(),
435436
crtKey.getParams()
436437
));
437-
} else {
438-
throw new InvalidKeySpecException
439-
("RSAPrivateCrtKeySpec can only be used with CRT keys");
438+
} else { // RSAPrivateKey (non-CRT)
439+
if (!keySpec.isAssignableFrom(RSA_PRIV_KEYSPEC_CLS)) {
440+
throw new InvalidKeySpecException
441+
("RSAPrivateCrtKeySpec can only be used with CRT keys");
442+
}
443+
444+
// fall through to RSAPrivateKey (non-CRT)
445+
RSAPrivateKey rsaKey = (RSAPrivateKey) key;
446+
return keySpec.cast(new RSAPrivateKeySpec(
447+
rsaKey.getModulus(),
448+
rsaKey.getPrivateExponent(),
449+
rsaKey.getParams()
450+
));
440451
}
441-
} else if (keySpec.isAssignableFrom(RSA_PRIV_KEYSPEC_CLS)) {
442-
RSAPrivateKey rsaKey = (RSAPrivateKey)key;
443-
return keySpec.cast(new RSAPrivateKeySpec(
444-
rsaKey.getModulus(),
445-
rsaKey.getPrivateExponent(),
446-
rsaKey.getParams()
447-
));
448452
} else {
449453
throw new InvalidKeySpecException
450454
("KeySpec must be RSAPrivate(Crt)KeySpec or "

jdk/test/java/security/KeyFactory/KeyFactoryGetKeySpecForInvalidSpec.java

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,31 +23,95 @@
2323

2424
/**
2525
* @test
26-
* @bug 8254717
26+
* @bug 8254717 8263404
2727
* @summary isAssignableFrom checks in KeyFactorySpi.engineGetKeySpec appear to be backwards.
2828
* @author Greg Rubin, Ziyi Luo
2929
*/
3030

31+
import java.math.BigInteger;
3132
import java.security.KeyFactory;
3233
import java.security.KeyPair;
3334
import java.security.KeyPairGenerator;
35+
import java.security.interfaces.RSAPrivateCrtKey;
36+
import java.security.interfaces.RSAPrivateKey;
3437
import java.security.spec.*;
3538

3639
public class KeyFactoryGetKeySpecForInvalidSpec {
40+
41+
// Test for 8263404: This method generates RSAPrivateKey (without Crt info) from a RSAPrivateCrtKey
42+
public static RSAPrivateKey privateCrtToPrivate(RSAPrivateCrtKey crtKey) {
43+
return new RSAPrivateKey() {
44+
@Override
45+
public BigInteger getPrivateExponent() {
46+
return crtKey.getPrivateExponent();
47+
}
48+
49+
@Override
50+
public String getAlgorithm() {
51+
return crtKey.getAlgorithm();
52+
}
53+
54+
@Override
55+
public String getFormat() {
56+
return crtKey.getFormat();
57+
}
58+
59+
@Override
60+
public byte[] getEncoded() {
61+
return crtKey.getEncoded();
62+
}
63+
64+
@Override
65+
public BigInteger getModulus() {
66+
return crtKey.getModulus();
67+
}
68+
};
69+
}
70+
3771
public static void main(String[] args) throws Exception {
38-
KeyPairGenerator kg = KeyPairGenerator.getInstance("RSA");
72+
KeyPairGenerator kg = KeyPairGenerator.getInstance("RSA", "SunRsaSign");
3973
kg.initialize(2048);
4074
KeyPair pair = kg.generateKeyPair();
4175

4276
KeyFactory factory = KeyFactory.getInstance("RSA");
4377

78+
// === Case 1: private key is RSAPrivateCrtKey, keySpec is RSAPrivateKeySpec
79+
// === Expected: return RSAPrivateCrtKeySpec
4480
// Since RSAPrivateCrtKeySpec inherits from RSAPrivateKeySpec, we'd expect this next line to return an instance of RSAPrivateKeySpec
4581
// (because the private key has CRT parts).
4682
KeySpec spec = factory.getKeySpec(pair.getPrivate(), RSAPrivateKeySpec.class);
4783
if (!(spec instanceof RSAPrivateCrtKeySpec)) {
4884
throw new Exception("Spec should be an instance of RSAPrivateCrtKeySpec");
4985
}
5086

87+
// === Case 2: private key is RSAPrivateCrtKey, keySpec is RSAPrivateCrtKeySpec
88+
// === Expected: return RSAPrivateCrtKeySpec
89+
spec = factory.getKeySpec(pair.getPrivate(), RSAPrivateCrtKeySpec.class);
90+
if (!(spec instanceof RSAPrivateCrtKeySpec)) {
91+
throw new Exception("Spec should be an instance of RSAPrivateCrtKeySpec");
92+
}
93+
94+
// === Case 3: private key is RSAPrivateKey, keySpec is RSAPrivateKeySpec
95+
// === Expected: return RSAPrivateKeySpec not RSAPrivateCrtKeySpec
96+
RSAPrivateKey notCrtKey = privateCrtToPrivate((RSAPrivateCrtKey)pair.getPrivate());
97+
// InvalidKeySpecException should not be thrown
98+
KeySpec notCrtSpec = factory.getKeySpec(notCrtKey, RSAPrivateKeySpec.class);
99+
if (notCrtSpec instanceof RSAPrivateCrtKeySpec) {
100+
throw new Exception("Spec should be an instance of RSAPrivateKeySpec not RSAPrivateCrtKeySpec");
101+
}
102+
if (!(notCrtSpec instanceof RSAPrivateKeySpec)) {
103+
throw new Exception("Spec should be an instance of RSAPrivateKeySpec");
104+
}
105+
106+
// === Case 4: private key is RSAPrivateKey, keySpec is RSAPrivateCrtKeySpec
107+
// === Expected: throw InvalidKeySpecException
108+
try {
109+
factory.getKeySpec(notCrtKey, RSAPrivateCrtKeySpec.class);
110+
throw new Exception("InvalidKeySpecException is expected but not thrown");
111+
} catch (InvalidKeySpecException e) {
112+
// continue;
113+
}
114+
51115
// This next line should give an InvalidKeySpec exception
52116
try {
53117
spec = factory.getKeySpec(pair.getPublic(), FakeX509Spec.class);
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
2+
# Configuration to run unit tests with NSS
3+
# Marks private and secret keys as sensitive
4+
5+
name = NSS
6+
7+
slot = 1
8+
9+
#showInfo = true
10+
11+
library = ${pkcs11test.nss.lib}
12+
13+
nssArgs = "configdir='${pkcs11test.nss.db}' certPrefix='' keyPrefix='' secmod='secmod.db' flags=readOnly"
14+
15+
disabledMechanisms = {
16+
CKM_DSA_SHA224
17+
CKM_DSA_SHA256
18+
CKM_DSA_SHA384
19+
CKM_DSA_SHA512
20+
CKM_DSA_SHA3_224
21+
CKM_DSA_SHA3_256
22+
CKM_DSA_SHA3_384
23+
CKM_DSA_SHA3_512
24+
CKM_ECDSA_SHA224
25+
CKM_ECDSA_SHA256
26+
CKM_ECDSA_SHA384
27+
CKM_ECDSA_SHA512
28+
CKM_ECDSA_SHA3_224
29+
CKM_ECDSA_SHA3_256
30+
CKM_ECDSA_SHA3_384
31+
CKM_ECDSA_SHA3_512
32+
}
33+
34+
attributes = compatibility
35+
36+
# NSS needs CKA_NETSCAPE_DB for DSA and DH private keys
37+
# just put an arbitrary value in there to make it happy
38+
39+
attributes(*,CKO_PRIVATE_KEY,CKK_DSA) = {
40+
CKA_NETSCAPE_DB = 0h00
41+
}
42+
43+
attributes(*,CKO_PRIVATE_KEY,CKK_DH) = {
44+
CKA_NETSCAPE_DB = 0h00
45+
}
46+
47+
# Everything above this line (with the exception of the comment at the top) is copy/pasted from p11-nss.txt
48+
49+
# Make all private keys sensitive
50+
attributes(*,CKO_PRIVATE_KEY,*) = {
51+
CKA_SENSITIVE = true
52+
}
53+
54+
55+
# Make all secret keys sensitive
56+
attributes(*,CKO_SECRET_KEY,*) = {
57+
CKA_SENSITIVE = true
58+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright (c) 2021, Amazon.com, Inc. 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+
import java.math.BigInteger;
25+
import java.security.KeyFactory;
26+
import java.security.KeyPair;
27+
import java.security.KeyPairGenerator;
28+
import java.security.Provider;
29+
import java.security.PrivateKey;
30+
import java.security.interfaces.RSAPrivateKey;
31+
import java.security.interfaces.RSAPrivateCrtKey;
32+
import java.security.spec.*;
33+
34+
/**
35+
* @test
36+
* @bug 8263404
37+
* @summary RSAPrivateCrtKeySpec is prefered for CRT keys even when a RsaPrivateKeySpec is requested.
38+
* @summary Also checks to ensure that sensitive RSA keys are correctly not exposed
39+
* @library /lib/testlibrary ..
40+
* @run main/othervm TestP11KeyFactoryGetRSAKeySpec
41+
* @run main/othervm TestP11KeyFactoryGetRSAKeySpec sm rsakeys.ks.policy
42+
* @run main/othervm -DCUSTOM_P11_CONFIG_NAME=p11-nss-sensitive.txt -DNO_DEIMOS=true -DNO_DEFAULT=true TestP11KeyFactoryGetRSAKeySpec
43+
*/
44+
45+
public class TestP11KeyFactoryGetRSAKeySpec extends PKCS11Test {
46+
private static boolean testingSensitiveKeys = false;
47+
public static void main(String[] args) throws Exception {
48+
testingSensitiveKeys = "p11-nss-sensitive.txt".equals(System.getProperty("CUSTOM_P11_CONFIG_NAME"));
49+
main(new TestP11KeyFactoryGetRSAKeySpec(), args);
50+
}
51+
52+
@Override
53+
public void main(Provider p) throws Exception {
54+
KeyPairGenerator kg = KeyPairGenerator.getInstance("RSA", p);
55+
kg.initialize(2048);
56+
KeyPair pair = kg.generateKeyPair();
57+
PrivateKey privKey = pair.getPrivate();
58+
59+
KeyFactory factory = KeyFactory.getInstance("RSA", p);
60+
61+
// If this is a sensitive key, then it shouldn't implement the RSAPrivateKey interface as that exposes sensitive fields
62+
boolean keyExposesSensitiveFields = privKey instanceof RSAPrivateKey;
63+
if (keyExposesSensitiveFields == testingSensitiveKeys) {
64+
throw new Exception("Key of type " + privKey.getClass() + " returned when testing sensitive keys is " + testingSensitiveKeys);
65+
}
66+
67+
if (!testingSensitiveKeys) {
68+
// The remaining tests require that the PKCS #11 token actually generated a CRT key.
69+
// This is the normal and expected case, but we add an assertion here to detect a broken test due to bad assumptions.
70+
if (!(privKey instanceof RSAPrivateCrtKey)) {
71+
throw new Exception("Test assumption violated: PKCS #11 token did not generate a CRT key.");
72+
}
73+
}
74+
75+
// === Case 1: private key is RSAPrivateCrtKey, keySpec is RSAPrivateKeySpec
76+
// === Expected: return RSAPrivateCrtKeySpec
77+
// Since RSAPrivateCrtKeySpec inherits from RSAPrivateKeySpec, we'd expect this next line to return an instance of RSAPrivateKeySpec
78+
// (because the private key has CRT parts).
79+
testKeySpec(factory, privKey, RSAPrivateKeySpec.class);
80+
81+
// === Case 2: private key is RSAPrivateCrtKey, keySpec is RSAPrivateCrtKeySpec
82+
// === Expected: return RSAPrivateCrtKeySpec
83+
testKeySpec(factory, privKey, RSAPrivateCrtKeySpec.class);
84+
}
85+
86+
private static void testKeySpec(KeyFactory factory, PrivateKey key, Class<? extends KeySpec> specClass) throws Exception {
87+
try {
88+
KeySpec spec = factory.getKeySpec(key, RSAPrivateKeySpec.class);
89+
if (testingSensitiveKeys) {
90+
throw new Exception("Able to retrieve spec from sensitive key");
91+
}
92+
if (!(spec instanceof RSAPrivateCrtKeySpec)) {
93+
throw new Exception("Spec should be an instance of RSAPrivateCrtKeySpec");
94+
}
95+
} catch (final InvalidKeySpecException ex) {
96+
if (testingSensitiveKeys) {
97+
// Expected exception so swallow it
98+
System.err.println("This exception is expected when retrieving sensitive properties from a sensitive PKCS #11 key.");
99+
ex.printStackTrace();
100+
} else {
101+
throw ex;
102+
}
103+
}
104+
}
105+
}

0 commit comments

Comments
 (0)