From 693f6c7778b656c0bc2ad159568cf96331750112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann=20=28=40MarkL4YG=29?= Date: Sat, 23 Nov 2024 12:15:14 +0100 Subject: [PATCH] feat: Implement method for checking pKey encryption passphrase --- .../openssl/OpenSSLCertificateCreator.java | 38 +++++++++++++++++++ .../TestOpenSSLCertificateCreator.java | 18 ++++++--- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java index 8b8b6e8..b3d1007 100644 --- a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java +++ b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java @@ -25,6 +25,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.zeroturnaround.exec.ProcessExecutor; @@ -281,6 +282,43 @@ public class OpenSSLCertificateCreator { } } + public boolean isKeyEncrypted(@NonNull Path keyFile) throws InterruptedException, CommandLineOperationException { + // If any random passphrase works, the key is not encrypted. + return !verifyKeyPassphrase(keyFile, null); + } + + /** + * Verifies a passphrase against a provided key. + * @implNote Due to the implementation of the OpenSSL cli, any password will be valid for unencrypted keys. (Check with {@link #isKeyEncrypted(Path) or by passing {@code null} as the passphrase.) + */ + public boolean verifyKeyPassphrase(@NonNull Path keyFile, @Nullable String passphrase) + throws CommandLineOperationException, InterruptedException { + // Run OpenSSL command: openssl rsa -check -in -passin pass: -noout and check exit code + try { + String keyPass = passphrase != null ? passphrase : passwordProvider.generateNewPassword(); + StartedProcess verifyCommand = new ProcessExecutor() + .command( + resolveOpenSSL(), + "rsa", + "-check", + "-in", + keyFile.toString(), + "-passin", + "pass:" + keyPass, + "-noout" + ) + .redirectOutput(Slf4jStream.ofCaller().asError()) + .redirectError(Slf4jStream.ofCaller().asError()) + .start(); + var verifyResult = verifyCommand.getFuture().get(); + boolean commandSuccess = verifyResult.getExitValue() == 0; + log.trace("Key check {} with passphrase provided={}", commandSuccess, passphrase != null); + return commandSuccess; + } catch (IOException | InterruptedException | ExecutionException e) { + throw new CommandLineOperationException("Failed to verify key encryption", e); + } + } + private Path signCertificate( CertificateRequest request, Path caCert, diff --git a/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java b/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java index bf5de81..5c1da75 100644 --- a/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java +++ b/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java @@ -14,6 +14,8 @@ class TestOpenSSLCertificateCreator { public static final String TEST_CERT_PASSPHRASE = "ABC-123"; public static final Path TEST_CERT_PATH = Path.of("src/test/resources/openssl"); + public static final String ERR_NOT_ENCRYPTED = "Private key not encrypted"; + public static final String ERR_VERIFY_FAILED = "Certificate verification failed"; private CertificatePasswordProvider passwordProvider; @BeforeEach @@ -50,8 +52,11 @@ class TestOpenSSLCertificateCreator { try (var cert = certificateCreator.createCertificate(certRequest)) { assertThat(certificateCreator.verifyCertificate(cert.certificatePath(), cert.certificatePath())) - .isEqualTo(true); - System.out.println("Certificate created: " + cert); + .withFailMessage(ERR_VERIFY_FAILED) + .isTrue(); + assertThat(certificateCreator.isKeyEncrypted(cert.certificateKeyPath())) + .withFailMessage(ERR_NOT_ENCRYPTED) + .isTrue(); CertificateRequest childRequest = CertificateRequest .builder() @@ -73,12 +78,15 @@ class TestOpenSSLCertificateCreator { doNothing().when(spiedCert).close(); when(certificateProvider.requestCertificateUsage(cert.fingerprint())).thenReturn(spiedCert); try (var childCert = certificateCreator.createCertificate(childRequest)) { - System.out.println("Child certificate created: " + childCert); Path fullchain = childCert.fullchainPath(); assertThat( - certificateCreator.verifyCertificate(cert.certificatePath(), Objects.requireNonNull(fullchain)) + certificateCreator.verifyCertificate(Objects.requireNonNull(fullchain), cert.certificatePath()) ) - .isEqualTo(true); + .withFailMessage(ERR_VERIFY_FAILED) + .isTrue(); + assertThat(certificateCreator.isKeyEncrypted(childCert.certificateKeyPath())) + .withFailMessage(ERR_NOT_ENCRYPTED) + .isTrue(); } } }