Skip to content

Commit 68d3ed5

Browse files
committed
8296442: EncryptedPrivateKeyInfo can be created with an uninitialized AlgorithmParameters
Reviewed-by: xuelei, kdriver, mullan
1 parent 37848a9 commit 68d3ed5

File tree

6 files changed

+128
-72
lines changed

6 files changed

+128
-72
lines changed

src/java.base/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,20 @@
5757

5858
public class EncryptedPrivateKeyInfo {
5959

60-
// the "encryptionAlgorithm" field
60+
// The "encryptionAlgorithm" is stored in either the algid or
61+
// the params field. Precisely, if this object is created by
62+
// {@link #EncryptedPrivateKeyInfo(AlgorithmParameters, byte[])}
63+
// with an uninitialized AlgorithmParameters, the AlgorithmParameters
64+
// object is stored in the params field and algid is set to null.
65+
// In all other cases, algid is non null and params is null.
6166
private final AlgorithmId algid;
62-
63-
// the algorithm name of the encrypted private key
64-
private String keyAlg;
67+
private final AlgorithmParameters params;
6568

6669
// the "encryptedData" field
6770
private final byte[] encryptedData;
6871

6972
// the ASN.1 encoded contents of this class
70-
private byte[] encoded;
73+
private final byte[] encoded;
7174

7275
/**
7376
* Constructs (i.e., parses) an {@code EncryptedPrivateKeyInfo} from
@@ -100,6 +103,7 @@ public EncryptedPrivateKeyInfo(byte[] encoded) throws IOException {
100103
}
101104

102105
this.algid = AlgorithmId.parse(seq[0]);
106+
this.params = null;
103107
if (seq[0].data.available() != 0) {
104108
throw new IOException("encryptionAlgorithm field overrun");
105109
}
@@ -141,6 +145,7 @@ public EncryptedPrivateKeyInfo(String algName, byte[] encryptedData)
141145
throw new NullPointerException("the algName parameter " +
142146
"must be non-null");
143147
this.algid = AlgorithmId.get(algName);
148+
this.params = null;
144149

145150
if (encryptedData == null) {
146151
throw new NullPointerException("the encryptedData " +
@@ -181,7 +186,22 @@ public EncryptedPrivateKeyInfo(AlgorithmParameters algParams,
181186
if (algParams == null) {
182187
throw new NullPointerException("algParams must be non-null");
183188
}
184-
this.algid = AlgorithmId.get(algParams);
189+
AlgorithmId tmp;
190+
try {
191+
tmp = AlgorithmId.get(algParams);
192+
} catch (IllegalStateException e) {
193+
// This exception is thrown when algParams.getEncoded() fails.
194+
// While the spec of this constructor requires that
195+
// "getEncoded should return...", in reality people might
196+
// create with an uninitialized algParams first and only
197+
// initialize it before calling getEncoded(). Thus we support
198+
// this case as well.
199+
tmp = null;
200+
}
201+
202+
// one and only one is non null
203+
this.algid = tmp;
204+
this.params = this.algid != null ? null : algParams;
185205

186206
if (encryptedData == null) {
187207
throw new NullPointerException("encryptedData must be non-null");
@@ -197,7 +217,6 @@ public EncryptedPrivateKeyInfo(AlgorithmParameters algParams,
197217
this.encoded = null;
198218
}
199219

200-
201220
/**
202221
* Returns the encryption algorithm.
203222
* <p>Note: Standard name is returned instead of the specified one
@@ -209,15 +228,15 @@ public EncryptedPrivateKeyInfo(AlgorithmParameters algParams,
209228
* @return the encryption algorithm name.
210229
*/
211230
public String getAlgName() {
212-
return this.algid.getName();
231+
return algid == null ? params.getAlgorithm() : algid.getName();
213232
}
214233

215234
/**
216235
* Returns the algorithm parameters used by the encryption algorithm.
217236
* @return the algorithm parameters.
218237
*/
219238
public AlgorithmParameters getAlgParameters() {
220-
return this.algid.getParameters();
239+
return algid == null ? params : algid.getParameters();
221240
}
222241

223242
/**
@@ -252,14 +271,13 @@ public PKCS8EncodedKeySpec getKeySpec(Cipher cipher)
252271
byte[] encoded;
253272
try {
254273
encoded = cipher.doFinal(encryptedData);
255-
checkPKCS8Encoding(encoded);
274+
return pkcs8EncodingToSpec(encoded);
256275
} catch (GeneralSecurityException |
257276
IOException |
258277
IllegalStateException ex) {
259278
throw new InvalidKeySpecException(
260279
"Cannot retrieve the PKCS8EncodedKeySpec", ex);
261280
}
262-
return new PKCS8EncodedKeySpec(encoded, keyAlg);
263281
}
264282

265283
private PKCS8EncodedKeySpec getKeySpecImpl(Key decryptKey,
@@ -270,21 +288,20 @@ private PKCS8EncodedKeySpec getKeySpecImpl(Key decryptKey,
270288
try {
271289
if (provider == null) {
272290
// use the most preferred one
273-
c = Cipher.getInstance(algid.getName());
291+
c = Cipher.getInstance(getAlgName());
274292
} else {
275-
c = Cipher.getInstance(algid.getName(), provider);
293+
c = Cipher.getInstance(getAlgName(), provider);
276294
}
277-
c.init(Cipher.DECRYPT_MODE, decryptKey, algid.getParameters());
295+
c.init(Cipher.DECRYPT_MODE, decryptKey, getAlgParameters());
278296
encoded = c.doFinal(encryptedData);
279-
checkPKCS8Encoding(encoded);
297+
return pkcs8EncodingToSpec(encoded);
280298
} catch (NoSuchAlgorithmException nsae) {
281299
// rethrow
282300
throw nsae;
283301
} catch (GeneralSecurityException | IOException ex) {
284302
throw new InvalidKeyException(
285303
"Cannot retrieve the PKCS8EncodedKeySpec", ex);
286304
}
287-
return new PKCS8EncodedKeySpec(encoded, keyAlg);
288305
}
289306

290307
/**
@@ -388,14 +405,23 @@ public byte[] getEncoded() throws IOException {
388405
DerOutputStream tmp = new DerOutputStream();
389406

390407
// encode encryption algorithm
391-
algid.encode(tmp);
408+
if (algid != null) {
409+
algid.encode(tmp);
410+
} else {
411+
try {
412+
// Let's hope params has been initialized by now.
413+
AlgorithmId.get(params).encode(tmp);
414+
} catch (Exception e) {
415+
throw new IOException("not initialized", e);
416+
}
417+
}
392418

393419
// encode encrypted data
394420
tmp.putOctetString(encryptedData);
395421

396422
// wrap everything into a SEQUENCE
397423
out.write(DerValue.tag_Sequence, tmp);
398-
this.encoded = out.toByteArray();
424+
return out.toByteArray();
399425
}
400426
return this.encoded.clone();
401427
}
@@ -409,7 +435,7 @@ private static void checkTag(DerValue val, byte tag, String valName)
409435
}
410436

411437
@SuppressWarnings("fallthrough")
412-
private void checkPKCS8Encoding(byte[] encodedKey)
438+
private static PKCS8EncodedKeySpec pkcs8EncodingToSpec(byte[] encodedKey)
413439
throws IOException {
414440
DerInputStream in = new DerInputStream(encodedKey);
415441
DerValue[] values = in.getSequence(3);
@@ -420,9 +446,9 @@ private void checkPKCS8Encoding(byte[] encodedKey)
420446
/* fall through */
421447
case 3:
422448
checkTag(values[0], DerValue.tag_Integer, "version");
423-
keyAlg = AlgorithmId.parse(values[1]).getName();
449+
String keyAlg = AlgorithmId.parse(values[1]).getName();
424450
checkTag(values[2], DerValue.tag_OctetString, "privateKey");
425-
break;
451+
return new PKCS8EncodedKeySpec(encodedKey, keyAlg);
426452
default:
427453
throw new IOException("invalid key encoding");
428454
}

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

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -116,36 +116,6 @@ class MacData {
116116

117117
}
118118

