Skip to content

Commit 68cf65d

Browse files
author
Valerie Peng
committed
8023980: JCE doesn't provide any class to handle RSA private key in PKCS#1
Reviewed-by: weijun
1 parent 5dc5d94 commit 68cf65d

File tree

6 files changed

+407
-159
lines changed

6 files changed

+407
-159
lines changed

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

Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2021, 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
@@ -43,13 +43,15 @@
4343
* between the following:
4444
*
4545
* For public keys:
46-
* . PublicKey with an X.509 encoding
46+
* . RSA PublicKey with an X.509 encoding
47+
* . RSA PublicKey with an PKCS#1 encoding
4748
* . RSAPublicKey
4849
* . RSAPublicKeySpec
4950
* . X509EncodedKeySpec
5051
*
5152
* For private keys:
52-
* . PrivateKey with a PKCS#8 encoding
53+
* . RSA PrivateKey with a PKCS#8 encoding
54+
* . RSA PrivateKey with a PKCS#1 encoding
5355
* . RSAPrivateKey
5456
* . RSAPrivateCrtKey
5557
* . RSAPrivateKeySpec
@@ -95,8 +97,8 @@ static RSAKeyFactory getInstance(KeyType type) {
9597
return new RSAKeyFactory(type);
9698
}
9799

98-
// Internal utility method for checking key algorithm
99-
private static void checkKeyAlgo(Key key, String expectedAlg)
100+
// pkg-private utility method for checking key algorithm
101+
static void checkKeyAlgo(Key key, String expectedAlg)
100102
throws InvalidKeyException {
101103
String keyAlg = key.getAlgorithm();
102104
if (keyAlg == null || !(keyAlg.equalsIgnoreCase(expectedAlg))) {
@@ -265,14 +267,10 @@ private PublicKey translatePublicKey(PublicKey key)
265267
// catch providers that incorrectly implement RSAPublicKey
266268
throw new InvalidKeyException("Invalid key", e);
267269
}
268-
} else if ("X.509".equals(key.getFormat())) {
269-
RSAPublicKey translated = new RSAPublicKeyImpl(key.getEncoded());
270-
// ensure the key algorithm matches the current KeyFactory instance
271-
checkKeyAlgo(translated, type.keyAlgo);
272-
return translated;
273270
} else {
274-
throw new InvalidKeyException("Public keys must be instance "
275-
+ "of RSAPublicKey or have X.509 encoding");
271+
// create new key based on the format and encoding of current 'key'
272+
return RSAPublicKeyImpl.newKey(type, key.getFormat(),
273+
key.getEncoded());
276274
}
277275
}
278276

@@ -309,27 +307,18 @@ private PrivateKey translatePrivateKey(PrivateKey key)
309307
// catch providers that incorrectly implement RSAPrivateKey
310308
throw new InvalidKeyException("Invalid key", e);
311309
}
312-
} else if ("PKCS#8".equals(key.getFormat())) {
313-
RSAPrivateKey translated =
314-
RSAPrivateCrtKeyImpl.newKey(key.getEncoded());
315-
// ensure the key algorithm matches the current KeyFactory instance
316-
checkKeyAlgo(translated, type.keyAlgo);
317-
return translated;
318310
} else {
319-
throw new InvalidKeyException("Private keys must be instance "
320-
+ "of RSAPrivate(Crt)Key or have PKCS#8 encoding");
311+
return RSAPrivateCrtKeyImpl.newKey(type, key.getFormat(),
312+
key.getEncoded());
321313
}
322314
}
323315

