Skip to content

Commit 1c596e1

Browse files
martinuyfranferrax
andcommitted
8301553: Support Password-Based Cryptography in SunPKCS11 (iteration #4)
Co-authored-by: Francisco Ferrari <[email protected]> Co-authored-by: Martin Balao <[email protected]>
1 parent 80575cc commit 1c596e1

File tree

7 files changed

+106
-85
lines changed

7 files changed

+106
-85
lines changed

src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,8 @@ final class PBKDF2KeyImpl implements javax.crypto.interfaces.PBEKey {
6565
private byte[] key;
6666

6767
// The following fields are not Serializable. See writeReplace method.
68-
@SuppressWarnings("serial")
69-
private Mac prf;
70-
@SuppressWarnings("serial")
71-
private Cleaner.Cleanable cleaner;
68+
private transient Mac prf;
69+
private transient Cleaner.Cleanable cleaner;
7270

7371
private static byte[] getPasswordBytes(char[] passwd) {
7472
CharBuffer cb = CharBuffer.wrap(passwd);

src/java.base/share/classes/sun/security/util/PBEUtil.java

Lines changed: 77 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,51 @@ public static final class PBES2Params {
6767
private byte[] salt;
6868
private IvParameterSpec ivSpec;
6969

70+
/*
71+
* Initialize a PBES2Params instance. May generate random salt and
72+
* IV if not passed and the operation is encryption. If initialization
73+
* fails, values are reset. Used by PBES2Params and P11PBECipher
74+
* (SunPKCS11).
75+
*/
76+
public void initialize(int blkSize, int opmode, int iCount, byte[] salt,
77+
AlgorithmParameterSpec params, SecureRandom random)
78+
throws InvalidAlgorithmParameterException {
79+
try {
80+
boolean doEncrypt = ((opmode == Cipher.ENCRYPT_MODE) ||
81+
(opmode == Cipher.WRAP_MODE));
82+
if (params instanceof PBEParameterSpec pbeParams) {
83+
params = pbeParams.getParameterSpec();
84+
}
85+
if (params instanceof IvParameterSpec iv) {
86+
this.ivSpec = iv;
87+
} else if (params == null && doEncrypt) {
88+
byte[] ivBytes = new byte[blkSize];
89+
random.nextBytes(ivBytes);
90+
this.ivSpec = new IvParameterSpec(ivBytes);
91+
} else {
92+
throw new InvalidAlgorithmParameterException("Wrong " +
93+
"parameter type: IvParameterSpec " +
94+
(doEncrypt ? "or null " : "") + "expected");
95+
}
96+
this.iCount = iCount;
97+
if (salt == null) {
98+
if (doEncrypt) {
99+
salt = new byte[DEFAULT_SALT_LENGTH];
100+
random.nextBytes(salt);
101+
} else {
102+
throw new InvalidAlgorithmParameterException("Salt " +
103+
"needed for decryption");
104+
}
105+
}
106+
this.salt = salt;
107+
} catch (InvalidAlgorithmParameterException e) {
108+
this.ivSpec = null;
109+
this.iCount = 0;
110+
this.salt = null;
111+
throw e;
112+
}
113+
}
114+
70115
/*
71116
* Obtain an IvParameterSpec for Cipher services. This method returns
72117
* null when the state is not initialized. Used by PBES2Core (SunJCE)
@@ -77,116 +122,84 @@ public IvParameterSpec getIvSpec() {
77122
}
78123

79124
/*
80-
* Obtain AlgorithmParameters for Cipher services. If the state is not
81-
* initialized, this method will generate new values randomly or assign
82-
* from defaults. If the state is initialized, existing values will be
83-
* returned. Used by PBES2Core (SunJCE) and P11PBECipher (SunPKCS11).
125+
* Obtain AlgorithmParameters for Cipher services. This method will
126+
* initialize PBES2Params if needed, generating new values randomly or
127+
* assigning from defaults. If PBES2Params is initialized, existing
128+
* values will be returned. Used by PBES2Core (SunJCE) and
129+
* P11PBECipher (SunPKCS11).
84130
*/
85131
public AlgorithmParameters getAlgorithmParameters(int blkSize,
86132
String pbeAlgo, Provider algParamsProv, SecureRandom random) {
87-
AlgorithmParameters params = null;
88-
if (salt == null) {
89-
// generate random salt and use default iteration count
90-
salt = new byte[DEFAULT_SALT_LENGTH];
91-
random.nextBytes(salt);
92-
iCount = DEFAULT_ITERATIONS;
93-
}
94-
if (ivSpec == null) {
95-
// generate random IV
96-
byte[] ivBytes = new byte[blkSize];
97-
random.nextBytes(ivBytes);
98-
ivSpec = new IvParameterSpec(ivBytes);
99-
}
100-
PBEParameterSpec pbeSpec = new PBEParameterSpec(
101-
salt, iCount, ivSpec);
133+
AlgorithmParameters params;
102134
try {
135+
if (iCount == 0 && salt == null && ivSpec == null) {
136+
initialize(blkSize, Cipher.ENCRYPT_MODE, DEFAULT_ITERATIONS,
137+
null, null, random);
138+
}
103139
params = AlgorithmParameters.getInstance(pbeAlgo,
104140
algParamsProv);
105-
params.init(pbeSpec);
141+
params.init(new PBEParameterSpec(salt, iCount, ivSpec));
106142
} catch (NoSuchAlgorithmException nsae) {
107143
// should never happen
108144
throw new RuntimeException("AlgorithmParameters for "
109145
+ pbeAlgo + " not configured");
110146
} catch (InvalidParameterSpecException ipse) {
111147
// should never happen
112148
throw new RuntimeException("PBEParameterSpec not supported");
149+
} catch (InvalidAlgorithmParameterException iape) {
150+
// should never happen
151+
throw new RuntimeException("Error initializing PBES2Params");
113152
}
114153
return params;
115154
}
116155

117156
/*
118-
* Obtain a PBEKeySpec for Cipher services, after key and parameters
119-
* validation, random generation or assignment from defaults. Used by
120-
* PBES2Core (SunJCE) and P11PBECipher (SunPKCS11).
157+
* Initialize PBES2Params and obtain a PBEKeySpec for Cipher services.
158+
* Data from the key, parameters, defaults or random may be used for
159+
* initialization. Used by PBES2Core (SunJCE) and P11PBECipher
160+
* (SunPKCS11).
121161
*/
122162
public PBEKeySpec getPBEKeySpec(int blkSize, int keyLength, int opmode,
123163
Key key, AlgorithmParameterSpec params, SecureRandom random)
124164
throws InvalidKeyException, InvalidAlgorithmParameterException {
125165
if (key == null) {
126166
throw new InvalidKeyException("Null key");
127167
}
128-
129-
char[] passwdChars = null;
130-
salt = null;
131-
iCount = 0;
132-
ivSpec = null;
133168
PBEKeySpec pbeSpec;
134169
byte[] passwdBytes;
170+
char[] passwdChars = null;
135171
if (!(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3)) ||
136172
(passwdBytes = key.getEncoded()) == null) {
137173
throw new InvalidKeyException("Missing password");
138174
}
139175
try {
140-
boolean doEncrypt = ((opmode == Cipher.ENCRYPT_MODE) ||
141-
(opmode == Cipher.WRAP_MODE));
142-
176+
int iCountInit;
177+
byte[] saltInit;
143178
// Extract from the supplied PBE params, if present
144179
if (params instanceof PBEParameterSpec pbeParams) {
145180
// salt should be non-null per PBEParameterSpec
146-
salt = check(pbeParams.getSalt());
147-
iCount = check(pbeParams.getIterationCount());
148-
AlgorithmParameterSpec ivParams =
149-
pbeParams.getParameterSpec();
150-
if (ivParams instanceof IvParameterSpec iv) {
151-
ivSpec = iv;
152-
} else if (ivParams == null && doEncrypt) {
153-
// generate random IV
154-
byte[] ivBytes = new byte[blkSize];
155-
random.nextBytes(ivBytes);
156-
ivSpec = new IvParameterSpec(ivBytes);
157-
} else {
158-
throw new InvalidAlgorithmParameterException(
159-
"Wrong parameter type: IV expected");
160-
}
161-
} else if (params == null && doEncrypt) {
181+
iCountInit = check(pbeParams.getIterationCount());
182+
saltInit = check(pbeParams.getSalt());
183+
} else if (params == null) {
162184
// Try extracting from the key if present. If unspecified,
163-
// PBEKey returns null and 0 respectively.
185+
// PBEKey returns 0 and null respectively.
164186
if (key instanceof javax.crypto.interfaces.PBEKey pbeKey) {
165-
salt = check(pbeKey.getSalt());
166-
iCount = check(pbeKey.getIterationCount());
167-
}
168-
if (salt == null) {
169-
// generate random salt
170-
salt = new byte[DEFAULT_SALT_LENGTH];
171-
random.nextBytes(salt);
172-
}
173-
if (iCount == 0) {
174-
// use default iteration count
175-
iCount = DEFAULT_ITERATIONS;
187+
iCountInit = check(pbeKey.getIterationCount());
188+
saltInit = check(pbeKey.getSalt());
189+
} else {
190+
iCountInit = DEFAULT_ITERATIONS;
191+
saltInit = null;
176192
}
177-
// generate random IV
178-
byte[] ivBytes = new byte[blkSize];
179-
random.nextBytes(ivBytes);
180-
ivSpec = new IvParameterSpec(ivBytes);
181193
} else {
182194
throw new InvalidAlgorithmParameterException(
183195
"Wrong parameter type: PBE expected");
184196
}
197+
initialize(blkSize, opmode, iCountInit, saltInit, params,
198+
random);
185199
passwdChars = new char[passwdBytes.length];
186200
for (int i = 0; i < passwdChars.length; i++) {
187201
passwdChars[i] = (char) (passwdBytes[i] & 0x7f);
188202
}
189-
190203
pbeSpec = new PBEKeySpec(passwdChars, salt, iCount, keyLength);
191204
} finally {
192205
// password char[] was cloned in PBEKeySpec constructor,

src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,7 @@ private static abstract class P11PublicKey extends P11Key implements
500500
static final class P11PBEKey extends P11SecretKey
501501
implements PBEKey {
502502
private static final long serialVersionUID = 6847576994253634876L;
503-
private final char[] password;
503+
private char[] password;
504504
private final byte[] salt;
505505
private final int iterationCount;
506506
P11PBEKey(Session session, long keyID, String algorithm,
@@ -514,6 +514,9 @@ static final class P11PBEKey extends P11SecretKey
514514

515515
@Override
516516
public char[] getPassword() {
517+
if (password == null) {
518+
throw new IllegalStateException("password has been cleared");
519+
}
517520
return password.clone();
518521
}
519522

@@ -529,6 +532,7 @@ public int getIterationCount() {
529532

530533
void clearPassword() {
531534
Arrays.fill(password, '\0');
535+
password = null;
532536
}
533537
}
534538

src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PBECipher.java

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
import javax.crypto.NoSuchPaddingException;
4040
import javax.crypto.ShortBufferException;
4141
import javax.crypto.spec.PBEKeySpec;
42-
import javax.crypto.spec.PBEParameterSpec;
4342

4443
import sun.security.jca.JCAUtil;
4544
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
@@ -107,9 +106,8 @@ protected byte[] engineGetIV() {
107106
// see JCE spec
108107
@Override
109108
protected AlgorithmParameters engineGetParameters() {
110-
return pbes2Params.getAlgorithmParameters(
111-
blkSize, pbeAlg, P11Util.getSunJceProvider(),
112-
JCAUtil.getSecureRandom());
109+
return pbes2Params.getAlgorithmParameters(blkSize, pbeAlg,
110+
P11Util.getSunJceProvider(), JCAUtil.getSecureRandom());
113111
}
114112

115113
// see JCE spec
@@ -134,10 +132,18 @@ protected void engineInit(int opmode, Key key,
134132
// because this is a PBE Cipher service. In addition to checking the
135133
// key, check that params (if passed) are consistent.
136134
PBEUtil.checkKeyAndParams(key, params, pbeAlg);
137-
if (params instanceof PBEParameterSpec pbeParams) {
138-
// Reassign params to the underlying service params.
139-
params = pbeParams.getParameterSpec();
135+
// At this point, we know that the key is a P11PBEKey.
136+
P11Key.P11PBEKey p11PBEKey = (P11Key.P11PBEKey) key;
137+
// PBE services require a PBE key of the same algorithm and the
138+
// underlying service (non-PBE) won't check it.
139+
if (!pbeAlg.equals(p11PBEKey.getAlgorithm())) {
140+
throw new InvalidKeyException("Cannot use a " +
141+
p11PBEKey.getAlgorithm() + " key for a " + pbeAlg +
142+
" service");
140143
}
144+
pbes2Params.initialize(blkSize, opmode,
145+
p11PBEKey.getIterationCount(), p11PBEKey.getSalt(), params,
146+
random);
141147
} else {
142148
// If the key is not a P11Key, a derivation is needed. Data for
143149
// derivation has to be carried either as part of the key or params.
@@ -158,9 +164,8 @@ protected void engineInit(int opmode, Key key,
158164
} finally {
159165
pbeSpec.clearPassword();
160166
}
161-
params = pbes2Params.getIvSpec();
162167
}
163-
cipher.engineInit(opmode, key, params, random);
168+
cipher.engineInit(opmode, key, pbes2Params.getIvSpec(), random);
164169
}
165170

166171
// see JCE spec
@@ -209,9 +214,11 @@ protected int engineDoFinal(byte[] input, int inputOffset,
209214

210215
// see JCE spec
211216
@Override
212-
protected int engineGetKeySize(Key key)
213-
throws InvalidKeyException {
214-
return cipher.engineGetKeySize(key);
217+
protected int engineGetKeySize(Key key) {
218+
// It's guaranteed that when engineInit succeeds, the key length
219+
// for the underlying cipher is equal to the PBE service key length.
220+
// Otherwise, initialization fails.
221+
return svcPbeKi.keyLen;
215222
}
216223

217224
}

src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,7 @@ protected SecretKey engineGenerateSecret(KeySpec keySpec)
579579
}
580580
} else if (keySpec instanceof PBEKeySpec pbeKeySpec &&
581581
svcPbeKi != null) {
582-
return (SecretKey) derivePBEKey(token, pbeKeySpec, svcPbeKi);
582+
return derivePBEKey(token, pbeKeySpec, svcPbeKi);
583583
} else if (algorithm.equalsIgnoreCase("DES")) {
584584
if (keySpec instanceof DESKeySpec desKeySpec) {
585585
return generateDESSecret(desKeySpec.getKey(), "DES");

src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Util.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,7 @@
3131
import java.nio.CharBuffer;
3232
import java.nio.charset.Charset;
3333
import java.security.*;
34-
import java.util.Arrays;
3534

36-
import jdk.internal.access.SharedSecrets;
3735
import sun.security.pkcs11.wrapper.PKCS11Exception;
3836
import static sun.security.pkcs11.wrapper.PKCS11Exception.RV.*;
3937

@@ -90,6 +88,7 @@ static char[] encodePassword(char[] password, Charset cs,
9088
int i = 0;
9189
while (passwordBytes.hasRemaining()) {
9290
encPassword[i] = (char) (passwordBytes.get() & 0xFF);
91+
// Erase password bytes as we read during encoding.
9392
passwordBytes.put(i++, (byte) 0);
9493
}
9594
return encPassword;

src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
33
*/
44

55
/* Copyright (c) 2002 Graz University of Technology. All rights reserved.

0 commit comments

Comments
 (0)