From 5fb428ca368aaaf6216b1136ba25781697334935 Mon Sep 17 00:00:00 2001 From: Wzy19930507 <1208931582@qq.com> Date: Wed, 17 Jan 2024 15:56:44 +0800 Subject: [PATCH] Reactor `PemPrivateKeyParser` to use `DerElement` --- .../boot/ssl/pem/PemPrivateKeyParser.java | 94 ++++++++++++++++++- 1 file changed, 91 insertions(+), 3 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ssl/pem/PemPrivateKeyParser.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ssl/pem/PemPrivateKeyParser.java index 113d490ea18c..8bb26e947dd8 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ssl/pem/PemPrivateKeyParser.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ssl/pem/PemPrivateKeyParser.java @@ -27,6 +27,7 @@ import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.util.ArrayList; +import java.util.Arrays; import java.util.Base64; import java.util.Collections; import java.util.List; @@ -91,6 +92,36 @@ final class PemPrivateKeyParser { */ private static final int[] RSA_ALGORITHM = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }; + /** + * ASN.1 encoded object identifier {@literal 1.2.840.113549.1.1.10}. + */ + private static final int[] RSASSA_PSS_ALGORITHM = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a }; + + /** + * ASN.1 encoded object identifier {@literal 1.2.840.10040.4.1}. + */ + private static final int[] DSA_ALGORITHM = { 0x2a, 0x86, 0x48, 0xce, 0x38, 0x04, 0x01 }; + + /** + * ASN.1 encoded object identifier {@literal 1.3.101.110}. + */ + private static final int[] X25519_ALGORITHM = { 0x2b, 0x65, 0x6e }; + + /** + * ASN.1 encoded object identifier {@literal 1.3.101.111}. + */ + private static final int[] X448_ALGORITHM = { 0x2b, 0x65, 0x6f }; + + /** + * ASN.1 encoded object identifier {@literal 1.3.101.112}. + */ + private static final int[] ED448_ALGORITHM = { 0x2b, 0x65, 0x70 }; + + /** + * ASN.1 encoded object identifier {@literal 1.3.101.113}. + */ + private static final int[] ED25519_ALGORITHM = { 0x2b, 0x65, 0x71 }; + /** * ASN.1 encoded object identifier {@literal 1.2.840.10045.2.1}. */ @@ -132,10 +163,10 @@ private static int[] getEcParameters(DerElement parameters) { DerElement contents = DerElement.of(parameters.getContents()); Assert.state(contents != null && contents.isType(ValueType.PRIMITIVE, TagType.OBJECT_IDENTIFIER), "Key spec parameters should contain object identifier"); - return getEcParameters(contents.getContents()); + return getOid(contents.getContents()); } - private static int[] getEcParameters(ByteBuffer bytes) { + private static int[] getOid(ByteBuffer bytes) { int[] result = new int[bytes.remaining()]; for (int i = 0; i < result.length; i++) { result[i] = bytes.get() & 0xFF; @@ -160,7 +191,55 @@ private static PKCS8EncodedKeySpec createKeySpecForAlgorithm(byte[] bytes, int[] } private static PKCS8EncodedKeySpec createKeySpecForPkcs8(byte[] bytes, String password) { - return new PKCS8EncodedKeySpec(bytes); + DerElement ecPrivateKey = DerElement.of(bytes); + Assert.state(ecPrivateKey.isType(ValueType.ENCODED, TagType.SEQUENCE), + "Key spec should be an ASN.1 encoded sequence"); + DerElement version = DerElement.of(ecPrivateKey.getContents()); + Assert.state(version != null && version.isType(ValueType.PRIMITIVE, TagType.INTEGER), + "Key spec should start with version"); + DerElement sequence = DerElement.of(ecPrivateKey.getContents()); + Assert.state(sequence != null && sequence.isType(ValueType.ENCODED, TagType.SEQUENCE), + "Key spec should contain private key"); + DerElement algorithmIdentifier = DerElement.of(sequence.getContents()); + Assert.state( + algorithmIdentifier != null + && algorithmIdentifier.isType(ValueType.PRIMITIVE, TagType.OBJECT_IDENTIFIER), + "Key spec container object identifier"); + int[] oid = getOid(algorithmIdentifier.getContents()); + String algorithmName = getAlgorithm(oid); + if (algorithmName != null) { + return new PKCS8EncodedKeySpec(bytes, algorithmName); + } + else { + return new PKCS8EncodedKeySpec(bytes); + } + } + + private static String getAlgorithm(int[] oid) { + if (oid == null) { + return null; + } + if (Arrays.equals(RSA_ALGORITHM, oid)) { + return "RSA"; + } + else if (Arrays.equals(RSASSA_PSS_ALGORITHM, oid)) { + return "RSASSA-PSS"; + } + else if (Arrays.equals(DSA_ALGORITHM, oid)) { + return "DSA"; + } + else if (Arrays.equals(ED448_ALGORITHM, oid) || Arrays.equals(ED25519_ALGORITHM, oid)) { + return "EdDSA"; + } + else if (Arrays.equals(X448_ALGORITHM, oid) || Arrays.equals(X25519_ALGORITHM, oid)) { + return "XDH"; + } + else if (Arrays.equals(EC_ALGORITHM, oid)) { + return "EC"; + } + else { + return null; + } } private static PKCS8EncodedKeySpec createKeySpecForPkcs8Encrypted(byte[] bytes, String password) { @@ -231,6 +310,15 @@ private static byte[] decodeBase64(String content) { private PrivateKey parse(byte[] bytes, String password) { PKCS8EncodedKeySpec keySpec = this.keySpecFactory.apply(bytes, password); + if (keySpec.getAlgorithm() != null) { + try { + KeyFactory keyFactory = KeyFactory.getInstance(keySpec.getAlgorithm()); + return keyFactory.generatePrivate(keySpec); + } + catch (InvalidKeySpecException | NoSuchAlgorithmException ex) { + // Ignore + } + } for (String algorithm : this.algorithms) { try { KeyFactory keyFactory = KeyFactory.getInstance(algorithm);