324316
// internal implementation of generatePublic. See JCA doc
325317
private PublicKey generatePublic(KeySpec keySpec)
326318
throws GeneralSecurityException {
327319
if (keySpec instanceof X509EncodedKeySpec) {
328-
X509EncodedKeySpec x509Spec = (X509EncodedKeySpec)keySpec;
329-
RSAPublicKey generated = new RSAPublicKeyImpl(x509Spec.getEncoded());
330-
// ensure the key algorithm matches the current KeyFactory instance
331-
checkKeyAlgo(generated, type.keyAlgo);
332-
return generated;
320+
return RSAPublicKeyImpl.newKey(type, "X.509",
321+
((X509EncodedKeySpec)keySpec).getEncoded());
333322
} else if (keySpec instanceof RSAPublicKeySpec) {
334323
RSAPublicKeySpec rsaSpec = (RSAPublicKeySpec)keySpec;
335324
try {
@@ -351,11 +340,8 @@ private PublicKey generatePublic(KeySpec keySpec)
351340
private PrivateKey generatePrivate(KeySpec keySpec)
352341
throws GeneralSecurityException {
353342
if (keySpec instanceof PKCS8EncodedKeySpec) {
354-
PKCS8EncodedKeySpec pkcsSpec = (PKCS8EncodedKeySpec)keySpec;
355-
RSAPrivateKey generated = RSAPrivateCrtKeyImpl.newKey(pkcsSpec.getEncoded());
356-
// ensure the key algorithm matches the current KeyFactory instance
357-
checkKeyAlgo(generated, type.keyAlgo);
358-
return generated;
343+
return RSAPrivateCrtKeyImpl.newKey(type, "PKCS#8",
344+
((PKCS8EncodedKeySpec)keySpec).getEncoded());
359345
} else if (keySpec instanceof RSAPrivateCrtKeySpec) {
360346
RSAPrivateCrtKeySpec rsaSpec = (RSAPrivateCrtKeySpec)keySpec;
361347
try {
@@ -395,7 +381,8 @@ protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
395381
try {
396382
// convert key to one of our keys
397383
// this also verifies that the key is a valid RSA key and ensures
398-
// that the encoding is X.509/PKCS#8 for public/private keys
384+
// that the encoding is X.509/PKCS#8 or PKCS#1 for public/private
385+
// keys
399386
key = engineTranslateKey(key);
400387
} catch (InvalidKeyException e) {
401388
throw new InvalidKeySpecException(e);

src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java

Lines changed: 83 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2021, 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
@@ -74,30 +74,52 @@ public final class RSAPrivateCrtKeyImpl
7474
private transient AlgorithmParameterSpec keyParams;
7575

7676
/**
77-
* Generate a new key from its encoding. Returns a CRT key if possible
78-
* and a non-CRT key otherwise. Used by RSAKeyFactory.
77+
* Generate a new RSAPrivate(Crt)Key from the specified type,
78+
* format and encoding. Returns a CRT key if possible and a non-CRT
79+
* key otherwise.
80+
* Also used by SunPKCS11 provider.
7981
*/
80-
public static RSAPrivateKey newKey(byte[] encoded)
81-
throws InvalidKeyException {
82+
public static RSAPrivateKey newKey(KeyType type, String format,
83+
byte[] encoded) throws InvalidKeyException {
8284
if (encoded == null || encoded.length == 0) {
8385
throw new InvalidKeyException("Missing key encoding");
8486
}
85-
RSAPrivateCrtKeyImpl key = new RSAPrivateCrtKeyImpl(encoded);
86-
// check all CRT-specific components are available, if any one
87-
// missing, return a non-CRT key instead
88-
if ((key.getPublicExponent().signum() == 0) ||
89-
(key.getPrimeExponentP().signum() == 0) ||
90-
(key.getPrimeExponentQ().signum() == 0) ||
91-
(key.getPrimeP().signum() == 0) ||
92-
(key.getPrimeQ().signum() == 0) ||
93-
(key.getCrtCoefficient().signum() == 0)) {
94-
return new RSAPrivateKeyImpl(
95-
key.type, key.keyParams,
96-
key.getModulus(),
97-
key.getPrivateExponent()
98-
);
99-
} else {
100-
return key;
87+
switch (format) {
88+
case "PKCS#8":
89+
RSAPrivateCrtKeyImpl key = new RSAPrivateCrtKeyImpl(encoded);
90+
RSAKeyFactory.checkKeyAlgo(key, type.keyAlgo);
91+
// check all CRT-specific components are available, if any one
92+
// missing, return a non-CRT key instead
93+
if ((key.getPublicExponent().signum() == 0) ||
94+
(key.getPrimeExponentP().signum() == 0) ||
95+
(key.getPrimeExponentQ().signum() == 0) ||
96+
(key.getPrimeP().signum() == 0) ||
97+
(key.getPrimeQ().signum() == 0) ||
98+
(key.getCrtCoefficient().signum() == 0)) {
99+
return new RSAPrivateKeyImpl(key.type, key.keyParams,
100+
key.getModulus(), key.getPrivateExponent());
101+
} else {
102+
return key;
103+
}
104+
case "PKCS#1":
105+
try {
106+
BigInteger[] comps = parseASN1(encoded);
107+
if ((comps[1].signum() == 0) || (comps[3].signum() == 0) ||
108+
(comps[4].signum() == 0) || (comps[5].signum() == 0) ||
109+
(comps[6].signum() == 0) || (comps[7].signum() == 0)) {
110+
return new RSAPrivateKeyImpl(type, null, comps[0],
111+
comps[2]);
112+
} else {
113+
return new RSAPrivateCrtKeyImpl(type, null, comps[0],
114+
comps[1], comps[2], comps[3], comps[4], comps[5],
115+
comps[6], comps[7]);
116+
}
117+
} catch (IOException ioe) {
118+
throw new InvalidKeyException("Invalid PKCS#1 encoding", ioe);
119+
}
120+
default:
121+
throw new InvalidKeyException("Unsupported RSA Private(Crt)Key "
122+
+ "format: " + format);
101123
}
102124
}
103125

@@ -126,7 +148,7 @@ public static RSAPrivateKey newKey(KeyType type,
126148
/**
127149
* Construct a key from its encoding. Called from newKey above.
128150
*/
129-
RSAPrivateCrtKeyImpl(byte[] encoded) throws InvalidKeyException {
151+
private RSAPrivateCrtKeyImpl(byte[] encoded) throws InvalidKeyException {
130152
super(encoded);
131153
parseKeyBits();
132154
RSAKeyFactory.checkRSAProviderKeyLengths(n.bitLength(), e);
@@ -258,37 +280,47 @@ public String toString() {
258280
+ "\n modulus: " + n + "\n private exponent: " + d;
259281
}
260282

283+
// utility method for parsing DER encoding of RSA private keys in PKCS#1
284+
// format as defined in RFC 8017 Appendix A.1.2, i.e. SEQ of version, n,
285+
// e, d, p, q, pe, qe, and coeff, and return the parsed components.
286+
private static BigInteger[] parseASN1(byte[] raw) throws IOException {
287+
DerValue derValue = new DerValue(raw);
288+
if (derValue.tag != DerValue.tag_Sequence) {
289+
throw new IOException("Not a SEQUENCE");
290+
}
291+
int version = derValue.data.getInteger();
292+
if (version != 0) {
293+
throw new IOException("Version must be 0");
294+
}
295+
296+
BigInteger[] result = new BigInteger[8]; // n, e, d, p, q, pe, qe, coeff
297+
/*
298+
* Some implementations do not correctly encode ASN.1 INTEGER values
299+
* in 2's complement format, resulting in a negative integer when
300+
* decoded. Correct the error by converting it to a positive integer.
301+
*
302+
* See CR 6255949
303+
*/
304+
for (int i = 0; i < result.length; i++) {
305+
result[i] = derValue.data.getPositiveBigInteger();
306+
}
307+
if (derValue.data.available() != 0) {
308+
throw new IOException("Extra data available");
309+
}
310+
return result;
311+
}
312+
261313
private void parseKeyBits() throws InvalidKeyException {
262314
try {
263-
DerInputStream in = new DerInputStream(key);
264-
DerValue derValue = in.getDerValue();
265-
if (derValue.tag != DerValue.tag_Sequence) {
266-
throw new IOException("Not a SEQUENCE");
267-
}
268-
DerInputStream data = derValue.data;
269-
int version = data.getInteger();
270-
if (version != 0) {
271-
throw new IOException("Version must be 0");
272-
}
273-
274-
/*
275-
* Some implementations do not correctly encode ASN.1 INTEGER values
276-
* in 2's complement format, resulting in a negative integer when
277-
* decoded. Correct the error by converting it to a positive integer.
278-
*
279-
* See CR 6255949
280-
*/
281-
n = data.getPositiveBigInteger();
282-
e = data.getPositiveBigInteger();
283-
d = data.getPositiveBigInteger();
284-
p = data.getPositiveBigInteger();
285-
q = data.getPositiveBigInteger();
286-
pe = data.getPositiveBigInteger();
287-
qe = data.getPositiveBigInteger();
288-
coeff = data.getPositiveBigInteger();
289-
if (derValue.data.available() != 0) {
290-
throw new IOException("Extra data available");
291-
}
315+
BigInteger[] comps = parseASN1(key);
316+
n = comps[0];
317+
e = comps[1];
318+
d = comps[2];
319+
p = comps[3];
320+
q = comps[4];
321+
pe = comps[5];
322+
qe = comps[6];
323+
coeff = comps[7];
292324
} catch (IOException e) {
293325
throw new InvalidKeyException("Invalid RSA private key", e);
294326
}

src/java.base/share/classes/sun/security/rsa/RSAPublicKeyImpl.java

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2021, 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
@@ -66,17 +66,36 @@ public final class RSAPublicKeyImpl extends X509Key implements RSAPublicKey {
6666
private transient AlgorithmParameterSpec keyParams;
6767

6868
/**
69-
* Generate a new RSAPublicKey from the specified encoding.
70-
* Used by SunPKCS11 provider.
69+
* Generate a new RSAPublicKey from the specified type, format, and
70+
* encoding.
71+
* Also used by SunPKCS11 provider.
7172
*/
72-
public static RSAPublicKey newKey(byte[] encoded)
73-
throws InvalidKeyException {
74-
return new RSAPublicKeyImpl(encoded);
73+
public static RSAPublicKey newKey(KeyType type, String format,
74+
byte[] encoded) throws InvalidKeyException {
75+
RSAPublicKey key;
76+
switch (format) {
77+
case "X.509":
78+
key = new RSAPublicKeyImpl(encoded);
79+
RSAKeyFactory.checkKeyAlgo(key, type.keyAlgo);
80+
break;
81+
case "PKCS#1":
82+
try {
83+
BigInteger[] comps = parseASN1(encoded);
84+
key = new RSAPublicKeyImpl(type, null, comps[0], comps[1]);
85+
} catch (IOException ioe) {
86+
throw new InvalidKeyException("Invalid PKCS#1 encoding", ioe);
87+
}
88+
break;
89+
default:
90+
throw new InvalidKeyException("Unsupported RSA PublicKey format: " +
91+
format);
92+
}
93+
return key;
7594
}
7695

7796
/**
7897
* Generate a new RSAPublicKey from the specified type and components.
79-
* Used by SunPKCS11 provider.
98+
* Also used by SunPKCS11 provider.
8099
*/
81100
public static RSAPublicKey newKey(KeyType type,
82101
AlgorithmParameterSpec params, BigInteger n, BigInteger e)
@@ -123,9 +142,9 @@ public static RSAPublicKey newKey(KeyType type,
123142
}
124143

125144
/**
126-
* Construct a key from its encoding. Used by RSAKeyFactory.
145+
* Construct a key from its encoding.
127146
*/
128-
RSAPublicKeyImpl(byte[] encoded) throws InvalidKeyException {
147+
private RSAPublicKeyImpl(byte[] encoded) throws InvalidKeyException {
129148
if (encoded == null || encoded.length == 0) {
130149
throw new InvalidKeyException("Missing key encoding");
131150
}
@@ -181,22 +200,30 @@ public AlgorithmParameterSpec getParams() {
181200
return keyParams;
182201
}
183202

203+
// utility method for parsing DER encoding of RSA public keys in PKCS#1
204+
// format as defined in RFC 8017 Appendix A.1.1, i.e. SEQ of n and e.
205+
private static BigInteger[] parseASN1(byte[] raw) throws IOException {
206+
DerValue derValue = new DerValue(raw);
207+
if (derValue.tag != DerValue.tag_Sequence) {
208+
throw new IOException("Not a SEQUENCE");
209+
}
210+
BigInteger[] result = new BigInteger[2]; // n, e
211+
result[0] = derValue.data.getPositiveBigInteger();
212+
result[1] = derValue.data.getPositiveBigInteger();
213+
if (derValue.data.available() != 0) {
214+
throw new IOException("Extra data available");
215+
}
216+
return result;
217+
}
218+
184219
/**
185220
* Parse the key. Called by X509Key.
186221
*/
187222
protected void parseKeyBits() throws InvalidKeyException {
188223
try {
189-
DerInputStream in = new DerInputStream(getKey().toByteArray());
190-
DerValue derValue = in.getDerValue();
191-
if (derValue.tag != DerValue.tag_Sequence) {
192-
throw new IOException("Not a SEQUENCE");
193-
}
194-
DerInputStream data = derValue.data;
195-
n = data.getPositiveBigInteger();
196-
e = data.getPositiveBigInteger();
197-
if (derValue.data.available() != 0) {
198-
throw new IOException("Extra data available");
199-
}
224+
BigInteger[] comps = parseASN1(getKey().toByteArray());
225+
n = comps[0];
226+
e = comps[1];
200227
} catch (IOException e) {
201228
throw new InvalidKeyException("Invalid RSA public key", e);
202229
}

0 commit comments

Comments
 (0)