Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 18 additions & 31 deletions src/java.base/share/classes/sun/security/rsa/RSAKeyFactory.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -43,13 +43,15 @@
* between the following:
*
* For public keys:
* . PublicKey with an X.509 encoding
* . RSA PublicKey with an X.509 encoding
* . RSA PublicKey with an PKCS#1 encoding
* . RSAPublicKey
* . RSAPublicKeySpec
* . X509EncodedKeySpec
*
* For private keys:
* . PrivateKey with a PKCS#8 encoding
* . RSA PrivateKey with a PKCS#8 encoding
* . RSA PrivateKey with a PKCS#1 encoding
* . RSAPrivateKey
* . RSAPrivateCrtKey
* . RSAPrivateKeySpec
Expand Down Expand Up @@ -95,8 +97,8 @@ static RSAKeyFactory getInstance(KeyType type) {
return new RSAKeyFactory(type);
}

// Internal utility method for checking key algorithm
private static void checkKeyAlgo(Key key, String expectedAlg)
// pkg-private utility method for checking key algorithm
static void checkKeyAlgo(Key key, String expectedAlg)
throws InvalidKeyException {
String keyAlg = key.getAlgorithm();
if (keyAlg == null || !(keyAlg.equalsIgnoreCase(expectedAlg))) {
Expand Down Expand Up @@ -265,14 +267,10 @@ private PublicKey translatePublicKey(PublicKey key)
// catch providers that incorrectly implement RSAPublicKey
throw new InvalidKeyException("Invalid key", e);
}
} else if ("X.509".equals(key.getFormat())) {
RSAPublicKey translated = new RSAPublicKeyImpl(key.getEncoded());
// ensure the key algorithm matches the current KeyFactory instance
checkKeyAlgo(translated, type.keyAlgo);
return translated;
} else {
throw new InvalidKeyException("Public keys must be instance "
+ "of RSAPublicKey or have X.509 encoding");
// create new key based on the format and encoding of current 'key'
return RSAPublicKeyImpl.newKey(type, key.getFormat(),
key.getEncoded());
}
}

Expand Down Expand Up @@ -309,27 +307,18 @@ private PrivateKey translatePrivateKey(PrivateKey key)
// catch providers that incorrectly implement RSAPrivateKey
throw new InvalidKeyException("Invalid key", e);
}
} else if ("PKCS#8".equals(key.getFormat())) {
RSAPrivateKey translated =
RSAPrivateCrtKeyImpl.newKey(key.getEncoded());
// ensure the key algorithm matches the current KeyFactory instance
checkKeyAlgo(translated, type.keyAlgo);
return translated;
} else {
throw new InvalidKeyException("Private keys must be instance "
+ "of RSAPrivate(Crt)Key or have PKCS#8 encoding");
return RSAPrivateCrtKeyImpl.newKey(type, key.getFormat(),
key.getEncoded());
}
}

