Skip to content

Commit abcd095

Browse files
martinuygnu-andrew
authored andcommitted
RH1991003: Enable the import of plain keys into the NSS software token.
This can be individually disabled using -Dcom.redhat.fips.plainKeySupport=false
1 parent bfd7c5d commit abcd095

File tree

7 files changed

+439
-5
lines changed

7 files changed

+439
-5
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ private static class ProviderProperty {
8282
public boolean isSystemFipsEnabled() {
8383
return SystemConfigurator.isSystemFipsEnabled();
8484
}
85+
@Override
86+
public boolean isPlainKeySupportEnabled() {
87+
return SystemConfigurator.isPlainKeySupportEnabled();
88+
}
8589
});
8690

8791
// doPrivileged here because there are multiple

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ final class SystemConfigurator {
5555
CRYPTO_POLICIES_BASE_DIR + "/back-ends/java.config";
5656

5757
private static boolean systemFipsEnabled = false;
58+
private static boolean plainKeySupportEnabled = false;
5859

5960
private static final String SYSTEMCONF_NATIVE_LIB = "systemconf";
6061

@@ -150,6 +151,16 @@ static boolean configure(Properties props) {
150151
}
151152
loadedProps = true;
152153
systemFipsEnabled = true;
154+
String plainKeySupport = System.getProperty("com.redhat.fips.plainKeySupport",
155+
"true");
156+
plainKeySupportEnabled = !"false".equals(plainKeySupport);
157+
if (sdebug != null) {
158+
if (plainKeySupportEnabled) {
159+
sdebug.println("FIPS support enabled with plain key support");
160+
} else {
161+
sdebug.println("FIPS support enabled without plain key support");
162+
}
163+
}
153164
}
154165
} catch (Exception e) {
155166
if (sdebug != null) {
@@ -177,6 +188,19 @@ static boolean isSystemFipsEnabled() {
177188
return systemFipsEnabled;
178189
}
179190

191+
/**
192+
* Returns {@code true} if system FIPS alignment is enabled
193+
* and plain key support is allowed. Plain key support is
194+
* enabled by default but can be disabled with
195+
* {@code -Dcom.redhat.fips.plainKeySupport=false}.
196+
*
197+
* @return a boolean indicating whether plain key support
198+
* should be enabled.
199+
*/
200+
static boolean isPlainKeySupportEnabled() {
201+
return plainKeySupportEnabled;
202+
}
203+
180204
/*
181205
* OpenJDK FIPS mode will be enabled only if the com.redhat.fips
182206
* system property is true (default) and the system is in FIPS mode.

src/java.base/share/classes/jdk/internal/access/JavaSecuritySystemConfiguratorAccess.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@
2727

2828
public interface JavaSecuritySystemConfiguratorAccess {
2929
boolean isSystemFipsEnabled();
30+
boolean isPlainKeySupportEnabled();
3031
}
Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
/*
2+
* Copyright (c) 2021, Red Hat, Inc.
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. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
26+
package sun.security.pkcs11;
27+
28+
import java.math.BigInteger;
29+
import java.security.KeyFactory;
30+
import java.security.Provider;
31+
import java.security.Security;
32+
import java.util.HashMap;
33+
import java.util.Map;
34+
import java.util.concurrent.locks.ReentrantLock;
35+
36+
import javax.crypto.Cipher;
37+
import javax.crypto.spec.DHPrivateKeySpec;
38+
import javax.crypto.spec.IvParameterSpec;
39+
40+
import sun.security.jca.JCAUtil;
41+
import sun.security.pkcs11.TemplateManager;
42+
import sun.security.pkcs11.wrapper.CK_ATTRIBUTE;
43+
import sun.security.pkcs11.wrapper.CK_MECHANISM;
44+
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
45+
import static sun.security.pkcs11.wrapper.PKCS11Exception.*;
46+
import sun.security.pkcs11.wrapper.PKCS11Exception;
47+
import sun.security.rsa.RSAUtil.KeyType;
48+
import sun.security.util.Debug;
49+
import sun.security.util.ECUtil;
50+
51+
final class FIPSKeyImporter {
52+
53+
private static final Debug debug =
54+
Debug.getInstance("sunpkcs11");
55+
56+
private static P11Key importerKey = null;
57+
private static final ReentrantLock importerKeyLock = new ReentrantLock();
58+
private static CK_MECHANISM importerKeyMechanism = null;
59+
private static Cipher importerCipher = null;
60+
61+
private static Provider sunECProvider = null;
62+
private static final ReentrantLock sunECProviderLock = new ReentrantLock();
63+
64+
private static KeyFactory DHKF = null;
65+
private static final ReentrantLock DHKFLock = new ReentrantLock();
66+
67+
static Long importKey(SunPKCS11 sunPKCS11, long hSession, CK_ATTRIBUTE[] attributes)
68+
throws PKCS11Exception {
69+
long keyID = -1;
70+
Token token = sunPKCS11.getToken();
71+
if (debug != null) {
72+
debug.println("Private or Secret key will be imported in" +
73+
" system FIPS mode.");
74+
}
75+
if (importerKey == null) {
76+
importerKeyLock.lock();
77+
try {
78+
if (importerKey == null) {
79+
if (importerKeyMechanism == null) {
80+
// Importer Key creation has not been tried yet. Try it.
81+
createImporterKey(token);
82+
}
83+
if (importerKey == null || importerCipher == null) {
84+
if (debug != null) {
85+
debug.println("Importer Key could not be" +
86+
" generated.");
87+
}
88+
throw new PKCS11Exception(CKR_GENERAL_ERROR);
89+
}
90+
if (debug != null) {
91+
debug.println("Importer Key successfully" +
92+
" generated.");
93+
}
94+
}
95+
} finally {
96+
importerKeyLock.unlock();
97+
}
98+
}
99+
long importerKeyID = importerKey.getKeyID();
100+
try {
101+
byte[] keyBytes = null;
102+
byte[] encKeyBytes = null;
103+
long keyClass = 0L;
104+
long keyType = 0L;
105+
Map<Long, CK_ATTRIBUTE> attrsMap = new HashMap<>();
106+
for (CK_ATTRIBUTE attr : attributes) {
107+
if (attr.type == CKA_CLASS) {
108+
keyClass = attr.getLong();
109+
} else if (attr.type == CKA_KEY_TYPE) {
110+
keyType = attr.getLong();
111+
}
112+
attrsMap.put(attr.type, attr);
113+
}
114+
BigInteger v = null;
115+
if (keyClass == CKO_PRIVATE_KEY) {
116+
if (keyType == CKK_RSA) {
117+
if (debug != null) {
118+
debug.println("Importing an RSA private key...");
119+
}
120+
keyBytes = sun.security.rsa.RSAPrivateCrtKeyImpl.newKey(
121+
KeyType.RSA,
122+
null,
123+
((v = attrsMap.get(CKA_MODULUS).getBigInteger()) != null)
124+
? v : BigInteger.ZERO,
125+
((v = attrsMap.get(CKA_PUBLIC_EXPONENT).getBigInteger()) != null)
126+
? v : BigInteger.ZERO,
127+
((v = attrsMap.get(CKA_PRIVATE_EXPONENT).getBigInteger()) != null)
128+
? v : BigInteger.ZERO,
129+
((v = attrsMap.get(CKA_PRIME_1).getBigInteger()) != null)
130+
? v : BigInteger.ZERO,
131+
((v = attrsMap.get(CKA_PRIME_2).getBigInteger()) != null)
132+
? v : BigInteger.ZERO,
133+
((v = attrsMap.get(CKA_EXPONENT_1).getBigInteger()) != null)
134+
? v : BigInteger.ZERO,
135+
((v = attrsMap.get(CKA_EXPONENT_2).getBigInteger()) != null)
136+
? v : BigInteger.ZERO,
137+
((v = attrsMap.get(CKA_COEFFICIENT).getBigInteger()) != null)
138+
? v : BigInteger.ZERO
139+
).getEncoded();
140+
} else if (keyType == CKK_DSA) {
141+
if (debug != null) {
142+
debug.println("Importing a DSA private key...");
143+
}
144+
keyBytes = new sun.security.provider.DSAPrivateKey(
145+
((v = attrsMap.get(CKA_VALUE).getBigInteger()) != null)
146+
? v : BigInteger.ZERO,
147+
((v = attrsMap.get(CKA_PRIME).getBigInteger()) != null)
148+
? v : BigInteger.ZERO,
149+
((v = attrsMap.get(CKA_SUBPRIME).getBigInteger()) != null)
150+
? v : BigInteger.ZERO,
151+
((v = attrsMap.get(CKA_BASE).getBigInteger()) != null)
152+
? v : BigInteger.ZERO
153+
).getEncoded();
154+
if (token.config.getNssNetscapeDbWorkaround() &&
155+
attrsMap.get(CKA_NETSCAPE_DB) == null) {
156+
attrsMap.put(CKA_NETSCAPE_DB,
157+
new CK_ATTRIBUTE(CKA_NETSCAPE_DB, BigInteger.ZERO));
158+
}
159+
} else if (keyType == CKK_EC) {
160+
if (debug != null) {
161+
debug.println("Importing an EC private key...");
162+
}
163+
if (sunECProvider == null) {
164+
sunECProviderLock.lock();
165+
try {
166+
if (sunECProvider == null) {
167+
sunECProvider = Security.getProvider("SunEC");
168+
}
169+
} finally {
170+
sunECProviderLock.unlock();
171+
}
172+
}
173+
keyBytes = ECUtil.generateECPrivateKey(
174+
((v = attrsMap.get(CKA_VALUE).getBigInteger()) != null)
175+
? v : BigInteger.ZERO,
176+
ECUtil.getECParameterSpec(sunECProvider,
177+
attrsMap.get(CKA_EC_PARAMS).getByteArray()))
178+
.getEncoded();
179+
if (token.config.getNssNetscapeDbWorkaround() &&
180+
attrsMap.get(CKA_NETSCAPE_DB) == null) {
181+
attrsMap.put(CKA_NETSCAPE_DB,
182+
new CK_ATTRIBUTE(CKA_NETSCAPE_DB, BigInteger.ZERO));
183+
}
184+
} else if (keyType == CKK_DH) {
185+
if (debug != null) {
186+
debug.println("Importing a Diffie-Hellman private key...");
187+
}
188+
if (DHKF == null) {
189+
DHKFLock.lock();
190+
try {
191+
if (DHKF == null) {
192+
DHKF = KeyFactory.getInstance(
193+
"DH", P11Util.getSunJceProvider());
194+
}
195+
} finally {
196+
DHKFLock.unlock();
197+
}
198+
}
199+
DHPrivateKeySpec spec = new DHPrivateKeySpec
200+
(((v = attrsMap.get(CKA_VALUE).getBigInteger()) != null)
201+
? v : BigInteger.ZERO,
202+
((v = attrsMap.get(CKA_PRIME).getBigInteger()) != null)
203+
? v : BigInteger.ZERO,
204+
((v = attrsMap.get(CKA_BASE).getBigInteger()) != null)
205+
? v : BigInteger.ZERO);
206+
keyBytes = DHKF.generatePrivate(spec).getEncoded();
207+
if (token.config.getNssNetscapeDbWorkaround() &&
208+
attrsMap.get(CKA_NETSCAPE_DB) == null) {
209+
attrsMap.put(CKA_NETSCAPE_DB,
210+
new CK_ATTRIBUTE(CKA_NETSCAPE_DB, BigInteger.ZERO));
211+
}
212+
} else {
213+
if (debug != null) {
214+
debug.println("Unrecognized private key type.");
215+
}
216+
throw new PKCS11Exception(CKR_GENERAL_ERROR);
217+
}
218+
} else if (keyClass == CKO_SECRET_KEY) {
219+
if (debug != null) {
220+
debug.println("Importing a secret key...");
221+
}
222+
keyBytes = attrsMap.get(CKA_VALUE).getByteArray();
223+
}
224+
if (keyBytes == null || keyBytes.length == 0) {
225+
if (debug != null) {
226+
debug.println("Private or secret key plain bytes could" +
227+
" not be obtained. Import failed.");
228+
}
229+
throw new PKCS11Exception(CKR_GENERAL_ERROR);
230+
}
231+
importerCipher.init(Cipher.ENCRYPT_MODE, importerKey,
232+
new IvParameterSpec((byte[])importerKeyMechanism.pParameter),
233+
null);
234+
attributes = new CK_ATTRIBUTE[attrsMap.size()];
235+
attrsMap.values().toArray(attributes);
236+
encKeyBytes = importerCipher.doFinal(keyBytes);
237+
attributes = token.getAttributes(TemplateManager.O_IMPORT,
238+
keyClass, keyType, attributes);
239+
keyID = token.p11.C_UnwrapKey(hSession,
240+
importerKeyMechanism, importerKeyID, encKeyBytes, attributes);
241+
if (debug != null) {
242+
debug.println("Imported key ID: " + keyID);
243+
}
244+
} catch (Throwable t) {
245+
throw new PKCS11Exception(CKR_GENERAL_ERROR);
246+
} finally {
247+
importerKey.releaseKeyID();
248+
}
249+
return Long.valueOf(keyID);
250+
}
251+
252+
private static void createImporterKey(Token token) {
253+
if (debug != null) {
254+
debug.println("Generating Importer Key...");
255+
}
256+
byte[] iv = new byte[16];
257+
JCAUtil.getSecureRandom().nextBytes(iv);
258+
importerKeyMechanism = new CK_MECHANISM(CKM_AES_CBC_PAD, iv);
259+
try {
260+
CK_ATTRIBUTE[] attributes = token.getAttributes(TemplateManager.O_GENERATE,
261+
CKO_SECRET_KEY, CKK_AES, new CK_ATTRIBUTE[] {
262+
new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
263+
new CK_ATTRIBUTE(CKA_VALUE_LEN, 256 >> 3)});
264+
Session s = null;
265+
try {
266+
s = token.getObjSession();
267+
long keyID = token.p11.C_GenerateKey(
268+
s.id(), new CK_MECHANISM(CKM_AES_KEY_GEN),
269+
attributes);
270+
if (debug != null) {
271+
debug.println("Importer Key ID: " + keyID);
272+
}
273+
importerKey = (P11Key)P11Key.secretKey(s, keyID, "AES",
274+
256 >> 3, null);
275+
} catch (PKCS11Exception e) {
276+
// best effort
277+
} finally {
278+
token.releaseSession(s);
279+
}
280+
if (importerKey != null) {
281+
importerCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
282+
}
283+
} catch (Throwable t) {
284+
// best effort
285+
importerKey = null;
286+
importerCipher = null;
287+
// importerKeyMechanism value is kept initialized to indicate that
288+
// Importer Key creation has been tried and failed.
289+
}
290+
}
291+
}

0 commit comments

Comments
 (0)