diff --git a/.github/labeler.yml b/.github/labeler.yml
index d4d332657..20650649f 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -4,8 +4,8 @@ functions:
onboarding-ms:
- apps/onboarding-ms/**
-onboarding-sdk:
-- apps/onboarding-sdk/**
+libs:
+- libs
ops:
- .github/**
diff --git a/libs/onboarding-sdk-crypto/.gitignore b/libs/onboarding-sdk-crypto/.gitignore
new file mode 100644
index 000000000..5ff6309b7
--- /dev/null
+++ b/libs/onboarding-sdk-crypto/.gitignore
@@ -0,0 +1,38 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/libs/onboarding-sdk-crypto/README.md b/libs/onboarding-sdk-crypto/README.md
new file mode 100644
index 000000000..44329c4f6
--- /dev/null
+++ b/libs/onboarding-sdk-crypto/README.md
@@ -0,0 +1,60 @@
+# Onboarding SDK Crypto
+
+This module contains utilities to perform cryptographic operation, such digital signatures.
+
+See [Confluence page](https://pagopa.atlassian.net/wiki/spaces/SCP/pages/616857618/Firma+digitale+per+mezzo+dei+servizi+di+Aruba)
+for integration and documentation details
+
+
+### Hash signature sources
+
+It is possible to configure different hash signature sources.
+
+The sources available inside this repository are:
+
+* Pkcs7HashSignService
+
+## Pkcs7HashSignService
+
+It will use the provided private key and certificate, you must set these env variables.
+
+| Properties | Description | Default |
+|--------------------|---------------------------------------------------------------------------------|---------|
+| crypto.key.cert | The private key (PEM) used when the pkcs7 hash signature source is local | |
+| crypto.key.private | The certificate (PEM) used when the pkcs7 hash signature source is local | |
+
+
+## Installation
+
+To use this library in your projects, you can add the dependency to your pom.xml if you're using Maven:
+
+```shell script
+
+ it.pagopa.selfcare
+ onboarding-sdk-crypto
+ 0.1.0
+
+```
+If you are using Gradle, you can add the dependency to your build.gradle file:
+
+```shell script
+dependencies {
+ implementation 'it.pagopa.selfcare:onboarding-sdk-crypto:0.1.0'
+}
+```
+
+## Usage
+
+You can inject the service in the context of Quarkus or Spring (replace @ApplicationScoped with @Bean).
+
+```java script
+ @ApplicationScoped
+ public Pkcs7HashSignService pkcs7HashSignService(){
+ return new Pkcs7HashSignServiceImpl();
+ }
+
+ @ApplicationScoped
+ public PadesSignService padesSignService(Pkcs7HashSignService pkcs7HashSignService){
+ return new PadesSignServiceImpl(pkcs7HashSignService);
+ }
+ ```
\ No newline at end of file
diff --git a/libs/onboarding-sdk-crypto/pom.xml b/libs/onboarding-sdk-crypto/pom.xml
new file mode 100644
index 000000000..5a4553efe
--- /dev/null
+++ b/libs/onboarding-sdk-crypto/pom.xml
@@ -0,0 +1,72 @@
+
+ 4.0.0
+
+ it.pagopa.selfcare
+ onboarding-sdk-pom
+ 0.1.0
+ ../onboarding-sdk-pom
+
+ onboarding-sdk-crypto
+ onboarding-sdk-crypto
+ http://maven.apache.org
+
+
+
+
+ jakarta.xml.soap
+ jakarta.xml.soap-api
+ 3.0.1
+
+
+ jakarta.xml.ws
+ jakarta.xml.ws-api
+ 4.0.1
+
+
+ com.sun.xml.ws
+ jaxws-rt
+ 4.0.2
+
+
+ jakarta.activation
+ jakarta.activation-api
+
+
+ jakarta.xml.bind
+ jakarta.xml.bind-api
+
+
+ com.sun.xml.bind
+ jaxb-impl
+ 4.0.1
+
+
+ com.sun.xml.messaging.saaj
+ saaj-impl
+ 3.0.0
+
+
+ org.slf4j
+ slf4j-api
+
+
+
+ org.apache.pdfbox
+ pdfbox
+ 2.0.27
+
+
+ org.bouncycastle
+ bcprov-jdk18on
+
+
+ org.bouncycastle
+ bcpkix-jdk18on
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
diff --git a/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/PadesSignService.java b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/PadesSignService.java
new file mode 100644
index 000000000..5ebeb709c
--- /dev/null
+++ b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/PadesSignService.java
@@ -0,0 +1,10 @@
+package it.pagopa.selfcare.onboarding.crypto;
+
+
+import it.pagopa.selfcare.onboarding.crypto.entity.SignatureInformation;
+
+import java.io.File;
+
+public interface PadesSignService {
+ void padesSign(File pdfFile, File signedPdfFile, SignatureInformation signInfo);
+}
diff --git a/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/PadesSignServiceImpl.java b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/PadesSignServiceImpl.java
new file mode 100644
index 000000000..a08acc7fc
--- /dev/null
+++ b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/PadesSignServiceImpl.java
@@ -0,0 +1,43 @@
+package it.pagopa.selfcare.onboarding.crypto;
+
+import it.pagopa.selfcare.onboarding.crypto.entity.SignatureInformation;
+import it.pagopa.selfcare.onboarding.crypto.utils.CryptoUtils;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureOptions;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.Calendar;
+
+public class PadesSignServiceImpl implements PadesSignService {
+ private final Pkcs7HashSignService pkcs7Signature;
+
+ public PadesSignServiceImpl(Pkcs7HashSignService pkcs7Signature) {
+ this.pkcs7Signature = pkcs7Signature;
+ }
+
+ public void padesSign(File pdfFile, File signedPdfFile, SignatureInformation signInfo) {
+ CryptoUtils.createParentDirectoryIfNotExists(signedPdfFile);
+
+ try (FileOutputStream fos = new FileOutputStream(signedPdfFile);
+ PDDocument doc = PDDocument.load(pdfFile)){
+
+ PDSignature signature = new PDSignature();
+ signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
+ signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
+ signature.setName(signInfo.getName());
+ signature.setLocation(signInfo.getLocation());
+ signature.setReason(signInfo.getReason());
+ signature.setSignDate(Calendar.getInstance());
+ SignatureOptions signatureOptions = new SignatureOptions();
+ signatureOptions.setPreferredSignatureSize(35944);
+ doc.addSignature(signature, this.pkcs7Signature, signatureOptions);
+ doc.saveIncremental(fos);
+
+ } catch (Exception var12) {
+ throw new IllegalStateException(String.format("Something gone wrong while signing input pdf %s and storing it into %s", pdfFile.getAbsolutePath(), signedPdfFile.getAbsolutePath()), var12);
+ }
+ }
+}
+
diff --git a/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/Pkcs7HashSignService.java b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/Pkcs7HashSignService.java
new file mode 100644
index 000000000..f15152ac6
--- /dev/null
+++ b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/Pkcs7HashSignService.java
@@ -0,0 +1,6 @@
+package it.pagopa.selfcare.onboarding.crypto;
+
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
+
+public interface Pkcs7HashSignService extends SignatureInterface {
+}
diff --git a/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/Pkcs7HashSignServiceImpl.java b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/Pkcs7HashSignServiceImpl.java
new file mode 100644
index 000000000..45f093e12
--- /dev/null
+++ b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/Pkcs7HashSignServiceImpl.java
@@ -0,0 +1,57 @@
+package it.pagopa.selfcare.onboarding.crypto;
+
+
+import it.pagopa.selfcare.onboarding.crypto.config.LocalCryptoConfig;
+import it.pagopa.selfcare.onboarding.crypto.config.LocalCryptoInitializer;
+import it.pagopa.selfcare.onboarding.crypto.utils.CMSTypedDataInputStream;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaCertStore;
+import org.bouncycastle.cms.*;
+import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+import org.bouncycastle.util.Store;
+
+import java.io.*;
+import java.security.cert.CertificateEncodingException;
+import java.util.Collections;
+
+/**
+ * Implementation of {@link Pkcs7HashSignService} which will use provided private and public keys to perform sign operations
+ */
+public class Pkcs7HashSignServiceImpl implements Pkcs7HashSignService {
+
+ private final CMSSignedDataGenerator cmsSignGenerator;
+
+ public Pkcs7HashSignServiceImpl() {
+ try {
+ LocalCryptoConfig localCryptoConfig = LocalCryptoInitializer.initializeConfig();
+ BouncyCastleProvider bc = new BouncyCastleProvider();
+ Store> certStore = new JcaCertStore(Collections.singletonList(localCryptoConfig.getCertificate()));
+
+ cmsSignGenerator = new CMSSignedDataGenerator();
+ ContentSigner sha512Signer = new JcaContentSignerBuilder("SHA256WithRSA").setProvider(bc).build(localCryptoConfig.getPrivateKey());
+
+ cmsSignGenerator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(
+ new JcaDigestCalculatorProviderBuilder().setProvider(bc).build()).build(sha512Signer, new X509CertificateHolder(localCryptoConfig.getCertificate().getEncoded())
+ ));
+ cmsSignGenerator.addCertificates(certStore);
+ } catch (CertificateEncodingException | OperatorCreationException | CMSException | IOException e) {
+ throw new IllegalStateException("Something gone wrong while initializing CertStore using provided private and public key", e);
+ }
+ }
+
+ public byte[] sign(InputStream is) throws IOException {
+ try {
+ CMSTypedDataInputStream msg = new CMSTypedDataInputStream(is);
+ CMSSignedData signedData = cmsSignGenerator.generate(msg, false);
+ return signedData.getEncoded();
+ } catch (CMSException e) {
+ throw new IllegalArgumentException("Something gone wrong while performing pkcs7 hash sign", e);
+ }
+ }
+
+}
diff --git a/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/config/LocalCryptoConfig.java b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/config/LocalCryptoConfig.java
new file mode 100644
index 000000000..049e43e9a
--- /dev/null
+++ b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/config/LocalCryptoConfig.java
@@ -0,0 +1,26 @@
+package it.pagopa.selfcare.onboarding.crypto.config;
+
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+
+public class LocalCryptoConfig {
+
+ private Certificate certificate;
+ private PrivateKey privateKey;
+
+ public Certificate getCertificate() {
+ return certificate;
+ }
+
+ public void setCertificate(Certificate certificate) {
+ this.certificate = certificate;
+ }
+
+ public PrivateKey getPrivateKey() {
+ return privateKey;
+ }
+
+ public void setPrivateKey(PrivateKey privateKey) {
+ this.privateKey = privateKey;
+ }
+}
diff --git a/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/config/LocalCryptoInitializer.java b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/config/LocalCryptoInitializer.java
new file mode 100644
index 000000000..8c3e5ff13
--- /dev/null
+++ b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/config/LocalCryptoInitializer.java
@@ -0,0 +1,29 @@
+package it.pagopa.selfcare.onboarding.crypto.config;
+
+import it.pagopa.selfcare.onboarding.crypto.utils.CryptoUtils;
+import org.apache.commons.lang3.StringUtils;
+
+
+public class LocalCryptoInitializer {
+
+ private LocalCryptoInitializer(){}
+
+ public static LocalCryptoConfig initializeConfig() {
+ LocalCryptoConfig config = new LocalCryptoConfig();
+ String cert = System.getProperty("crypto.key.cert");
+ String pKey = System.getProperty("crypto.key.private");
+
+ try {
+ if(StringUtils.isBlank(cert) || StringUtils.isBlank(pKey)){
+ throw new IllegalStateException("Define private and cert values in order to perform locally sign operations");
+ }
+
+ config.setCertificate(CryptoUtils.getCertificate(cert));
+ config.setPrivateKey(CryptoUtils.getPrivateKey(pKey));
+ } catch (Exception e) {
+ throw new IllegalStateException("Something gone wrong while loading crypto private and public keys", e);
+ }
+
+ return config;
+ }
+}
diff --git a/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/entity/SignatureInformation.java b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/entity/SignatureInformation.java
new file mode 100644
index 000000000..d12a5f6ce
--- /dev/null
+++ b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/entity/SignatureInformation.java
@@ -0,0 +1,37 @@
+package it.pagopa.selfcare.onboarding.crypto.entity;
+
+public class SignatureInformation {
+ private String name;
+ private String location;
+ private String reason;
+
+ public SignatureInformation(String name, String location, String reason) {
+ this.name = name;
+ this.location = location;
+ this.reason = reason;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getLocation() {
+ return location;
+ }
+
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
+ public String getReason() {
+ return reason;
+ }
+
+ public void setReason(String reason) {
+ this.reason = reason;
+ }
+}
diff --git a/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/utils/CMSTypedDataInputStream.java b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/utils/CMSTypedDataInputStream.java
new file mode 100644
index 000000000..01de4d265
--- /dev/null
+++ b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/utils/CMSTypedDataInputStream.java
@@ -0,0 +1,37 @@
+package it.pagopa.selfcare.onboarding.crypto.utils;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.cms.CMSTypedData;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class CMSTypedDataInputStream implements CMSTypedData {
+ InputStream in;
+
+ public CMSTypedDataInputStream(InputStream is) {
+ in = is;
+ }
+
+ @Override
+ public ASN1ObjectIdentifier getContentType() {
+ return PKCSObjectIdentifiers.data;
+ }
+
+ @Override
+ public Object getContent() {
+ return in;
+ }
+
+ @Override
+ public void write(OutputStream out) throws IOException {
+ byte[] buffer = new byte[8 * 1024];
+ int read;
+ while ((read = in.read(buffer)) != -1) {
+ out.write(buffer, 0, read);
+ }
+ in.close();
+ }
+}
\ No newline at end of file
diff --git a/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/utils/CryptoUtils.java b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/utils/CryptoUtils.java
new file mode 100644
index 000000000..1984fdde7
--- /dev/null
+++ b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/utils/CryptoUtils.java
@@ -0,0 +1,76 @@
+package it.pagopa.selfcare.onboarding.crypto.utils;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.KeyFactory;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Base64;
+
+public class CryptoUtils {
+
+ private CryptoUtils() {}
+
+ public static void createParentDirectoryIfNotExists(File destFile) {
+ Path destDir = destFile.toPath().getParent();
+ if (!Files.exists(destDir)) {
+ try {
+ Files.createDirectories(destDir);
+ } catch (IOException var3) {
+ throw new IllegalArgumentException(String.format("Something gone wrong while creating destination folder: %s", destDir), var3);
+ }
+ }
+
+ }
+
+
+
+ public static byte[] getDigest(InputStream is) {
+ try {
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ return digest.digest(is.readAllBytes());
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException("Something gone wrong selecting digest algorithm", e);
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Something gone wrong while reading inputStream", e);
+ }
+ }
+
+ public static X509Certificate getCertificate(String cert) throws IOException, CertificateException {
+ try(
+ InputStream is = new ByteArrayInputStream(cert.getBytes(StandardCharsets.UTF_8))
+ ) {
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ return (X509Certificate) cf.generateCertificate(is);
+ }
+ }
+
+ public static RSAPrivateKey getPrivateKey(String privateKey) throws InvalidKeySpecException, NoSuchAlgorithmException, IOException {
+ String keyStringFormat = pemToString(privateKey);
+ try(
+ InputStream is = new ByteArrayInputStream(Base64.getDecoder().decode(keyStringFormat))
+ ) {
+ PKCS8EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec(is.readAllBytes());
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ return (RSAPrivateKey) kf.generatePrivate(encodedKeySpec);
+ }
+ }
+
+ public static String pemToString(String target) {
+ return target
+ .replaceAll("^-----BEGIN[A-Z|\\s]+-----", "")
+ .replaceAll("\\n+", "")
+ .replaceAll("-----END[A-Z|\\s]+-----$", "");
+ }
+}
diff --git a/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/utils/SoapLoggingHandler.java b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/utils/SoapLoggingHandler.java
new file mode 100644
index 000000000..30300b30f
--- /dev/null
+++ b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/utils/SoapLoggingHandler.java
@@ -0,0 +1,63 @@
+package it.pagopa.selfcare.onboarding.crypto.utils;
+
+import jakarta.xml.soap.SOAPException;
+import jakarta.xml.soap.SOAPMessage;
+import jakarta.xml.ws.handler.MessageContext;
+import jakarta.xml.ws.handler.soap.SOAPHandler;
+import jakarta.xml.ws.handler.soap.SOAPMessageContext;
+import org.slf4j.Logger;
+
+import javax.xml.namespace.QName;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Set;
+
+
+public class SoapLoggingHandler implements SOAPHandler {
+
+ private static final Logger log = org.slf4j.LoggerFactory.getLogger(SoapLoggingHandler.class);
+
+ @Override
+ public void close(MessageContext msg) {
+ // Do Nothing
+ }
+
+ @Override
+ public boolean handleFault(SOAPMessageContext msg) {
+ SOAPMessage message = msg.getMessage();
+ try {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ message.writeTo(outputStream);
+ log.info("Obtained a fault message: {}", outputStream);
+ } catch (SOAPException | IOException e) {
+ log.error("Something gone wrong while tracing soap fault message");
+ }
+ return true;
+ }
+
+ @Override
+ public boolean handleMessage(SOAPMessageContext msg) {
+ if(log.isDebugEnabled()) {
+ SOAPMessage message = msg.getMessage();
+ boolean isOutboundMessage = (Boolean) msg.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
+ String msgType = isOutboundMessage
+ ? "OUTBOUND MESSAGE"
+ : "INBOUND MESSAGE";
+ try {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ message.writeTo(outputStream);
+ log.debug("Obtained a {} message: {}", msgType, outputStream);
+ } catch (SOAPException | IOException e) {
+ log.error(String.format("Something gone wrong while tracing soap %s", msgType));
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public Set getHeaders() {
+ return Collections.emptySet();
+ }
+
+}
\ No newline at end of file
diff --git a/libs/onboarding-sdk-crypto/src/test/java/it/pagopa/selfcare/onboarding/crypto/PadesSignServiceTest.java b/libs/onboarding-sdk-crypto/src/test/java/it/pagopa/selfcare/onboarding/crypto/PadesSignServiceTest.java
new file mode 100644
index 000000000..2dd64efe6
--- /dev/null
+++ b/libs/onboarding-sdk-crypto/src/test/java/it/pagopa/selfcare/onboarding/crypto/PadesSignServiceTest.java
@@ -0,0 +1,140 @@
+package it.pagopa.selfcare.onboarding.crypto;
+
+
+import it.pagopa.selfcare.onboarding.crypto.config.LocalCryptoConfig;
+import it.pagopa.selfcare.onboarding.crypto.entity.SignatureInformation;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.io.IOUtils;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageTree;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.util.Store;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.util.Collection;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+
+public class PadesSignServiceTest {
+
+ private PadesSignService service;
+
+ // mocked data will not be aligned with timestamp alway updated, thus base test could not successfully sign
+ protected boolean verifySignerInformation = true;
+ protected Path inputFilePath = Path.of("src/test/resources/signTest.pdf");
+
+ private Pkcs7HashSignService pkcs7HashSignService;
+
+ @BeforeEach
+ void setup() throws IOException {
+ pkcs7HashSignService = mock(Pkcs7HashSignService.class);
+ service = new PadesSignServiceImpl(pkcs7HashSignService);
+ }
+
+ @Test
+ protected void testPadesSign() throws IOException, GeneralSecurityException, OperatorCreationException, CMSException {
+ File inputFile = inputFilePath.toFile();
+ File outputFile = getOutputPadesFile();
+ if (outputFile.exists()) {
+ Assertions.assertTrue(outputFile.delete());
+ }
+
+ when(pkcs7HashSignService.sign(any())).thenReturn(Files.readAllBytes(inputFilePath));
+
+ service.padesSign(inputFile, outputFile, new SignatureInformation("PagoPA S.P.A", "Rome", "onboarding contract"));
+ Assertions.assertTrue(outputFile.exists());
+
+ checkPadesSignature(inputFile, outputFile);
+ }
+
+ protected File getOutputPadesFile() {
+ return Path.of("target/tmp/signedSignTest-selfSigned.pdf").toFile();
+ }
+
+ @SuppressWarnings("unchecked")
+ private void checkPadesSignature(File origFile, File signedFile)
+ throws IOException, CMSException, OperatorCreationException, GeneralSecurityException
+ {
+ PDDocument document = PDDocument.load(origFile);
+ // get string representation of pages COSObject
+ String origPageKey = document.getDocumentCatalog().getCOSObject().getItem(COSName.PAGES).toString();
+ document.close();
+
+ document = PDDocument.load(signedFile);
+
+ // early detection of problems in the page structure
+ int p = 0;
+ PDPageTree pageTree = document.getPages();
+ for (PDPage page : document.getPages())
+ {
+ Assertions.assertEquals(p, pageTree.indexOf(page));
+ ++p;
+ }
+
+ Assertions.assertEquals(origPageKey, document.getDocumentCatalog().getCOSObject().getItem(COSName.PAGES).toString());
+
+ List signatureDictionaries = document.getSignatureDictionaries();
+ if (signatureDictionaries.isEmpty())
+ {
+ Assertions.fail("no signature found");
+ }
+ for (PDSignature sig : document.getSignatureDictionaries())
+ {
+ byte[] contents = sig.getContents();
+
+ byte[] buf = sig.getSignedContent(new FileInputStream(signedFile));
+
+ // verify that getSignedContent() brings the same content
+ // regardless whether from an InputStream or from a byte array
+ FileInputStream fis2 = new FileInputStream(signedFile);
+ byte[] buf2 = sig.getSignedContent(IOUtils.toByteArray(fis2));
+ Assertions.assertArrayEquals(buf, buf2);
+ fis2.close();
+
+ // verify that all getContents() methods returns the same content
+ FileInputStream fis3 = new FileInputStream(signedFile);
+ byte[] contents2 = sig.getContents(IOUtils.toByteArray(fis3));
+ Assertions.assertArrayEquals(contents, contents2);
+ fis3.close();
+ byte[] contents3 = sig.getContents(new FileInputStream(signedFile));
+ Assertions.assertArrayEquals(contents, contents3);
+ }
+ document.close();
+ }
+
+ @Test
+ void testHandleException() throws IOException {
+ File inputFile = inputFilePath.toFile();
+ File outputFile = getOutputPadesFile();
+ if (outputFile.exists()) {
+ Assertions.assertTrue(outputFile.delete());
+ }
+
+
+ when(pkcs7HashSignService.sign(any())).thenThrow(new RuntimeException());
+ assertThrows(IllegalStateException.class, () -> service.padesSign(inputFile, outputFile, new SignatureInformation("PagoPA S.P.A", "Rome", "onboarding contract")));
+ }
+}
\ No newline at end of file
diff --git a/libs/onboarding-sdk-crypto/src/test/java/it/pagopa/selfcare/onboarding/crypto/Pkcs7HashSignServiceTest.java b/libs/onboarding-sdk-crypto/src/test/java/it/pagopa/selfcare/onboarding/crypto/Pkcs7HashSignServiceTest.java
new file mode 100644
index 000000000..6059cec79
--- /dev/null
+++ b/libs/onboarding-sdk-crypto/src/test/java/it/pagopa/selfcare/onboarding/crypto/Pkcs7HashSignServiceTest.java
@@ -0,0 +1,90 @@
+package it.pagopa.selfcare.onboarding.crypto;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Base64;
+
+class Pkcs7HashSignServiceTest {
+
+ private static final String testCert = "-----BEGIN CERTIFICATE-----\n" +
+ "MIIDnzCCAoegAwIBAgIUJ8/0z+sR6Llr9FcIGoc5nvZQydgwDQYJKoZIhvcNAQEL\n" +
+ "BQAwXzELMAkGA1UEBhMCSVQxDTALBgNVBAgMBFJPTUUxDTALBgNVBAcMBFJPTUUx\n" +
+ "DjAMBgNVBAoMBUlEUEFZMQ4wDAYDVQQLDAVJRFBBWTESMBAGA1UEAwwJbG9jYWxo\n" +
+ "b3N0MB4XDTIyMTEwOTE1MTI0NFoXDTMyMDkxNzE1MTI0NFowXzELMAkGA1UEBhMC\n" +
+ "SVQxDTALBgNVBAgMBFJPTUUxDTALBgNVBAcMBFJPTUUxDjAMBgNVBAoMBUlEUEFZ\n" +
+ "MQ4wDAYDVQQLDAVJRFBBWTESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG\n" +
+ "9w0BAQEFAAOCAQ8AMIIBCgKCAQEArDOJKswwCaKdYJbaHZz3bgEIl7z1ArZpNI54\n" +
+ "ZGaXcRitiwjr/W9fenW69mG7IAlITuPtaIu4iggXTcSRuaulres2EvuP7KjL0tfo\n" +
+ "x/PstqaMZzLF8wOYfJE4iJ8ffcQL67LJ3/Wwn2FhYVV+4D2AYW8QPdRm406HJG7b\n" +
+ "NKLmdM9AFUQp6zoTvNegyWQyAfH40i72UopltDubcAykD6YgkRctCtKd8h/BRpIR\n" +
+ "tMn0AGLM/o5qwYu+eCAy8/7Ppj3HzCwHkDOJad/g2pRj4soJdvn5rP6TM4OVtZ7V\n" +
+ "ehxionkaccBPcyDGSrIo5837XYaGv3r7Rn0rCplfxnU4Gtmd5wIDAQABo1MwUTAd\n" +
+ "BgNVHQ4EFgQUPYfJeHRHwSLmcueB8jUQSHUReVIwHwYDVR0jBBgwFoAUPYfJeHRH\n" +
+ "wSLmcueB8jUQSHUReVIwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC\n" +
+ "AQEAK34LEHSVM44Wwbs9nKDKeQTRGosdd+gQSrqGf3nI0vkhckuaoYPnuFKi+eo2\n" +
+ "r+J6xXgqhQfrvhXnYxNEJr9U+9ELBc3IjG6bTUS6HyWhu2PJCeckxQJqonVntl99\n" +
+ "jmEr4G7QJeDc9oJmC0NJqBmQS/D0tMxChNWpYe1AoGXwqc4S6NTd3x2Z8THzv8du\n" +
+ "MMn7+1f/VOWe7/Iuuvx5DHN2JFi0lvhMqwglIweGn/qLGB0+r9GM+QlfGuZvUey2\n" +
+ "x3C0DLQnNIkNKktGjaNjCmpZcd9SIVi6TOPpR+AxlIddYvUXu4GYVXyfDPgzPeha\n" +
+ "JDiI4WMkIMmYSzhMc/lfuDMGow==\n" +
+ "-----END CERTIFICATE-----";
+
+ private static final String testPrivateKey = "-----BEGIN PRIVATE KEY-----\n" +
+ "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCsM4kqzDAJop1g\n" +
+ "ltodnPduAQiXvPUCtmk0jnhkZpdxGK2LCOv9b196dbr2YbsgCUhO4+1oi7iKCBdN\n" +
+ "xJG5q6Wt6zYS+4/sqMvS1+jH8+y2poxnMsXzA5h8kTiInx99xAvrssnf9bCfYWFh\n" +
+ "VX7gPYBhbxA91GbjTockbts0ouZ0z0AVRCnrOhO816DJZDIB8fjSLvZSimW0O5tw\n" +
+ "DKQPpiCRFy0K0p3yH8FGkhG0yfQAYsz+jmrBi754IDLz/s+mPcfMLAeQM4lp3+Da\n" +
+ "lGPiygl2+fms/pMzg5W1ntV6HGKieRpxwE9zIMZKsijnzftdhoa/evtGfSsKmV/G\n" +
+ "dTga2Z3nAgMBAAECggEAEC6FmMJ4Tyd7T3zNgVPjQnCRbKTihz858qjislibqZKO\n" +
+ "mE6d0oJ5P+o5R/bWHUQSCevMPvNGQ55QBkxO/1ocZxP/0FfYZf5UrPsCEmwfFejf\n" +
+ "r8DrLhNr7GS/IcOGM4zNK/hwlP2i+88sVfexRQQygLVtmsnPY1PZSjiqm68lJdu+\n" +
+ "aP8TYM10y1aeiYnfuUYvnvXJFXeTEockhaUJTmeIQNbbUy+pyJ0mAPASPtXRLr8h\n" +
+ "UflutICnWcx4v/qkCn1jmHw+NMA4q7hOH7UuOAqj53FqGMN+IWfjMmmYoQ7MVURx\n" +
+ "8CrnEtlCOua+C8EEIFL2ylvV7X0cv/DqCJLVQoegsQKBgQDLzMaAjNgD8xSXp+Gj\n" +
+ "beeUsSGptEaGMuA89AzyTnCyvU9a1HGwDAghoQPae+pVk7R5uokojWkBVzP/kKxv\n" +
+ "ZldGwPOegUUdBLS4yJML+OkqtoCgf3Mbcozm5dVYtx7bYdhh3PswzRmn/h/YjEAz\n" +
+ "+/mxi6dJir0k0Nd4YNtQbzBctwKBgQDYTtSmJvVQdOHnzqA/LRmMF1l+HaqLuDfu\n" +
+ "B4rDlxCdDfOAvHqz+3YapP3B4MQuz29TSDqwAnzoN2XZX5B6g/jKauWpAwZkFXuO\n" +
+ "fqcfNG/+MewTcHIYNm+EtgXtIsnCXDfAeXdQapzNsOX+XSF/aWcgGHg18xOBPt0R\n" +
+ "7Aoa/h34UQKBgQCsCzGjwcJ2CxXeNPYxfg1ao/HUDoDet0I/kpL/VqKi8Vd1SRS0\n" +
+ "VmPi58eWALfBCJD5ljRFjKMRY6lc3KgE3vNconTG4UAUEC30NDaWi8liqnCJjS4C\n" +
+ "BMDYBzwEyYn+D2qYqvFOsEYxYEFIEJX+jH+sl0VguwOTec38LF/YVhUQnwKBgG5u\n" +
+ "2Kw3SZkZA1ioqjF24gsexKbZmH+avps8qICw+F9mhwIbt/15jVOPFqrMCPzpFKoN\n" +
+ "P0ErFAAugEYZPxb9l6AoMTY3gCTKvvkB+mq5B9BcRm2qQ+XOrOKxV5c44o7jK+eN\n" +
+ "W/fnZkSxYsqZW4fEFU1SkNTiU/vxT0ZeHs6nHD/xAoGAOIqaqQnJfGj/wLo3Z9o5\n" +
+ "/Oxu1zTPGZC6SqpdygCjlQ0kQ8Bp0LV7nL06/VCHAHI2lF12xApRnFk7GY3xyqK8\n" +
+ "nYxeRASCj3GGmLupGshtfCtDBeysE2h7kj3Bo0d6g1Ye+j8BUZuZaZm6WNlo7cgE\n" +
+ "NLHn1k0IpmXFOiFa1Y1D6Bc=\n" +
+ "-----END PRIVATE KEY-----";
+
+ private Pkcs7HashSignService service;
+
+ @BeforeEach
+ void setup(){
+ System.setProperty("crypto.key.cert", testCert);
+ System.setProperty("crypto.key.private", testPrivateKey);
+ service = new Pkcs7HashSignServiceImpl();
+ }
+ @Test
+ void shouldAssertEncoding() throws IOException {
+ try (FileInputStream fis = new FileInputStream(Path.of("src/test/resources/signTest.pdf").toFile())) {
+ byte[] result = service.sign(fis);
+ Assertions.assertNotNull(result);
+ checkPkcs7HashSign(Base64.getEncoder().encodeToString(result));
+ }
+ }
+
+ protected void checkPkcs7HashSign(String result) {
+ String expectedPrefix = "MIAGCSqGSIb3DQEHAqCAMIACAQExDTALBglghkgBZQMEAgEwCwYJKoZIhvcNAQcBoIAwggOfMIICh6ADAgECAhQnz";
+ Assertions.assertTrue(
+ result.startsWith(expectedPrefix),
+ String.format("Prefix unexpected:\n%s\n%s", result, expectedPrefix)
+ );
+ }
+}
diff --git a/libs/onboarding-sdk-crypto/src/test/resources/signTest.pdf b/libs/onboarding-sdk-crypto/src/test/resources/signTest.pdf
new file mode 100644
index 000000000..19d5aefec
Binary files /dev/null and b/libs/onboarding-sdk-crypto/src/test/resources/signTest.pdf differ
diff --git a/libs/onboarding-sdk-pom/pom.xml b/libs/onboarding-sdk-pom/pom.xml
index 9af27883b..0c0ea2667 100644
--- a/libs/onboarding-sdk-pom/pom.xml
+++ b/libs/onboarding-sdk-pom/pom.xml
@@ -15,6 +15,8 @@
17
17
+ 5.7.2
+ 5.7.0
@@ -29,8 +31,30 @@
../onboarding-sdk-azure-storage
../onboarding-sdk-common
../onboarding-sdk-product
+ ../onboarding-sdk-crypto
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${junit-jupiter.version}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit-jupiter.version}
+ test
+
+
+ org.mockito
+ mockito-core
+ ${mockito.version}
+ test
+
+
+
diff --git a/libs/onboarding-sdk-product/pom.xml b/libs/onboarding-sdk-product/pom.xml
index d65e1c1b7..1b29b83e9 100644
--- a/libs/onboarding-sdk-product/pom.xml
+++ b/libs/onboarding-sdk-product/pom.xml
@@ -12,7 +12,6 @@
2.15.2
- 5.7.2
@@ -30,19 +29,6 @@
${project.version}
-
- org.junit.jupiter
- junit-jupiter-api
- ${junit-jupiter.version}
- test
-
-
- org.junit.jupiter
- junit-jupiter-engine
- ${junit-jupiter.version}
- test
-
-
diff --git a/libs/pom.xml b/libs/pom.xml
index 5a58db456..f5c434c12 100644
--- a/libs/pom.xml
+++ b/libs/pom.xml
@@ -17,6 +17,18 @@
+
+
+
+ io.quarkus.platform
+ quarkus-bom
+ 3.4.2
+ pom
+ import
+
+
+
+
diff --git a/test-coverage/pom.xml b/test-coverage/pom.xml
index 72776ef51..a1ba02965 100644
--- a/test-coverage/pom.xml
+++ b/test-coverage/pom.xml
@@ -89,6 +89,11 @@
onboarding-sdk-azure-storage
0.1.0
+
+ it.pagopa.selfcare
+ onboarding-sdk-crypto
+ 0.1.0
+