Skip to content

Commit cc6303f

Browse files
Wzy19930507philwebb
authored andcommitted
Reactor PemPrivateKeyParser to use DerElement
Update `PemPrivateKeyParser` so that the algorithm is read using DerElement whenever possible. See gh-39162
1 parent c2ca6bf commit cc6303f

File tree

1 file changed

+91
-3
lines changed

1 file changed

+91
-3
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ssl/pem/PemPrivateKeyParser.java

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.security.spec.InvalidKeySpecException;
2828
import java.security.spec.PKCS8EncodedKeySpec;
2929
import java.util.ArrayList;
30+
import java.util.Arrays;
3031
import java.util.Base64;
3132
import java.util.Collections;
3233
import java.util.List;
@@ -91,6 +92,36 @@ final class PemPrivateKeyParser {
9192
*/
9293
private static final int[] RSA_ALGORITHM = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
9394

95+
/**
96+
* ASN.1 encoded object identifier {@literal 1.2.840.113549.1.1.10}.
97+
*/
98+
private static final int[] RSASSA_PSS_ALGORITHM = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a };
99+
100+
/**
101+
* ASN.1 encoded object identifier {@literal 1.2.840.10040.4.1}.
102+
*/
103+
private static final int[] DSA_ALGORITHM = { 0x2a, 0x86, 0x48, 0xce, 0x38, 0x04, 0x01 };
104+
105+
/**
106+
* ASN.1 encoded object identifier {@literal 1.3.101.110}.
107+
*/
108+
private static final int[] X25519_ALGORITHM = { 0x2b, 0x65, 0x6e };
109+
110+
/**
111+
* ASN.1 encoded object identifier {@literal 1.3.101.111}.
112+
*/
113+
private static final int[] X448_ALGORITHM = { 0x2b, 0x65, 0x6f };
114+
115+
/**
116+
* ASN.1 encoded object identifier {@literal 1.3.101.112}.
117+
*/
118+
private static final int[] ED448_ALGORITHM = { 0x2b, 0x65, 0x70 };
119+
120+
/**
121+
* ASN.1 encoded object identifier {@literal 1.3.101.113}.
122+
*/
123+
private static final int[] ED25519_ALGORITHM = { 0x2b, 0x65, 0x71 };
124+
94125
/**
95126
* ASN.1 encoded object identifier {@literal 1.2.840.10045.2.1}.
96127
*/
@@ -132,10 +163,10 @@ private static int[] getEcParameters(DerElement parameters) {
132163
DerElement contents = DerElement.of(parameters.getContents());
133164
Assert.state(contents.isType(ValueType.PRIMITIVE, TagType.OBJECT_IDENTIFIER),
134165
"Key spec parameters should contain object identifier");
135-
return getEcParameters(contents.getContents());
166+
return getOid(contents.getContents());
136167
}
137168

138-
private static int[] getEcParameters(ByteBuffer bytes) {
169+
private static int[] getOid(ByteBuffer bytes) {
139170
int[] result = new int[bytes.remaining()];
140171
for (int i = 0; i < result.length; i++) {
141172
result[i] = bytes.get() & 0xFF;
@@ -160,7 +191,55 @@ private static PKCS8EncodedKeySpec createKeySpecForAlgorithm(byte[] bytes, int[]
160191
}
161192

162193
private static PKCS8EncodedKeySpec createKeySpecForPkcs8(byte[] bytes, String password) {
163-
return new PKCS8EncodedKeySpec(bytes);
194+
DerElement ecPrivateKey = DerElement.of(bytes);
195+
Assert.state(ecPrivateKey.isType(ValueType.ENCODED, TagType.SEQUENCE),
196+
"Key spec should be an ASN.1 encoded sequence");
197+
DerElement version = DerElement.of(ecPrivateKey.getContents());
198+
Assert.state(version != null && version.isType(ValueType.PRIMITIVE, TagType.INTEGER),
199+
"Key spec should start with version");
200+
DerElement sequence = DerElement.of(ecPrivateKey.getContents());
201+
Assert.state(sequence != null && sequence.isType(ValueType.ENCODED, TagType.SEQUENCE),
202+
"Key spec should contain private key");
203+
DerElement algorithmIdentifier = DerElement.of(sequence.getContents());
204+
Assert.state(
205+
algorithmIdentifier != null
206+
&& algorithmIdentifier.isType(ValueType.PRIMITIVE, TagType.OBJECT_IDENTIFIER),
207+
"Key spec container object identifier");
208+
int[] oid = getOid(algorithmIdentifier.getContents());
209+
String algorithmName = getAlgorithm(oid);
210+
if (algorithmName != null) {
211+
return new PKCS8EncodedKeySpec(bytes, algorithmName);
212+
}
213+
else {
214+
return new PKCS8EncodedKeySpec(bytes);
215+
}
216+
}
217+
218+
private static String getAlgorithm(int[] oid) {
219+
if (oid == null) {
220+
return null;
221+
}
222+
if (Arrays.equals(RSA_ALGORITHM, oid)) {
223+
return "RSA";
224+
}
225+
else if (Arrays.equals(RSASSA_PSS_ALGORITHM, oid)) {
226+
return "RSASSA-PSS";
227+
}
228+
else if (Arrays.equals(DSA_ALGORITHM, oid)) {
229+
return "DSA";
230+
}
231+
else if (Arrays.equals(ED448_ALGORITHM, oid) || Arrays.equals(ED25519_ALGORITHM, oid)) {
232+
return "EdDSA";
233+
}
234+
else if (Arrays.equals(X448_ALGORITHM, oid) || Arrays.equals(X25519_ALGORITHM, oid)) {
235+
return "XDH";
236+
}
237+
else if (Arrays.equals(EC_ALGORITHM, oid)) {
238+
return "EC";
239+
}
240+
else {
241+
return null;
242+
}
164243
}
165244

166245
private static PKCS8EncodedKeySpec createKeySpecForPkcs8Encrypted(byte[] bytes, String password) {
@@ -231,6 +310,15 @@ private static byte[] decodeBase64(String content) {
231310

232311
private PrivateKey parse(byte[] bytes, String password) {
233312
PKCS8EncodedKeySpec keySpec = this.keySpecFactory.apply(bytes, password);
313+
if (keySpec.getAlgorithm() != null) {
314+
try {
315+
KeyFactory keyFactory = KeyFactory.getInstance(keySpec.getAlgorithm());
316+
return keyFactory.generatePrivate(keySpec);
317+
}
318+
catch (InvalidKeySpecException | NoSuchAlgorithmException ex) {
319+
// Ignore
320+
}
321+
}
234322
for (String algorithm : this.algorithms) {
235323
try {
236324
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);

0 commit comments

Comments
 (0)