// internal implementation of generatePublic. See JCA doc
private PublicKey generatePublic(KeySpec keySpec)
throws GeneralSecurityException {
if (keySpec instanceof X509EncodedKeySpec) {
X509EncodedKeySpec x509Spec = (X509EncodedKeySpec)keySpec;
RSAPublicKey generated = new RSAPublicKeyImpl(x509Spec.getEncoded());
// ensure the key algorithm matches the current KeyFactory instance
checkKeyAlgo(generated, type.keyAlgo);
return generated;
return RSAPublicKeyImpl.newKey(type, "X.509",
((X509EncodedKeySpec)keySpec).getEncoded());
} else if (keySpec instanceof RSAPublicKeySpec) {
RSAPublicKeySpec rsaSpec = (RSAPublicKeySpec)keySpec;
try {
Expand All @@ -351,11 +340,8 @@ private PublicKey generatePublic(KeySpec keySpec)
private PrivateKey generatePrivate(KeySpec keySpec)
throws GeneralSecurityException {
if (keySpec instanceof PKCS8EncodedKeySpec) {
PKCS8EncodedKeySpec pkcsSpec = (PKCS8EncodedKeySpec)keySpec;
RSAPrivateKey generated = RSAPrivateCrtKeyImpl.newKey(pkcsSpec.getEncoded());
// ensure the key algorithm matches the current KeyFactory instance
checkKeyAlgo(generated, type.keyAlgo);
return generated;
return RSAPrivateCrtKeyImpl.newKey(type, "PKCS#8",
((PKCS8EncodedKeySpec)keySpec).getEncoded());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will you clean up the getEncoded() output or shall I?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's better that you do it this time? Just so that the backport won't miss it.

Copy link
Contributor Author

@valeriepeng valeriepeng Jan 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or if you integrated before me, I will manually merge the changes and clean up the getEncoded() also.

} else if (keySpec instanceof RSAPrivateCrtKeySpec) {
RSAPrivateCrtKeySpec rsaSpec = (RSAPrivateCrtKeySpec)keySpec;
try {
Expand Down Expand Up @@ -395,7 +381,8 @@ protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
try {
// convert key to one of our keys
// this also verifies that the key is a valid RSA key and ensures
// that the encoding is X.509/PKCS#8 for public/private keys
// that the encoding is X.509/PKCS#8 or PKCS#1 for public/private
// keys
key = engineTranslateKey(key);
} catch (InvalidKeyException e) {
throw new InvalidKeySpecException(e);
Expand Down
134 changes: 83 additions & 51 deletions src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -74,30 +74,52 @@ public final class RSAPrivateCrtKeyImpl
private transient AlgorithmParameterSpec keyParams;

/**
* Generate a new key from its encoding. Returns a CRT key if possible
* and a non-CRT key otherwise. Used by RSAKeyFactory.
* Generate a new RSAPrivate(Crt)Key from the specified type,
* format and encoding. Returns a CRT key if possible and a non-CRT
* key otherwise.
* Also used by SunPKCS11 provider.
*/
public static RSAPrivateKey newKey(byte[] encoded)
throws InvalidKeyException {
public static RSAPrivateKey newKey(KeyType type, String format,
byte[] encoded) throws InvalidKeyException {
if (encoded == null || encoded.length == 0) {
throw new InvalidKeyException("Missing key encoding");
}
RSAPrivateCrtKeyImpl key = new RSAPrivateCrtKeyImpl(encoded);
// check all CRT-specific components are available, if any one
// missing, return a non-CRT key instead
if ((key.getPublicExponent().signum() == 0) ||
(key.getPrimeExponentP().signum() == 0) ||
(key.getPrimeExponentQ().signum() == 0) ||
(key.getPrimeP().signum() == 0) ||
(key.getPrimeQ().signum() == 0) ||
(key.getCrtCoefficient().signum() == 0)) {
return new RSAPrivateKeyImpl(
key.type, key.keyParams,
key.getModulus(),
key.getPrivateExponent()
);
} else {
return key;
switch (format) {
case "PKCS#8":
RSAPrivateCrtKeyImpl key = new RSAPrivateCrtKeyImpl(encoded);
RSAKeyFactory.checkKeyAlgo(key, type.keyAlgo);
// check all CRT-specific components are available, if any one
// missing, return a non-CRT key instead
if ((key.getPublicExponent().signum() == 0) ||
(key.getPrimeExponentP().signum() == 0) ||
(key.getPrimeExponentQ().signum() == 0) ||
(key.getPrimeP().signum() == 0) ||
(key.getPrimeQ().signum() == 0) ||
(key.getCrtCoefficient().signum() == 0)) {
return new RSAPrivateKeyImpl(key.type, key.keyParams,
key.getModulus(), key.getPrivateExponent());
} else {
return key;
}
case "PKCS#1":
try {
BigInteger[] comps = parseASN1(encoded);
if ((comps[1].signum() == 0) || (comps[3].signum() == 0) ||
(comps[4].signum() == 0) || (comps[5].signum() == 0) ||
(comps[6].signum() == 0) || (comps[7].signum() == 0)) {
return new RSAPrivateKeyImpl(type, null, comps[0],
comps[2]);
} else {
return new RSAPrivateCrtKeyImpl(type, null, comps[0],
comps[1], comps[2], comps[3], comps[4], comps[5],
comps[6], comps[7]);
}
} catch (IOException ioe) {
throw new InvalidKeyException("Invalid PKCS#1 encoding", ioe);
}
default:
throw new InvalidKeyException("Unsupported RSA Private(Crt)Key "
+ "format: " + format);
}
}

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

// utility method for parsing DER encoding of RSA private keys in PKCS#1
// format as defined in RFC 8017 Appendix A.1.2, i.e. SEQ of version, n,
// e, d, p, q, pe, qe, and coeff, and return the parsed components.
private static BigInteger[] parseASN1(byte[] raw) throws IOException {
DerValue derValue = new DerValue(raw);
if (derValue.tag != DerValue.tag_Sequence) {
throw new IOException("Not a SEQUENCE");
}
int version = derValue.data.getInteger();
if (version != 0) {
throw new IOException("Version must be 0");
}

BigInteger[] result = new BigInteger[8]; // n, e, d, p, q, pe, qe, coeff
/*
* Some implementations do not correctly encode ASN.1 INTEGER values
* in 2's complement format, resulting in a negative integer when
* decoded. Correct the error by converting it to a positive integer.
*
* See CR 6255949
*/
for (int i = 0; i < result.length; i++) {
result[i] = derValue.data.getPositiveBigInteger();
}
if (derValue.data.available() != 0) {
throw new IOException("Extra data available");
}
return result;
}

private void parseKeyBits() throws InvalidKeyException {
try {
DerInputStream in = new DerInputStream(key);
DerValue derValue = in.getDerValue();
if (derValue.tag != DerValue.tag_Sequence) {
throw new IOException("Not a SEQUENCE");
}
DerInputStream data = derValue.data;
int version = data.getInteger();
if (version != 0) {
throw new IOException("Version must be 0");
}

/*
* Some implementations do not correctly encode ASN.1 INTEGER values
* in 2's complement format, resulting in a negative integer when
* decoded. Correct the error by converting it to a positive integer.
*
* See CR 6255949
*/
n = data.getPositiveBigInteger();
e = data.getPositiveBigInteger();
d = data.getPositiveBigInteger();
p = data.getPositiveBigInteger();
q = data.getPositiveBigInteger();
pe = data.getPositiveBigInteger();
qe = data.getPositiveBigInteger();
coeff = data.getPositiveBigInteger();
if (derValue.data.available() != 0) {
throw new IOException("Extra data available");
}
BigInteger[] comps = parseASN1(key);
n = comps[0];
e = comps[1];
d = comps[2];
p = comps[3];
q = comps[4];
pe = comps[5];
qe = comps[6];
coeff = comps[7];
} catch (IOException e) {
throw new InvalidKeyException("Invalid RSA private key", e);
}
Expand Down
67 changes: 47 additions & 20 deletions src/java.base/share/classes/sun/security/rsa/RSAPublicKeyImpl.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -66,17 +66,36 @@ public final class RSAPublicKeyImpl extends X509Key implements RSAPublicKey {
private transient AlgorithmParameterSpec keyParams;

/**
* Generate a new RSAPublicKey from the specified encoding.
* Used by SunPKCS11 provider.
* Generate a new RSAPublicKey from the specified type, format, and
* encoding.
* Also used by SunPKCS11 provider.
*/
public static RSAPublicKey newKey(byte[] encoded)
throws InvalidKeyException {
return new RSAPublicKeyImpl(encoded);
public static RSAPublicKey newKey(KeyType type, String format,
byte[] encoded) throws InvalidKeyException {
RSAPublicKey key;
switch (format) {
case "X.509":
key = new RSAPublicKeyImpl(encoded);
RSAKeyFactory.checkKeyAlgo(key, type.keyAlgo);
break;
case "PKCS#1":
try {
BigInteger[] comps = parseASN1(encoded);
key = new RSAPublicKeyImpl(type, null, comps[0], comps[1]);
} catch (IOException ioe) {
throw new InvalidKeyException("Invalid PKCS#1 encoding", ioe);
}
break;
default:
throw new InvalidKeyException("Unsupported RSA PublicKey format: " +
format);
}
return key;
}

/**
* Generate a new RSAPublicKey from the specified type and components.
* Used by SunPKCS11 provider.
* Also used by SunPKCS11 provider.
*/
public static RSAPublicKey newKey(KeyType type,
AlgorithmParameterSpec params, BigInteger n, BigInteger e)
Expand Down Expand Up @@ -123,9 +142,9 @@ public static RSAPublicKey newKey(KeyType type,
}

/**
* Construct a key from its encoding. Used by RSAKeyFactory.
* Construct a key from its encoding.
*/
RSAPublicKeyImpl(byte[] encoded) throws InvalidKeyException {
private RSAPublicKeyImpl(byte[] encoded) throws InvalidKeyException {
if (encoded == null || encoded.length == 0) {
throw new InvalidKeyException("Missing key encoding");
}
Expand Down Expand Up @@ -181,22 +200,30 @@ public AlgorithmParameterSpec getParams() {
return keyParams;
}

// utility method for parsing DER encoding of RSA public keys in PKCS#1
// format as defined in RFC 8017 Appendix A.1.1, i.e. SEQ of n and e.
private static BigInteger[] parseASN1(byte[] raw) throws IOException {
DerValue derValue = new DerValue(raw);
if (derValue.tag != DerValue.tag_Sequence) {
throw new IOException("Not a SEQUENCE");
}
BigInteger[] result = new BigInteger[2]; // n, e
result[0] = derValue.data.getPositiveBigInteger();
result[1] = derValue.data.getPositiveBigInteger();
if (derValue.data.available() != 0) {
throw new IOException("Extra data available");
}
return result;
}

/**
* Parse the key. Called by X509Key.
*/
protected void parseKeyBits() throws InvalidKeyException {
try {
DerInputStream in = new DerInputStream(getKey().toByteArray());
DerValue derValue = in.getDerValue();
if (derValue.tag != DerValue.tag_Sequence) {
throw new IOException("Not a SEQUENCE");
}
DerInputStream data = derValue.data;
n = data.getPositiveBigInteger();
e = data.getPositiveBigInteger();
if (derValue.data.available() != 0) {
throw new IOException("Extra data available");
}
BigInteger[] comps = parseASN1(getKey().toByteArray());
n = comps[0];
e = comps[1];
} catch (IOException e) {
throw new InvalidKeyException("Invalid RSA public key", e);
}
Expand Down
Loading