Merge branch '3.0.x'

pull/35417/head
Phillip Webb 2 years ago
commit e3ed87d165

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2022 the original author or authors. * Copyright 2012-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

@ -39,8 +39,8 @@ public class MailHealthIndicator extends AbstractHealthIndicator {
@Override @Override
protected void doHealthCheck(Builder builder) throws Exception { protected void doHealthCheck(Builder builder) throws Exception {
int port = this.mailSender.getPort(); int port = this.mailSender.getPort();
builder.withDetail("location", (port == JavaMailSenderImpl.DEFAULT_PORT) ? this.mailSender.getHost() builder.withDetail("location", (port != JavaMailSenderImpl.DEFAULT_PORT)
: this.mailSender.getHost() + ":" + this.mailSender.getPort()); ? this.mailSender.getHost() + ":" + this.mailSender.getPort() : this.mailSender.getHost());
this.mailSender.testConnection(); this.mailSender.testConnection();
builder.up(); builder.up();
} }

@ -138,8 +138,6 @@ public class R2dbcProperties {
/** /**
* Minimal number of idle connections. * Minimal number of idle connections.
*
* @since 2.7.12
*/ */
private int minIdle = 0; private int minIdle = 0;
@ -163,8 +161,6 @@ public class R2dbcProperties {
/** /**
* Maximum time to validate a connection from the pool. By default, wait * Maximum time to validate a connection from the pool. By default, wait
* indefinitely. * indefinitely.
*
* @since 2.7.12
*/ */
private Duration maxValidationTime; private Duration maxValidationTime;

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2022 the original author or authors. * Copyright 2012-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2022 the original author or authors. * Copyright 2012-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

@ -33,6 +33,7 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
@ -121,16 +122,16 @@ public class DockerApi {
return this.jsonStream; return this.jsonStream;
} }
private URI buildUrl(String path, Collection<String> params) { private URI buildUrl(String path, Collection<?> params) {
return buildUrl(path, StringUtils.toStringArray(params)); return buildUrl(path, (params != null) ? params.toArray() : null);
} }
private URI buildUrl(String path, String... params) { private URI buildUrl(String path, Object... params) {
try { try {
URIBuilder builder = new URIBuilder("/" + API_VERSION + path); URIBuilder builder = new URIBuilder("/" + API_VERSION + path);
int param = 0; int param = 0;
while (param < params.length) { while (param < params.length) {
builder.addParameter(params[param++], params[param++]); builder.addParameter(Objects.toString(params[param++]), Objects.toString(params[param++]));
} }
return builder.build(); return builder.build();
} }
@ -190,7 +191,7 @@ public class DockerApi {
throws IOException { throws IOException {
Assert.notNull(reference, "Reference must not be null"); Assert.notNull(reference, "Reference must not be null");
Assert.notNull(listener, "Listener must not be null"); Assert.notNull(listener, "Listener must not be null");
URI createUri = buildUrl("/images/create", "fromImage", reference.toString()); URI createUri = buildUrl("/images/create", "fromImage", reference);
DigestCaptureUpdateListener digestCapture = new DigestCaptureUpdateListener(); DigestCaptureUpdateListener digestCapture = new DigestCaptureUpdateListener();
listener.onStart(); listener.onStart();
try { try {
@ -348,14 +349,9 @@ public class DockerApi {
Assert.notNull(sourceReference, "SourceReference must not be null"); Assert.notNull(sourceReference, "SourceReference must not be null");
Assert.notNull(targetReference, "TargetReference must not be null"); Assert.notNull(targetReference, "TargetReference must not be null");
String tag = targetReference.getTag(); String tag = targetReference.getTag();
URI uri; String path = "/images/" + sourceReference + "/tag";
if (tag == null) { URI uri = (tag != null) ? buildUrl(path, "repo", targetReference.inTaglessForm(), "tag", tag)
uri = buildUrl("/images/" + sourceReference + "/tag", "repo", targetReference.toString()); : buildUrl(path, "repo", targetReference);
}
else {
uri = buildUrl("/images/" + sourceReference + "/tag", "repo",
targetReference.inTaglessForm().toString(), "tag", tag);
}
http().post(uri).close(); http().post(uri).close();
} }
@ -437,7 +433,7 @@ public class DockerApi {
public void logs(ContainerReference reference, UpdateListener<LogUpdateEvent> listener) throws IOException { public void logs(ContainerReference reference, UpdateListener<LogUpdateEvent> listener) throws IOException {
Assert.notNull(reference, "Reference must not be null"); Assert.notNull(reference, "Reference must not be null");
Assert.notNull(listener, "Listener must not be null"); Assert.notNull(listener, "Listener must not be null");
String[] params = { "stdout", "1", "stderr", "1", "follow", "1" }; Object[] params = { "stdout", "1", "stderr", "1", "follow", "1" };
URI uri = buildUrl("/containers/" + reference + "/logs", params); URI uri = buildUrl("/containers/" + reference + "/logs", params);
listener.onStart(); listener.onStart();
try { try {

@ -23,6 +23,7 @@ import java.nio.file.Path;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.KeyFactory; import java.security.KeyFactory;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Base64; import java.util.Base64;
@ -37,6 +38,7 @@ import java.util.regex.Pattern;
* *
* @author Scott Frederick * @author Scott Frederick
* @author Phillip Webb * @author Phillip Webb
* @author Moritz Halbritter
*/ */
final class PrivateKeyParser { final class PrivateKeyParser {
@ -57,9 +59,9 @@ final class PrivateKeyParser {
private static final List<PemParser> PEM_PARSERS; private static final List<PemParser> PEM_PARSERS;
static { static {
List<PemParser> parsers = new ArrayList<>(); List<PemParser> parsers = new ArrayList<>();
parsers.add(new PemParser(PKCS1_HEADER, PKCS1_FOOTER, "RSA", PrivateKeyParser::createKeySpecForPkcs1)); parsers.add(new PemParser(PKCS1_HEADER, PKCS1_FOOTER, PrivateKeyParser::createKeySpecForPkcs1, "RSA"));
parsers.add(new PemParser(EC_HEADER, EC_FOOTER, "EC", PrivateKeyParser::createKeySpecForEc)); parsers.add(new PemParser(EC_HEADER, EC_FOOTER, PrivateKeyParser::createKeySpecForEc, "EC"));
parsers.add(new PemParser(PKCS8_HEADER, PKCS8_FOOTER, "RSA", PKCS8EncodedKeySpec::new)); parsers.add(new PemParser(PKCS8_HEADER, PKCS8_FOOTER, PKCS8EncodedKeySpec::new, "RSA", "EC", "DSA", "Ed25519"));
PEM_PARSERS = Collections.unmodifiableList(parsers); PEM_PARSERS = Collections.unmodifiableList(parsers);
} }
@ -134,14 +136,14 @@ final class PrivateKeyParser {
private final Pattern pattern; private final Pattern pattern;
private final String algorithm;
private final Function<byte[], PKCS8EncodedKeySpec> keySpecFactory; private final Function<byte[], PKCS8EncodedKeySpec> keySpecFactory;
PemParser(String header, String footer, String algorithm, private final String[] algorithms;
Function<byte[], PKCS8EncodedKeySpec> keySpecFactory) {
PemParser(String header, String footer, Function<byte[], PKCS8EncodedKeySpec> keySpecFactory,
String... algorithms) {
this.pattern = Pattern.compile(header + BASE64_TEXT + footer, Pattern.CASE_INSENSITIVE); this.pattern = Pattern.compile(header + BASE64_TEXT + footer, Pattern.CASE_INSENSITIVE);
this.algorithm = algorithm; this.algorithms = algorithms;
this.keySpecFactory = keySpecFactory; this.keySpecFactory = keySpecFactory;
} }
@ -158,8 +160,15 @@ final class PrivateKeyParser {
private PrivateKey parse(byte[] bytes) { private PrivateKey parse(byte[] bytes) {
try { try {
PKCS8EncodedKeySpec keySpec = this.keySpecFactory.apply(bytes); PKCS8EncodedKeySpec keySpec = this.keySpecFactory.apply(bytes);
KeyFactory keyFactory = KeyFactory.getInstance(this.algorithm); for (String algorithm : this.algorithms) {
return keyFactory.generatePrivate(keySpec); KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
try {
return keyFactory.generatePrivate(keySpec);
}
catch (InvalidKeySpecException ex) {
}
}
return null;
} }
catch (GeneralSecurityException ex) { catch (GeneralSecurityException ex) {
throw new IllegalArgumentException("Unexpected key format", ex); throw new IllegalArgumentException("Unexpected key format", ex);

@ -28,6 +28,7 @@ import org.springframework.util.FileSystemUtils;
* Utility to write certificate and key PEM files for testing. * Utility to write certificate and key PEM files for testing.
* *
* @author Scott Frederick * @author Scott Frederick
* @author Moritz Halbritter
*/ */
public class PemFileWriter { public class PemFileWriter {
@ -52,24 +53,6 @@ public class PemFileWriter {
-----END TRUSTED CERTIFICATE----- -----END TRUSTED CERTIFICATE-----
"""; """;
public static final String CA_PRIVATE_KEY = """
%s-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANWM4sVhKMs63dtA
tkdYI8jwqD3xS5RulGE8k9Lbnjcuso7+qmyfJXGYO+tejtyPS7jh4WYfVPnwgwoD
34g3ciXwxFvz/nbDjBcjFFz6yT83tsj6Tp71fIieQzPd7nxFYQgssy3WLtD9j94V
Nlvk9/yJvQwOxTjwNrxdUqyCdqrVAgMBAAECgYEAyJTlZ8nj3Eg1nLxCue6C5jmN
fWkIuanH+zFAE/0utdxJ4WA4yYAOVo1MMr8FZwu9bzHTWe2yDnWnT5/ltPeHYX2X
9Pg5cY0tjq07utaMwLKWgJ0Xoh2UpVM799t/rSvMWmLaZ2c8nipX+gQfYJFpX8Vg
mR3QPxwdmNyFo13qif0CQQD4z2SqCfARuxscTCJDZ6wReikMQxaJvq74lPEtT26L
rBr/bN+mG7+rMEHxs5wtU47aNjUKuVVC0Qfhsf95ahvHAkEA27inSlxrwGvhvFsD
FWdgDsfYpPZdL4YgpVSEvcoypRGg2suJw2omcKcY56XpkmWUqZc06QirumtnEC0P
HfnsgwJBAMVhEURrOc13FxytsQiz96atuF6H4htH79o3ndQKDXI0B/7VSd6maLjP
QaESkTTL8qldE1r8h4zH8m6zHC4fZQUCQFWJ+8bdWC2fUlBr9jVc+26Fqvf92aVo
yEjVMKBamYDd7gt/9fAX4UM2KmH0m4wc89VaQoT+lSyMJ6GKiToYVFUCQEXcyoeO
zWqtSgEX/eXQXzmMKxYnjv1O//ba3Q7UiHd/XO5j4QXAJpcB6h0h00uC5KY2d0Zy
JQ1kB1C2l6l9tyc=
-----END PRIVATE KEY-----""".formatted(EXAMPLE_SECRET_QUALIFIER);
public static final String CERTIFICATE = """ public static final String CERTIFICATE = """
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIICjzCCAfgCAQEwDQYJKoZIhvcNAQEFBQAwgY8xCzAJBgNVBAYTAlVTMRMwEQYD MIICjzCCAfgCAQEwDQYJKoZIhvcNAQEFBQAwgY8xCzAJBgNVBAYTAlVTMRMwEQYD
@ -114,6 +97,77 @@ public class PemFileWriter {
hOhjWFCQjnewR98Y8pEb1fnORll4LhHPlQ== hOhjWFCQjnewR98Y8pEb1fnORll4LhHPlQ==
-----END EC PRIVATE KEY-----""".formatted(EXAMPLE_SECRET_QUALIFIER); -----END EC PRIVATE KEY-----""".formatted(EXAMPLE_SECRET_QUALIFIER);
public static final String PRIVATE_DSA_KEY = EXAMPLE_SECRET_QUALIFIER + "-----BEGIN PRIVATE KEY-----\n"
+ "MIICXAIBADCCAjUGByqGSM44BAEwggIoAoIBAQCPeTXZuarpv6vtiHrPSVG28y7F\n"
+ "njuvNxjo6sSWHz79NgbnQ1GpxBgzObgJ58KuHFObp0dbhdARrbi0eYd1SYRpXKwO\n"
+ "jxSzNggooi/6JxEKPWKpk0U0CaD+aWxGWPhL3SCBnDcJoBBXsZWtzQAjPbpUhLYp\n"
+ "H51kjviDRIZ3l5zsBLQ0pqwudemYXeI9sCkvwRGMn/qdgYHnM423krcw17njSVkv\n"
+ "aAmYchU5Feo9a4tGU8YzRY+AOzKkwuDycpAlbk4/ijsIOKHEUOThjBopo33fXqFD\n"
+ "3ktm/wSQPtXPFiPhWNSHxgjpfyEc2B3KI8tuOAdl+CLjQr5ITAV2OTlgHNZnAh0A\n"
+ "uvaWpoV499/e5/pnyXfHhe8ysjO65YDAvNVpXQKCAQAWplxYIEhQcE51AqOXVwQN\n"
+ "NNo6NHjBVNTkpcAtJC7gT5bmHkvQkEq9rI837rHgnzGC0jyQQ8tkL4gAQWDt+coJ\n"
+ "syB2p5wypifyRz6Rh5uixOdEvSCBVEy1W4AsNo0fqD7UielOD6BojjJCilx4xHjG\n"
+ "jQUntxyaOrsLC+EsRGiWOefTznTbEBplqiuH9kxoJts+xy9LVZmDS7TtsC98kOmk\n"
+ "ltOlXVNb6/xF1PYZ9j897buHOSXC8iTgdzEpbaiH7B5HSPh++1/et1SEMWsiMt7l\n"
+ "U92vAhErDR8C2jCXMiT+J67ai51LKSLZuovjntnhA6Y8UoELxoi34u1DFuHvF9ve\n"
+ "BB4CHHBQgJ3ST6U8rIxoTqGe42TiVckPf1PoSiJy8GY=\n" + "-----END PRIVATE KEY-----\n";
public static final String PKCS8_PRIVATE_EC_NIST_P256_KEY = EXAMPLE_SECRET_QUALIFIER
+ "-----BEGIN PRIVATE KEY-----\n" + "MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgd6SePFfpaTKFd1Gm\n"
+ "+WeHZNkORkot5hx6X9elPdICL9ygCgYIKoZIzj0DAQehRANCAASnMAMgeFBv9ks0\n"
+ "d0jP+utQ3mohwmxY93xljfaBofdg1IeHgDd4I4pBzPxEnvXrU3kcz+SgPZyH1ybl\n" + "P6mSXDXu\n"
+ "-----END PRIVATE KEY-----\n";
public static final String PKCS8_PRIVATE_EC_NIST_P384_KEY = EXAMPLE_SECRET_QUALIFIER
+ "-----BEGIN PRIVATE KEY-----\n" + "MIG/AgEAMBAGByqGSM49AgEGBSuBBAAiBIGnMIGkAgEBBDCexXiWKrtrqV1+d1Tv\n"
+ "t1n5huuw2A+204mQHRuPL9UC8l0XniJjx/PVELCciyJM/7+gBwYFK4EEACKhZANi\n"
+ "AASHEELZSdrHiSXqU1B+/jrOCr6yjxCMqQsetTb0q5WZdCXOhggGXfbzlRynqphQ\n"
+ "i4G7azBUklgLaXfxN5eFk6C+E38SYOR7iippcQsSR2ZsCiTk7rnur4b40gQ7IgLA\n" + "/sU=\n"
+ "-----END PRIVATE KEY-----\n";
public static final String PKCS8_PRIVATE_EC_PRIME256V1_KEY = EXAMPLE_SECRET_QUALIFIER
+ "-----BEGIN PRIVATE KEY-----\n" + "MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg4dVuddgQ6enDvPPw\n"
+ "Dd1mmS6FMm/kzTJjDVsltrNmRuSgCgYIKoZIzj0DAQehRANCAAR1WMrRADEaVj9m\n"
+ "uoUfPhUefJK+lS89NHikQ0ZdkHkybyVKLFMLe1hCynhzpKQmnpgud3E10F0P2PZQ\n" + "L9RCEpGf\n"
+ "-----END PRIVATE KEY-----\n";
public static final String PKCS8_PRIVATE_EC_SECP256R1_KEY = EXAMPLE_SECRET_QUALIFIER
+ "-----BEGIN PRIVATE KEY-----\n" + "MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgU9+v5hUNnTKix8fe\n"
+ "Pfz+NfXFlGxQZMReSCT2Id9PfKagCgYIKoZIzj0DAQehRANCAATeJg+YS4BrJ35A\n"
+ "KgRlZ59yKLDpmENCMoaYUuWbQ9hqHzdybQGzQsrNJqgH0nzWghPwP4nFaLPN+pgB\n" + "bqiRgbjG\n"
+ "-----END PRIVATE KEY-----\n";
public static final String PKCS8_PRIVATE_RSA_KEY = EXAMPLE_SECRET_QUALIFIER + "-----BEGIN PRIVATE KEY-----\n"
+ "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDR0KfxUw7MF/8R\n"
+ "B5/YXOM7yLnoHYb/M/6dyoulMbtEdKKhQhU28o5FiDkHcEG9PJQLgqrRgAjl3VmC\n"
+ "C9omtfZJQ2EpfkTttkJjnKOOroXhYE51/CYSckapBYCVh8GkjUEJuEfnp07cTfYZ\n"
+ "FqViIgIWPZyjkzl3w4girS7kCuzNdDntVJVx5F/EsFwMA8n3C0QazHQoM5s00Fer\n"
+ "6aTwd6AW0JD5QkADavpfzZ554e4HrVGwHlM28WKQQkFzzGu44FFXyVuEF3HeyVPu\n"
+ "g8GRHAc8UU7ijVgJB5TmbvRGYowIErD5i4VvGLuOv9mgR3aVyN0SdJ1N7aJnXpeS\n"
+ "QjAgf03jAgMBAAECggEBAIhQyzwj3WJGWOZkkLqOpufJotcmj/Wwf0VfOdkq9WMl\n"
+ "cB/bAlN/xWVxerPVgDCFch4EWBzi1WUaqbOvJZ2u7QNubmr56aiTmJCFTVI/GyZx\n"
+ "XqiTGN01N6lKtN7xo6LYTyAUhUsBTWAemrx0FSErvTVb9C/mUBj6hbEZ2XQ5kN5t\n"
+ "7qYX4Lu0zyn7s1kX5SLtm5I+YRq7HSwB6wLy+DSroO71izZ/VPwME3SwT5SN+c87\n"
+ "3dkklR7fumNd9dOpSWKrLPnq4aMko00rvIGc63xD1HrEpXUkB5v24YEn7HwCLEH7\n"
+ "b8jrp79j2nCvvR47inpf+BR8FIWAHEOUUqCEzjQkdiECgYEA6ifjMM0f02KPeIs7\n"
+ "zXd1lI7CUmJmzkcklCIpEbKWf/t/PHv3QgqIkJzERzRaJ8b+GhQ4zrSwAhrGUmI8\n"
+ "kDkXIqe2/2ONgIOX2UOHYHyTDQZHnlXyDecvHUTqs2JQZCGBZkXyZ9i0j3BnTymC\n"
+ "iZ8DvEa0nxsbP+U3rgzPQmXiQVMCgYEA5WN2Y/RndbriNsNrsHYRldbPO5nfV9rp\n"
+ "cDzcQU66HRdK5VIdbXT9tlMYCJIZsSqE0tkOwTgEB/sFvF/tIHSCY5iO6hpIyk6g\n"
+ "kkUzPcld4eM0dEPAge7SYUbakB9CMvA7MkDQSXQNFyZ0mH83+UikwT6uYHFh7+ox\n"
+ "N1P+psDhXzECgYEA1gXLVQnIcy/9LxMkgDMWV8j8uMyUZysDthpbK3/uq+A2dhRg\n"
+ "9g4msPd5OBQT65OpIjElk1n4HpRWfWqpLLHiAZ0GWPynk7W0D7P3gyuaRSdeQs0P\n"
+ "x8FtgPVDCN9t13gAjHiWjnC26Py2kNbCKAQeJ/MAmQTvrUFX2VCACJKTcV0CgYAj\n"
+ "xJWSUmrLfb+GQISLOG3Xim434e9keJsLyEGj4U29+YLRLTOvfJ2PD3fg5j8hU/rw\n"
+ "Ea5uTHi8cdTcIa0M8X3fX8txD3YoLYh2JlouGTcNYOst8d6TpBSj3HN6I5Wj8beZ\n"
+ "R2fy/CiKYpGtsbCdq0kdZNO18BgQW9kewncjs1GxEQKBgQCf8q34h6KuHpHSDh9h\n"
+ "YkDTypk0FReWBAVJCzDNDUMhVLFivjcwtaMd2LiC3FMKZYodr52iKg60cj43vbYI\n"
+ "frmFFxoL37rTmUocCTBKc0LhWj6MicI+rcvQYe1uwTrpWdFf1aZJMYRLRczeKtev\n" + "OWaE/9hVZ5+9pild1NukGpOydw==\n"
+ "-----END PRIVATE KEY-----\n";
public static final String PKCS8_PRIVATE_EC_ED25519_KEY = EXAMPLE_SECRET_QUALIFIER + "-----BEGIN PRIVATE KEY-----\n"
+ "MC4CAQAwBQYDK2VwBCIEIJOKNTaIJQTVuEqZ+yvclnjnlWJG6F+K+VsNCOlWRda+\n" + "-----END PRIVATE KEY-----";
private final Path tempDir; private final Path tempDir;
public PemFileWriter() throws IOException { public PemFileWriter() throws IOException {

@ -28,6 +28,8 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.boot.buildpack.platform.docker.ssl.PrivateKeyParser.DerEncoder; import org.springframework.boot.buildpack.platform.docker.ssl.PrivateKeyParser.DerEncoder;
@ -39,6 +41,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
* *
* @author Scott Frederick * @author Scott Frederick
* @author Phillip Webb * @author Phillip Webb
* @author Moritz Halbritter
*/ */
class PrivateKeyParserTests { class PrivateKeyParserTests {
@ -55,14 +58,34 @@ class PrivateKeyParserTests {
} }
@Test @Test
void parsePkcs8KeyFile() throws IOException { void parsePkcs8RsaKeyFile() throws IOException {
Path path = this.fileWriter.writeFile("key.pem", PemFileWriter.CA_PRIVATE_KEY); Path path = this.fileWriter.writeFile("key.pem", PemFileWriter.PKCS8_PRIVATE_RSA_KEY);
PrivateKey privateKey = PrivateKeyParser.parse(path); PrivateKey privateKey = PrivateKeyParser.parse(path);
assertThat(privateKey).isNotNull(); assertThat(privateKey).isNotNull();
assertThat(privateKey.getFormat()).isEqualTo("PKCS#8"); assertThat(privateKey.getFormat()).isEqualTo("PKCS#8");
Files.delete(path); Files.delete(path);
} }
@ParameterizedTest
@ValueSource(strings = { PemFileWriter.PKCS8_PRIVATE_EC_NIST_P256_KEY, PemFileWriter.PKCS8_PRIVATE_EC_NIST_P384_KEY,
PemFileWriter.PKCS8_PRIVATE_EC_PRIME256V1_KEY, PemFileWriter.PKCS8_PRIVATE_EC_SECP256R1_KEY })
void parsePkcs8EcKeyFile(String contents) throws IOException {
Path path = this.fileWriter.writeFile("key.pem", contents);
PrivateKey privateKey = PrivateKeyParser.parse(path);
assertThat(privateKey).isNotNull();
assertThat(privateKey.getFormat()).isEqualTo("PKCS#8");
assertThat(privateKey.getAlgorithm()).isEqualTo("EC");
}
@Test
void parsePkcs8DsaKeyFile() throws IOException {
Path path = this.fileWriter.writeFile("key.pem", PemFileWriter.PRIVATE_DSA_KEY);
PrivateKey privateKey = PrivateKeyParser.parse(path);
assertThat(privateKey).isNotNull();
assertThat(privateKey.getFormat()).isEqualTo("PKCS#8");
assertThat(privateKey.getAlgorithm()).isEqualTo("DSA");
}
@Test @Test
void parsePkcs1RsaKeyFile() throws IOException { void parsePkcs1RsaKeyFile() throws IOException {
Path path = this.fileWriter.writeFile("key.pem", PemFileWriter.PRIVATE_RSA_KEY); Path path = this.fileWriter.writeFile("key.pem", PemFileWriter.PRIVATE_RSA_KEY);
@ -83,6 +106,15 @@ class PrivateKeyParserTests {
Files.delete(path); Files.delete(path);
} }
@Test
void parsePkcs8Ed25519KeyFile() throws IOException {
Path path = this.fileWriter.writeFile("key.pem", PemFileWriter.PKCS8_PRIVATE_EC_ED25519_KEY);
PrivateKey privateKey = PrivateKeyParser.parse(path);
assertThat(privateKey).isNotNull();
assertThat(privateKey.getFormat()).isEqualTo("PKCS#8");
assertThat(privateKey.getAlgorithm()).isEqualTo("EdDSA");
}
@Test @Test
void parseWithNonKeyFileWillThrowException() throws IOException { void parseWithNonKeyFileWillThrowException() throws IOException {
Path path = this.fileWriter.writeFile("text.pem", "plain text"); Path path = this.fileWriter.writeFile("text.pem", "plain text");

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2022 the original author or authors. * Copyright 2012-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2022 the original author or authors. * Copyright 2012-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2022 the original author or authors. * Copyright 2012-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

@ -24,9 +24,7 @@ import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64; import java.util.Base64;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
@ -59,12 +57,9 @@ final class PemPrivateKeyParser {
private static final List<PemParser> PEM_PARSERS; private static final List<PemParser> PEM_PARSERS;
static { static {
List<PemParser> parsers = new ArrayList<>(); List<PemParser> parsers = new ArrayList<>();
parsers.add(new PemParser(PKCS1_HEADER, PKCS1_FOOTER, Collections.singleton("RSA"), parsers.add(new PemParser(PKCS1_HEADER, PKCS1_FOOTER, PemPrivateKeyParser::createKeySpecForPkcs1, "RSA"));
PemPrivateKeyParser::createKeySpecForPkcs1)); parsers.add(new PemParser(EC_HEADER, EC_FOOTER, PemPrivateKeyParser::createKeySpecForEc, "EC"));
parsers.add(new PemParser(EC_HEADER, EC_FOOTER, Collections.singleton("EC"), parsers.add(new PemParser(PKCS8_HEADER, PKCS8_FOOTER, PKCS8EncodedKeySpec::new, "RSA", "EC", "DSA", "Ed25519"));
PemPrivateKeyParser::createKeySpecForEc));
parsers.add(new PemParser(PKCS8_HEADER, PKCS8_FOOTER, Arrays.asList("RSA", "EC", "DSA", "Ed25519"),
PKCS8EncodedKeySpec::new));
PEM_PARSERS = Collections.unmodifiableList(parsers); PEM_PARSERS = Collections.unmodifiableList(parsers);
} }
@ -141,12 +136,12 @@ final class PemPrivateKeyParser {
private final Pattern pattern; private final Pattern pattern;
private final Collection<String> algorithms;
private final Function<byte[], PKCS8EncodedKeySpec> keySpecFactory; private final Function<byte[], PKCS8EncodedKeySpec> keySpecFactory;
PemParser(String header, String footer, Collection<String> algorithms, private final String[] algorithms;
Function<byte[], PKCS8EncodedKeySpec> keySpecFactory) {
PemParser(String header, String footer, Function<byte[], PKCS8EncodedKeySpec> keySpecFactory,
String... algorithms) {
this.pattern = Pattern.compile(header + BASE64_TEXT + footer, Pattern.CASE_INSENSITIVE); this.pattern = Pattern.compile(header + BASE64_TEXT + footer, Pattern.CASE_INSENSITIVE);
this.algorithms = algorithms; this.algorithms = algorithms;
this.keySpecFactory = keySpecFactory; this.keySpecFactory = keySpecFactory;
@ -170,7 +165,7 @@ final class PemPrivateKeyParser {
try { try {
return keyFactory.generatePrivate(keySpec); return keyFactory.generatePrivate(keySpec);
} }
catch (InvalidKeySpecException ignored) { catch (InvalidKeySpecException ex) {
} }
} }
return null; return null;

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2022 the original author or authors. * Copyright 2012-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2022 the original author or authors. * Copyright 2012-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

Loading…
Cancel
Save