119-
MacData(AlgorithmParameters algParams, byte[] digest,
120-
byte[] salt, int iterations) throws NoSuchAlgorithmException
121-
{
122-
if (algParams == null)
123-
throw new NullPointerException("the algParams parameter " +
124-
"must be non-null");
125-
126-
AlgorithmId algid = AlgorithmId.get(algParams);
127-
this.digestAlgorithmName = algid.getName();
128-
this.digestAlgorithmParams = algid.getParameters();
129-
130-
if (digest == null) {
131-
throw new NullPointerException("the digest " +
132-
"parameter must be non-null");
133-
} else if (digest.length == 0) {
134-
throw new IllegalArgumentException("the digest " +
135-
"parameter must not be empty");
136-
} else {
137-
this.digest = digest.clone();
138-
}
139-
140-
this.macSalt = salt;
141-
this.iterations = iterations;
142-
143-
// delay the generation of ASN.1 encoding until
144-
// getEncoded() is called
145-
this.encoded = null;
146-
147-
}
148-
149119
String getDigestAlgName() {
150120
return digestAlgorithmName;
151121
}

src/java.base/share/classes/sun/security/x509/AlgorithmId.java

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ public AlgorithmId(ObjectIdentifier oid) {
100100
*
101101
* @param oid the identifier for the algorithm.
102102
* @param algparams the associated algorithm parameters, can be null.
103+
* @exception IllegalStateException if algparams is not initialized
104+
* or cannot be encoded
103105
*/
104106
public AlgorithmId(ObjectIdentifier oid, AlgorithmParameters algparams) {
105107
algid = oid;
@@ -108,9 +110,9 @@ public AlgorithmId(ObjectIdentifier oid, AlgorithmParameters algparams) {
108110
try {
109111
encodedParams = algParams.getEncoded();
110112
} catch (IOException ioe) {
111-
// Ignore this at the moment. This exception can occur
112-
// if AlgorithmParameters was not initialized yet. Will
113-
// try to re-getEncoded() again later.
113+
throw new IllegalStateException(
114+
"AlgorithmParameters not initialized or cannot be decoded",
115+
ioe);
114116
}
115117
}
116118
}
@@ -157,17 +159,11 @@ protected void decodeParams() throws IOException {
157159
* @exception IOException on encoding error.
158160
*/
159161
@Override
160-
public void encode (DerOutputStream out) throws IOException {
162+
public void encode(DerOutputStream out) throws IOException {
161163
DerOutputStream bytes = new DerOutputStream();
162164

163165
bytes.putOID(algid);
164166

165-
// Re-getEncoded() from algParams if it was not initialized
166-
if (algParams != null && encodedParams == null) {
167-
encodedParams = algParams.getEncoded();
168-
// If still not initialized. Let the IOE be thrown.
169-
}
170-
171167
if (encodedParams == null) {
172168
// Changes backed out for compatibility with Solaris
173169

@@ -488,6 +484,8 @@ public static AlgorithmId get(String algname)
488484
*
489485
* @param algparams the associated algorithm parameters.
490486
* @exception NoSuchAlgorithmException on error.
487+
* @exception IllegalStateException if algparams is not initialized
488+
* or cannot be encoded
491489
*/
492490
public static AlgorithmId get(AlgorithmParameters algparams)
493491
throws NoSuchAlgorithmException {

test/jdk/javax/crypto/EncryptedPrivateKeyInfo/GetAlgName.java

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2022, 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,16 +23,11 @@
2323

2424
/**
2525
* @test
26-
* @bug 4941596
26+
* @bug 4941596 8296442
2727
* @summary Test the EncryptedPrivateKeyInfo.getAlgName(...) methods.
2828
* @author Valerie Peng
2929
*/
30-
import java.util.*;
31-
import java.nio.*;
32-
import java.io.*;
3330
import java.security.*;
34-
import java.util.Arrays;
35-
import java.security.spec.*;
3631
import javax.crypto.*;
3732
import javax.crypto.spec.*;
3833

@@ -61,8 +56,18 @@ public static void main(String[] argv) throws Exception {
6156
AlgorithmParameters ap = c.getParameters();
6257
epki = new EncryptedPrivateKeyInfo(ap, BYTES);
6358
if (!epki.getAlgName().equalsIgnoreCase(algo)) {
64-
System.out.println("...expect: " + algo);
65-
System.out.println("...got: " + epki.getAlgName());
59+
System.out.println("...expected: " + algo);
60+
System.out.println(" ...got: " + epki.getAlgName());
61+
status = false;
62+
}
63+
64+
// Make sure EncryptedPrivateKeyInfo can be created with an
65+
// uninitialized AlgorithmParameters.
66+
AlgorithmParameters ap2 = AlgorithmParameters.getInstance(ap.getAlgorithm());
67+
epki = new EncryptedPrivateKeyInfo(ap2, BYTES);
68+
if (!epki.getAlgName().equalsIgnoreCase(algo)) {
69+
System.out.println("...expected: " + algo);
70+
System.out.println(" ...got: " + epki.getAlgName());
6671
status = false;
6772
}
6873
}

test/jdk/javax/crypto/EncryptedPrivateKeyInfo/GetEncoded.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2022, 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,7 +23,7 @@
2323

2424
/**
2525
* @test
26-
* @bug 8261779
26+
* @bug 8261779 8296442
2727
* @summary Check that EncryptedPrivateKeyInfo.getEncoded() calls
2828
* AlgorithmParameters.getEncoded() when first called
2929
*/
@@ -56,6 +56,14 @@ public static void main(String[] argv) throws Exception {
5656
AlgorithmParameters ap1 = AlgorithmParameters.getInstance("EC");
5757
EncryptedPrivateKeyInfo epki1 =
5858
new EncryptedPrivateKeyInfo(ap1, new byte[] {1, 2, 3, 4});
59+
60+
try {
61+
epki1.getEncoded();
62+
throw new Exception("Should have thrown IOException");
63+
} catch (IOException ioe) {
64+
// test passed, expected exception
65+
}
66+
5967
ap1.init(new ECGenParameterSpec("secp256r1"));
6068

6169
EncryptedPrivateKeyInfo epki2 =
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (c) 2022, 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 8296442
27+
* @summary EncryptedPrivateKeyInfo can be created with an uninitialized AlgorithmParameters
28+
* @modules java.base/sun.security.x509
29+
*/
30+
31+
import sun.security.x509.AlgorithmId;
32+
33+
import java.security.AlgorithmParameters;
34+
35+
public class Uninitialized {
36+
public static void main(String[] args) throws Exception {
37+
AlgorithmParameters ap = AlgorithmParameters.getInstance("EC");
38+
boolean success;
39+
try {
40+
AlgorithmId.get(ap);
41+
success = true;
42+
} catch (Exception e) {
43+
success = false;
44+
}
45+
if (success) {
46+
throw new RuntimeException();
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)