mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 19:33:11 +02:00
Validate certificate inputs for cert signing (#5191)
## Summary - validate required certificate inputs before loading keystores to prevent null dereferences - surface clear errors for missing PEM, PKCS12/PFX, and JKS uploads during PDF signing - add a unit test covering the missing PKCS12/PFX keystore scenario ## Testing - ./gradlew :stirling-pdf:test --tests stirling.software.SPDF.controller.api.security.CertSignControllerTest ------ [Codex Task](https://chatgpt.com/codex/tasks/task_b_6934c8803d648328bf76b72a4f689c60)
This commit is contained in:
+23
@@ -191,6 +191,12 @@ public class CertSignController {
|
|||||||
|
|
||||||
switch (certType) {
|
switch (certType) {
|
||||||
case "PEM":
|
case "PEM":
|
||||||
|
privateKeyFile =
|
||||||
|
validateFilePresent(
|
||||||
|
privateKeyFile, "PEM private key", "private key file is required");
|
||||||
|
certFile =
|
||||||
|
validateFilePresent(
|
||||||
|
certFile, "PEM certificate", "certificate file is required");
|
||||||
ks = KeyStore.getInstance("JKS");
|
ks = KeyStore.getInstance("JKS");
|
||||||
ks.load(null);
|
ks.load(null);
|
||||||
PrivateKey privateKey = getPrivateKeyFromPEM(privateKeyFile.getBytes(), password);
|
PrivateKey privateKey = getPrivateKeyFromPEM(privateKeyFile.getBytes(), password);
|
||||||
@@ -200,10 +206,16 @@ public class CertSignController {
|
|||||||
break;
|
break;
|
||||||
case "PKCS12":
|
case "PKCS12":
|
||||||
case "PFX":
|
case "PFX":
|
||||||
|
p12File =
|
||||||
|
validateFilePresent(
|
||||||
|
p12File, "PKCS12 keystore", "PKCS12/PFX keystore file is required");
|
||||||
ks = KeyStore.getInstance("PKCS12");
|
ks = KeyStore.getInstance("PKCS12");
|
||||||
ks.load(p12File.getInputStream(), password.toCharArray());
|
ks.load(p12File.getInputStream(), password.toCharArray());
|
||||||
break;
|
break;
|
||||||
case "JKS":
|
case "JKS":
|
||||||
|
jksfile =
|
||||||
|
validateFilePresent(
|
||||||
|
jksfile, "JKS keystore", "JKS keystore file is required");
|
||||||
ks = KeyStore.getInstance("JKS");
|
ks = KeyStore.getInstance("JKS");
|
||||||
ks.load(jksfile.getInputStream(), password.toCharArray());
|
ks.load(jksfile.getInputStream(), password.toCharArray());
|
||||||
break;
|
break;
|
||||||
@@ -251,6 +263,17 @@ public class CertSignController {
|
|||||||
GeneralUtils.generateFilename(pdf.getOriginalFilename(), "_signed.pdf"));
|
GeneralUtils.generateFilename(pdf.getOriginalFilename(), "_signed.pdf"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MultipartFile validateFilePresent(
|
||||||
|
MultipartFile file, String argumentName, String errorDescription) {
|
||||||
|
if (file == null || file.isEmpty()) {
|
||||||
|
throw ExceptionUtils.createIllegalArgumentException(
|
||||||
|
"error.invalidArgument",
|
||||||
|
"Invalid argument: {0}",
|
||||||
|
argumentName + " - " + errorDescription);
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
private PrivateKey getPrivateKeyFromPEM(byte[] pemBytes, String password)
|
private PrivateKey getPrivateKeyFromPEM(byte[] pemBytes, String password)
|
||||||
throws IOException, OperatorCreationException, PKCSException {
|
throws IOException, OperatorCreationException, PKCSException {
|
||||||
try (PEMParser pemParser =
|
try (PEMParser pemParser =
|
||||||
|
|||||||
+29
-2
@@ -1,9 +1,10 @@
|
|||||||
package stirling.software.SPDF.controller.api.security;
|
package stirling.software.SPDF.controller.api.security;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.lenient;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -107,7 +108,8 @@ class CertSignControllerTest {
|
|||||||
derCertBytes = baos.toByteArray();
|
derCertBytes = baos.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
when(pdfDocumentFactory.load(any(MultipartFile.class)))
|
lenient()
|
||||||
|
.when(pdfDocumentFactory.load(any(MultipartFile.class)))
|
||||||
.thenAnswer(
|
.thenAnswer(
|
||||||
invocation -> {
|
invocation -> {
|
||||||
MultipartFile file = invocation.getArgument(0);
|
MultipartFile file = invocation.getArgument(0);
|
||||||
@@ -167,6 +169,31 @@ class CertSignControllerTest {
|
|||||||
assertTrue(response.getBody().length > 0);
|
assertTrue(response.getBody().length > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSignPdfWithMissingPkcs12FileThrowsError() {
|
||||||
|
MockMultipartFile pdfFile =
|
||||||
|
new MockMultipartFile(
|
||||||
|
"fileInput", "test.pdf", MediaType.APPLICATION_PDF_VALUE, pdfBytes);
|
||||||
|
|
||||||
|
SignPDFWithCertRequest request = new SignPDFWithCertRequest();
|
||||||
|
request.setFileInput(pdfFile);
|
||||||
|
request.setCertType("PFX");
|
||||||
|
request.setPassword("password");
|
||||||
|
request.setShowSignature(false);
|
||||||
|
request.setReason("test");
|
||||||
|
request.setLocation("test");
|
||||||
|
request.setName("tester");
|
||||||
|
request.setPageNumber(1);
|
||||||
|
request.setShowLogo(false);
|
||||||
|
|
||||||
|
IllegalArgumentException exception =
|
||||||
|
assertThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
() -> certSignController.signPDFWithCert(request));
|
||||||
|
|
||||||
|
assertTrue(exception.getMessage().contains("PKCS12 keystore"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSignPdfWithJks() throws Exception {
|
void testSignPdfWithJks() throws Exception {
|
||||||
MockMultipartFile pdfFile =
|
MockMultipartFile pdfFile =
|
||||||
|
|||||||
Reference in New Issue
Block a user