From 7a0e02276dbae39b71d2f9a5e94f77cce7a9ddf6 Mon Sep 17 00:00:00 2001 From: manuraf Date: Tue, 14 Nov 2023 17:18:58 +0100 Subject: [PATCH 01/12] java 17 and quarkus 3.5.1 --- apps/onboarding-functions/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/onboarding-functions/pom.xml b/apps/onboarding-functions/pom.xml index 9ac1ee5c8..2e0658a82 100644 --- a/apps/onboarding-functions/pom.xml +++ b/apps/onboarding-functions/pom.xml @@ -13,12 +13,12 @@ 3.11.0 - 11 + 17 UTF-8 UTF-8 quarkus-bom io.quarkus.platform - 3.3.3 + 3.5.1 true 3.1.2 0.1.0 From db071d533eb85a30e74bc8f2a5b4832cdd7b8d3a Mon Sep 17 00:00:00 2001 From: manuraf Date: Wed, 15 Nov 2023 11:55:40 +0100 Subject: [PATCH 02/12] utility classes to perform cryptographic operation --- libs/onboarding-sdk-crypto/.gitignore | 38 +++++++++ libs/onboarding-sdk-crypto/README.md | 60 ++++++++++++++ libs/onboarding-sdk-crypto/pom.xml | 78 +++++++++++++++++++ .../onboarding/crypto/PadesSignService.java | 10 +++ .../crypto/PadesSignServiceImpl.java | 72 +++++++++++++++++ .../crypto/Pkcs7HashSignService.java | 6 ++ .../crypto/Pkcs7HashSignServiceImpl.java | 57 ++++++++++++++ .../crypto/config/LocalCryptoConfig.java | 26 +++++++ .../crypto/config/LocalCryptoInitializer.java | 30 +++++++ .../crypto/entity/SignatureInformation.java | 31 ++++++++ .../onboarding/crypto/utils/CryptoUtils.java | 76 ++++++++++++++++++ .../crypto/utils/PropertiesLoader.java | 22 ++++++ .../crypto/utils/SoapLoggingHandler.java | 63 +++++++++++++++ .../main/resources/cryptoConfig.properties | 4 + libs/onboarding-sdk-pom/pom.xml | 1 + 15 files changed, 574 insertions(+) create mode 100644 libs/onboarding-sdk-crypto/.gitignore create mode 100644 libs/onboarding-sdk-crypto/README.md create mode 100644 libs/onboarding-sdk-crypto/pom.xml create mode 100644 libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/PadesSignService.java create mode 100644 libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/PadesSignServiceImpl.java create mode 100644 libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/Pkcs7HashSignService.java create mode 100644 libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/Pkcs7HashSignServiceImpl.java create mode 100644 libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/config/LocalCryptoConfig.java create mode 100644 libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/config/LocalCryptoInitializer.java create mode 100644 libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/entity/SignatureInformation.java create mode 100644 libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/utils/CryptoUtils.java create mode 100644 libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/utils/PropertiesLoader.java create mode 100644 libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/utils/SoapLoggingHandler.java create mode 100644 libs/onboarding-sdk-crypto/src/main/resources/cryptoConfig.properties 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..d874da3fb --- /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. + +| ENV | Description | Default | +|----------------------|---------------------------------------------------------------------------------|---------| +| CRYPTO_PRIVATE_KEY | The private key (PEM) used when the pkcs7 hash signature source is local | | +| CRYPTO_CERT | 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..47d4f8738 --- /dev/null +++ b/libs/onboarding-sdk-crypto/pom.xml @@ -0,0 +1,78 @@ + +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.0 + + + jakarta.xml.ws + jakarta.xml.ws-api + 4.0.0 + + + com.sun.xml.ws + jaxws-rt + 4.0.0 + + + jakarta.activation + jakarta.activation-api + 2.1.0 + + + jakarta.xml.bind + jakarta.xml.bind-api + 4.0.0 + + + com.sun.xml.bind + jaxb-impl + 4.0.1 + + + com.sun.xml.messaging.saaj + saaj-impl + 3.0.0 + + + org.slf4j + slf4j-api + 1.7.5 + + + + org.apache.pdfbox + pdfbox + 2.0.27 + + + org.bouncycastle + bcprov-jdk18on + 1.76 + + + org.bouncycastle + bcpkix-jdk18on + 1.76 + + + org.apache.commons + commons-lang3 + 3.13.0 + + + 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..63febd1ce --- /dev/null +++ b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/PadesSignServiceImpl.java @@ -0,0 +1,72 @@ +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); + + try { + PDDocument doc = PDDocument.load(pdfFile); + + try { + 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(18944); + doc.addSignature(signature, this.pkcs7Signature, signatureOptions); + doc.saveIncremental(fos); + } catch (Throwable var10) { + if (doc != null) { + try { + doc.close(); + } catch (Throwable var9) { + var10.addSuppressed(var9); + } + } + + throw var10; + } + + if (doc != null) { + doc.close(); + } + } catch (Throwable var11) { + try { + fos.close(); + } catch (Throwable var8) { + var11.addSuppressed(var8); + } + + throw var11; + } + + fos.close(); + } 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..c6cec0249 --- /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 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.IOException; +import java.io.InputStream; +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 { + CMSTypedData msg = new CMSProcessableByteArray(is.readAllBytes()); + 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..9f0402a04 --- /dev/null +++ b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/config/LocalCryptoInitializer.java @@ -0,0 +1,30 @@ +package it.pagopa.selfcare.onboarding.crypto.config; + +import it.pagopa.selfcare.onboarding.crypto.utils.CryptoUtils; +import it.pagopa.selfcare.onboarding.crypto.utils.PropertiesLoader; +import org.apache.commons.lang3.StringUtils; + +import java.util.Properties; + +public class LocalCryptoInitializer { + + public static LocalCryptoConfig initializeConfig() { + Properties properties = PropertiesLoader.loadProperties("cryptoConfig.properties"); + LocalCryptoConfig config = new LocalCryptoConfig(); + String cert = properties.getProperty("crypto.key.cert"); + String pKey = properties.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..76f9c20d4 --- /dev/null +++ b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/entity/SignatureInformation.java @@ -0,0 +1,31 @@ +package it.pagopa.selfcare.onboarding.crypto.entity; + +public class SignatureInformation { + private String name; + private String location; + private String 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/CryptoUtils.java b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/utils/CryptoUtils.java new file mode 100644 index 000000000..a25f709e3 --- /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.LinkOption; +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 { + + + 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/PropertiesLoader.java b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/utils/PropertiesLoader.java new file mode 100644 index 000000000..83f64f2c7 --- /dev/null +++ b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/utils/PropertiesLoader.java @@ -0,0 +1,22 @@ +package it.pagopa.selfcare.onboarding.crypto.utils; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +public class PropertiesLoader { + + public static Properties loadProperties(String fileName) { + Properties properties = new Properties(); + try (InputStream input = PropertiesLoader.class.getClassLoader().getResourceAsStream(fileName)) { + if (input == null) { + System.out.println("Sorry, unable to find " + fileName); + return properties; + } + properties.load(input); + } catch (IOException ex) { + ex.printStackTrace(); + } + return properties; + } +} 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/main/resources/cryptoConfig.properties b/libs/onboarding-sdk-crypto/src/main/resources/cryptoConfig.properties new file mode 100644 index 000000000..a2370e5b2 --- /dev/null +++ b/libs/onboarding-sdk-crypto/src/main/resources/cryptoConfig.properties @@ -0,0 +1,4 @@ +# The private key (PEM) used when the pkcs7 hash signature source is 'local' +crypto.key.private=${CRYPTO_PRIVATE_KEY:} +# The certificate (PEM) used when the pkcs7 hash signature source is 'local' +crypto.key.cert=${CRYPTO_CERT:} \ No newline at end of file diff --git a/libs/onboarding-sdk-pom/pom.xml b/libs/onboarding-sdk-pom/pom.xml index 9af27883b..b9d2877ff 100644 --- a/libs/onboarding-sdk-pom/pom.xml +++ b/libs/onboarding-sdk-pom/pom.xml @@ -29,6 +29,7 @@ ../onboarding-sdk-azure-storage ../onboarding-sdk-common ../onboarding-sdk-product + ../onboarding-sdk-crypto From b81cb515e9e99bfeaee93ad33a9a8f9225ac7bbd Mon Sep 17 00:00:00 2001 From: manuraf Date: Wed, 15 Nov 2023 12:02:56 +0100 Subject: [PATCH 03/12] remove accidentally upgrada quarkus --- apps/onboarding-functions/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/onboarding-functions/pom.xml b/apps/onboarding-functions/pom.xml index 2e0658a82..9ac1ee5c8 100644 --- a/apps/onboarding-functions/pom.xml +++ b/apps/onboarding-functions/pom.xml @@ -13,12 +13,12 @@ 3.11.0 - 17 + 11 UTF-8 UTF-8 quarkus-bom io.quarkus.platform - 3.5.1 + 3.3.3 true 3.1.2 0.1.0 From b77bda726d33dcd06fa878a6029c4e82d1594ad4 Mon Sep 17 00:00:00 2001 From: manuraf Date: Wed, 15 Nov 2023 16:05:41 +0100 Subject: [PATCH 04/12] load properties by env variables --- .../crypto/config/LocalCryptoInitializer.java | 7 ++---- .../crypto/utils/PropertiesLoader.java | 22 ------------------- .../main/resources/cryptoConfig.properties | 4 ---- 3 files changed, 2 insertions(+), 31 deletions(-) delete mode 100644 libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/utils/PropertiesLoader.java delete mode 100644 libs/onboarding-sdk-crypto/src/main/resources/cryptoConfig.properties 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 index 9f0402a04..770a9a0f9 100644 --- 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 @@ -1,18 +1,15 @@ package it.pagopa.selfcare.onboarding.crypto.config; import it.pagopa.selfcare.onboarding.crypto.utils.CryptoUtils; -import it.pagopa.selfcare.onboarding.crypto.utils.PropertiesLoader; import org.apache.commons.lang3.StringUtils; -import java.util.Properties; public class LocalCryptoInitializer { public static LocalCryptoConfig initializeConfig() { - Properties properties = PropertiesLoader.loadProperties("cryptoConfig.properties"); LocalCryptoConfig config = new LocalCryptoConfig(); - String cert = properties.getProperty("crypto.key.cert"); - String pKey = properties.getProperty("crypto.key.private"); + String cert = System.getProperty("crypto.key.cert"); + String pKey = System.getProperty("crypto.key.private"); try { if(StringUtils.isBlank(cert) || StringUtils.isBlank(pKey)){ diff --git a/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/utils/PropertiesLoader.java b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/utils/PropertiesLoader.java deleted file mode 100644 index 83f64f2c7..000000000 --- a/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/utils/PropertiesLoader.java +++ /dev/null @@ -1,22 +0,0 @@ -package it.pagopa.selfcare.onboarding.crypto.utils; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Properties; - -public class PropertiesLoader { - - public static Properties loadProperties(String fileName) { - Properties properties = new Properties(); - try (InputStream input = PropertiesLoader.class.getClassLoader().getResourceAsStream(fileName)) { - if (input == null) { - System.out.println("Sorry, unable to find " + fileName); - return properties; - } - properties.load(input); - } catch (IOException ex) { - ex.printStackTrace(); - } - return properties; - } -} diff --git a/libs/onboarding-sdk-crypto/src/main/resources/cryptoConfig.properties b/libs/onboarding-sdk-crypto/src/main/resources/cryptoConfig.properties deleted file mode 100644 index a2370e5b2..000000000 --- a/libs/onboarding-sdk-crypto/src/main/resources/cryptoConfig.properties +++ /dev/null @@ -1,4 +0,0 @@ -# The private key (PEM) used when the pkcs7 hash signature source is 'local' -crypto.key.private=${CRYPTO_PRIVATE_KEY:} -# The certificate (PEM) used when the pkcs7 hash signature source is 'local' -crypto.key.cert=${CRYPTO_CERT:} \ No newline at end of file From c839d53b9626116cc1a80ff02e1a2cdaee7b8032 Mon Sep 17 00:00:00 2001 From: manuraf Date: Wed, 15 Nov 2023 16:06:11 +0100 Subject: [PATCH 05/12] dependencies management by quarkus --- libs/onboarding-sdk-crypto/pom.xml | 138 +++++++++++++--------------- libs/onboarding-sdk-pom/pom.xml | 23 +++++ libs/onboarding-sdk-product/pom.xml | 14 --- libs/pom.xml | 12 +++ 4 files changed, 101 insertions(+), 86 deletions(-) diff --git a/libs/onboarding-sdk-crypto/pom.xml b/libs/onboarding-sdk-crypto/pom.xml index 47d4f8738..5a4553efe 100644 --- a/libs/onboarding-sdk-crypto/pom.xml +++ b/libs/onboarding-sdk-crypto/pom.xml @@ -1,78 +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 + 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.0 - - - jakarta.xml.ws - jakarta.xml.ws-api - 4.0.0 - - - com.sun.xml.ws - jaxws-rt - 4.0.0 - - - jakarta.activation - jakarta.activation-api - 2.1.0 - - - jakarta.xml.bind - jakarta.xml.bind-api - 4.0.0 - - - com.sun.xml.bind - jaxb-impl - 4.0.1 - - - com.sun.xml.messaging.saaj - saaj-impl - 3.0.0 - - - org.slf4j - slf4j-api - 1.7.5 - + + 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 - 1.76 - - - org.bouncycastle - bcpkix-jdk18on - 1.76 - - - org.apache.commons - commons-lang3 - 3.13.0 - - + + 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-pom/pom.xml b/libs/onboarding-sdk-pom/pom.xml index b9d2877ff..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 @@ -32,6 +34,27 @@ ../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 + + + + From 737866bdd1573e66e26f23319b7501dd1710fce7 Mon Sep 17 00:00:00 2001 From: manuraf Date: Wed, 15 Nov 2023 16:06:40 +0100 Subject: [PATCH 06/12] fix CMSTypeData --- .../crypto/Pkcs7HashSignServiceImpl.java | 6 +-- .../crypto/utils/CMSTypedDataInputStream.java | 37 +++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/utils/CMSTypedDataInputStream.java 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 index c6cec0249..45f093e12 100644 --- 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 @@ -3,6 +3,7 @@ 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.*; @@ -14,8 +15,7 @@ import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import org.bouncycastle.util.Store; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; import java.security.cert.CertificateEncodingException; import java.util.Collections; @@ -46,7 +46,7 @@ public Pkcs7HashSignServiceImpl() { public byte[] sign(InputStream is) throws IOException { try { - CMSTypedData msg = new CMSProcessableByteArray(is.readAllBytes()); + CMSTypedDataInputStream msg = new CMSTypedDataInputStream(is); CMSSignedData signedData = cmsSignGenerator.generate(msg, false); return signedData.getEncoded(); } catch (CMSException e) { 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 From bbb8bdc32fc252cd8b31a0012998bb73eccaa0b3 Mon Sep 17 00:00:00 2001 From: manuraf Date: Wed, 15 Nov 2023 16:06:54 +0100 Subject: [PATCH 07/12] unit test --- .../crypto/PadesSignServiceImpl.java | 57 ++----- .../crypto/entity/SignatureInformation.java | 6 + .../crypto/PadesSignServiceTest.java | 140 ++++++++++++++++++ .../crypto/Pkcs7HashSignServiceTest.java | 90 +++++++++++ .../src/test/resources/signTest.pdf | Bin 0 -> 27863 bytes test-coverage/pom.xml | 5 + 6 files changed, 255 insertions(+), 43 deletions(-) create mode 100644 libs/onboarding-sdk-crypto/src/test/java/it/pagopa/selfcare/onboarding/crypto/PadesSignServiceTest.java create mode 100644 libs/onboarding-sdk-crypto/src/test/java/it/pagopa/selfcare/onboarding/crypto/Pkcs7HashSignServiceTest.java create mode 100644 libs/onboarding-sdk-crypto/src/test/resources/signTest.pdf 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 index 63febd1ce..a08acc7fc 100644 --- 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 @@ -20,50 +20,21 @@ public PadesSignServiceImpl(Pkcs7HashSignService pkcs7Signature) { public void padesSign(File pdfFile, File signedPdfFile, SignatureInformation signInfo) { CryptoUtils.createParentDirectoryIfNotExists(signedPdfFile); - try { - FileOutputStream fos = new FileOutputStream(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); - try { - PDDocument doc = PDDocument.load(pdfFile); - - try { - 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(18944); - doc.addSignature(signature, this.pkcs7Signature, signatureOptions); - doc.saveIncremental(fos); - } catch (Throwable var10) { - if (doc != null) { - try { - doc.close(); - } catch (Throwable var9) { - var10.addSuppressed(var9); - } - } - - throw var10; - } - - if (doc != null) { - doc.close(); - } - } catch (Throwable var11) { - try { - fos.close(); - } catch (Throwable var8) { - var11.addSuppressed(var8); - } - - throw var11; - } - - fos.close(); } 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/entity/SignatureInformation.java b/libs/onboarding-sdk-crypto/src/main/java/it/pagopa/selfcare/onboarding/crypto/entity/SignatureInformation.java index 76f9c20d4..d12a5f6ce 100644 --- 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 @@ -5,6 +5,12 @@ public class SignatureInformation { 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; } 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 0000000000000000000000000000000000000000..19d5aefecfa4c2a7dda363bf2892770a61fa5cac GIT binary patch literal 27863 zcmcG#1z4QjvMz`d+!}(@K!UrwySqCy?v1+zm*DOaAh{K~}&QHjs!2t1`&L-0DRqkmGMg9XpUW$X(4HWDRl$Ia~g*@y7=Z z5fKbjkh9fazWlNJpPc&38CFRbPv;kdtcrG4U_&71A1Pztzsvnu`Y&=XCwVdC@sFcu zfWR)E?k{Fv9F=i#_V{yQ2Xg=I0jso+hpeWDxd-TFSyq!5$opsgh2jJ9{ewfz-NjN9 zuig{(X`EH1>zuN=t!nXqcIYPp*`gI)i)Z|Tb_smUq@^0Ko8X~;^v zXxB2g0Kc&OosGYb2nMqKdo=zi{%15~>>NEn?yNG7FK3eiS-Mz(Sd~D|HXgP>cAme4 zz#i@(b0-W`pS;>cWwbCcfb=caR&a+RhTnA7bUcvdNe(sQQz*e)lM%kwGlhoRUSD^& zEB^tN0Od?+oxq}+bZArExwS?~1)mK!V4f-67(LT=%p%}SDHFp)soW;^hS(rZVTVF# z!9bjv+X7dwbcpZnfX~4{?u%dw`{WdJEvO5r52f90_VFAk*vek!Ec-i_Xbt|cj;!@_ z33NTG;^R4dax<6yUm*FXv;V})pY;9nq<;nQPi9#)JuN)`AX@7Mh<~lBm^*=9vdt=C z4hH?jAt5OxEiKI`Y3^ue;cmw&?feoLJ7*hKT{~xSXRzJB=6{%_KwwLEJ68`EcOcJS z8I!Sd2YX1`n!5uzIbJH{A2c7=i?o%8Ef~oD56JuntpCXU7dQVy?_YTQ%g6tlUoTm+ z0spZg|5BQEmg3Gfjvyc#t2o&553YH+*^z?*#5mG?q&0rLqKj0 z_LtK1`Fq07%f9=Kx!BlP_5LCCM~9{d$Vul#LYY;?#ofu=k=4>1$o}s_e}eYE6_OPH z&q9Ap|5HfZ)5F%qole}@!^PRo1*qlX?hbnCT9PmDe#sr(?`Ifg7&{nC7 zaBy?8F|x7Kva!*=uqnG({ckv4`iYgNCFuW@WB(pX_WzOqRSSDf4<}X?ATR4*=mm1| zyb%9WXW-!H`J?Wi#paXeq&{c=i473=`YGxWxxKO068I@=JW@}A@uc_;``8$xEvAhA zaGTZpv;p4q6F=`kUfvt3NEj)EMwl<+*x~1go|qO$>U{7x5`f1qq$Qa!1ZYb7WMYy0 z6&?MiC}5lJ`C12iS3@yZ7$wG6`prfvw7qW$nHV*_Fzjw8={pp>LI$`sPMFoL;|C`f zTT*q;Mt!SVMB=! zjhx&M7Ix|6TsD7=6!KXR5f=dcgw$$o&xf1dfKI;IPJVOqFKDr$8hJ~0W@R@O-Cy`i zV3+Z&gJuGfZ>&Cxg0bJ{%^)h-=S zlCU_F@`HeUL~VJ@(UG(TZW?4r7)MlYoXM$u@FkN?nDcx6(t+iuhsE{VHhpuN3aMj! z>A9-dvp^oEj!X<8<6KxrgVqUXPdrFO7R zulJk3e)Ny4@(zYRPd+XBuMYjV4u+IGtUK7JsBtih`ke~CZ_N@&F+NwEp>#Aydtta)P`)K}_|-Y0b!PGU*CBUiP;l@3 z7(-fW_Sr#!>R$@pG5^i_qU+u1ij(N|fl{r@-N9tTzi<4D$Mmz$UHjbNALf6p{l6i9 zD>8K)WEP0OxQLcZEMb4hD!&keJ4=ubuxZb_~V8t|U`RP;re_n-Ds@B1FpJzxJ`r2i#R z@w=w<*NSl9*2C06oqn(Vx7cqJ8D32KJ(az+t>wO7!tSs3r>p8eUN33QROj$pKD1P6 z(&(z>=&76N7;p?23jHo~vm627Cz5k>c>#^-(F zk>PJv-Q$}q&sQL4U99^0>UUcP`*q-ZEIYrP3Qv85fmjI{B)^XF)b$CYpsgK-Tiv|* zE$uC}$crxKeGPBBIDXn(&s^sB1a!}4x3SJ5K0+%I9325l?Vpr%-1Pf%^tV<|3iY)O zP4-7eD;EN4v`8jg=&ngx8;#;IY1alEDv&9z@-Vc*L$aE#6OwpcasmMc;yQfVVo zNzN@Ua3aF5ifBKQX!kb<$B+@r;YHIsx;w#-_EjeqXk@VaQ`d|4=}79$y30e4Qpz7{ zz8`PxOk*2rZ(%=(lnak-ch07#CM>P=ab6_&WtvXe>#Z)bbTpPv80*L!>u4U|W{CTc zNZuN32@AbGB_VOeiyPw5T6zF)GCsMT;o8GQDV_|J92wLqR_2PcAdN97Jej&hn{fr>q=|LAc7^HoTf29)hazFCKatP%$zH875XN# zaLQw;%kZobo>ohD+`c_aS63IztH}v1JvrRZnawm&J1G281%7<(_nF9|fPZb1h9Fyg zK`pk~Ak_7s&eo&bu`af5xWMe-Q=M~tyMwf^ae zxe5>|uRxHbuPNJ@n)!Hib9q&L?BKmY2+r5lBvI?oSXa|n5ytjx?KY10<*ujd`9zP( zqK-{HB{5Le>V0yD!VMHxq|#%08f`%h8HC(M^*(n*^&>)fh>UL z(o^Gjpm>>PaaC%Hydt_sSu4epvk_v_yyb8y<3o-AG3+gV9gL9wEKgW7S@P#`vQ0v8 z#{Sut{7oE_5of0|{o;&#Tl#?fK#@r!u47*8I$7Q`K`-RKhjp@K^gV|{TXiaQRdWr& z0?HoMapS4o`wg{~ttBCdxv5~zusNkmo9J(%)RAJ{f**FKudR4bmi=E+@rSX846eNJ+&7XP{*pIOHq&fkwz7!CCoJ!t9*> zLH$*9+%ik`!t}?f8JfIafT^X{K)&waM9fd53YnNxC^|hN-mwdOh$f_pindl`i)>JYe8H={> z=WcGoap6632^}$}6U>#uZiyIPl{G2txRR?8l#oz8SGlbs`i0&o+4F1YYJ{}gO-?O4S)XO-2!(1sA6LF|-TFJ9 zRdzFJNnPnoIrUs($q9n4l;G@x4oWu`K5}yOH_jv5{0mEJ2lit2;~(ugIY{{u%ybzq zwY*Dx@F&Y2v-RuC84`?lacoAlj!1*3O_XXAn9tCA#Gk0(=8od{k0eGS`MbJ48wZ_! z)+u6-ocYxF5fJeMpWNFrPOjKc8fmlSx)$Qmx`b28KfcK~!R+eb5c5v*(2!0fr%NhK zq<$bgy}=vm(HYx?9d_{Lw0OckXEQ`M-G{ofrPkS_C_=gtP%eNjg+a|BEhzwJ!J{a= zc@>{MBD8+%j>XMmS88G1Z2yZBgm)^0(m21Ti;JdC?_4lbU7zlzfWMTkXTn;*5OsKZ z$;jPWd^-1_8(W9jX}*X-eoBTbU>fK`a!;Of49WRjM8S&eDbae_a`}eQO66M>cK;M= zI&ZeBFMI&ZlV7wc{4-7u#S_CF{`tg?>_Aco1(N7cfYOH^C~63?*s10e#0e54Hj`oY zW}4}6MnCV8j}bm6lpBH{LheK%zUf4m%$eyvJstf8g*Nx@ZSX5M}m97QQDzY1*76Q06v=rGDV;ShJ71$+X zziTL^T2uox%d(iH%K@onS#;9x0N*syh{ls~Y*N{sT5P}kMJOOReK(N)Hx;z7oR1FbuhGK@ z71Zcqh8C9jD8}-Z`$)#Fl=A_h4jNDDvA;BW7@&+AJxowVjUGBEnuZW76s#e{2<6mx z5|8~*?xPS(Tkazf8>sPQ5<61vqZHe#A;bWs)(~QXN@@tvLCNX86z8a+EcE2>cZ2A? z6dEOCLjao^cjXLz)(cpXcdG@g$hoxwbmiU30lIQ-oq$Yvw<tJ z-!F=qD?O}%f#{W~3O3kIsAaNYJQ{ZJbKj*t;N%m_<-!sG*D@Y&tZCd1T?^vV@77CF_2nRS(I5MmScu*RjbObjIpIur&fobgDbgT zbRkdQ$DN|Armd!~ek+h|%afc`8CjWQOQ4QFr&K~DwGdN*qx?2Uf{TBr5=X+%X*B?#`rc+h0d47$is6;GZLTXz{8{TQmMX$dQu*dhVEwt9vFB*DZrSIy$L?|FqNIYUA3}1T(iMJaerMHs;G7dLxve}i zF7Y0{=b0;v-lFnL&FK*@f4Ko7;Ho0^63FFYJW$UCx%VmjM4g+EVpUIl9b*DKxRRQ| z5mx+(&V3I*x9~L}_E5frHu?tVSAl_uO8H3i4X&_)XA-us(sWcb`I~jgmfXrHgR_!( zc&S^w(>Lola&$tBdbK^x)?S1CU`j#d0X z&cObI#$W3(>$cXli@onEvkiD~nBUmHu?HH6aTPlIkxx0Y%#n6|_ap8~WYJRe;86DF zF&>pIUBuSLo1&^l9r6|Dgm>FT<%DzFy(l1$79`mBnWZg;E%8}wq z&chGwP%gR@`%Ivw4?IIxvjf)QY2ij!!6*YSaq;mIu!eSiO{mL0HHW zs7gMC1X9_0Kupkx@f%jcWi*Uo)q@}f7~`cG)No#ImV@wKZam%-}QeejF-BpdD`ur!9>UV>;t(joYeZxGO-H~nz;kkF=Yqi^ID6*w3C@!sp; z-M(P{L3Q>tcv0eDt!LIZqkh4A|3+n-s4t;Ud4u9MP_*08@70D{Cn%` z8kW5|)@N91F7De_?yAu~1~j+n-dh7yZk3~g4ZZ-j7f&?YJ)A#oE ztC^S5tJjARDn3}yOb#eU3ic%4k`@YjOn&h`@vLN@qn`$V~_Z#zCE(&jggGhZa^>$V-!z#eVf5nTOqNx%dv|?-Rkd2)6vXO zvghm3!)ZeQ%v9iF&EV}e-GNb-GiyzohDZ?kw6!%I*gn%NyOan%z7Ax)cFoUJuAW87p8u2{XwPM zU$e2brp>3u*C6_kY%y9_qz07LSB@|$$SUwEh|BQDA(|nr5Mnd#23$Q@CgcoQ#ZXK$ zlmFElm84Weg;u7&`BD-BhL$Q8kiB438! zKwN>Z36Z|Stc4&GqN2kh!H0)10r3c5b%er-A??8Bi3z+%DucrWBFZ79gh&Blqmg$* z^ngeW@H{}`Xt)}&H)gPtVhCn%@gcCY@Lee40o5UJt|5)qm=r${6X9e-euNA{+9AA< z6^Je*6Ve|N4EGGv2P5)#SAs2sp@0uY7J(B%_>J`Px&q&a;tuWHW(Rmqe$H}^dCqfA zyTiPryo0ghxdU@fd@gGydd_f;dd_iYVKy`yAMSyo&J?raJ^Vd!o4SOUU1^ojN9-3#o|YpA(%u$2o5}-1&l2EPBbhjdk8g>>;K2a zf7uloSv%l+B(*8rI|1hfCN|Hw-SAJpEIya-o!5Up3i^)nG@ejvvdPMhm(gM-Vmax9 zXW5%caa%8A-2`>yJ|0@JZc8_wCI`7RzB_TObXeo@LVjevY93joSPd=wjb-(_{kovq zbew9*coJekghq}@e$)$pI4663Wt6BXHQ~&l ziI#be#nvy)_~nSU@6KP|r>M^LC)JW`WitX&*;SQ7}SwfWGru=?E* zeuMDA=z}j$(LPsg5BciQG-Nt&YN0O?+Wf#`rLEy=^OtHp zRJ5|4T(=9sG_z^qT*zpk&Pun@;oQh*#+h%xzjePqadXIdn3MXbF5kiwQaOqx~w{#c%4A=b5fKF1dD!Hd4F+yPIR*I-aF1ZtB?Kx zy0sNNu-RmScs1m&jv=qIESKH~*PJ~*2}=AHE41O=7$hIUyL`0~CwIlyS9V~2M~>lp zYbU}WIvteMWGh`xyQUefHq&6U20oF#K~P||EmST2iR=HacO27|adF~=I)EI+`hD9* z-4QP6X*=ArFLcW0I5TKY*%EgdmNyEAY$QSl@w+vq;dTrY|Y)r@N3t`Y`4 z|GEHyDoFRJN5ug95}1}2d6|Btp0S>~3JQ(>D{_N6OD1cBU@roab}B(uk%#HI*X~Ju zV7dyrTkM&@#~ndrC(|A?*6}IPj^~}|FPJVyFQUj@i5@?W2&;(Oyee-j=C(=Z)K9eI z4F%ccG#Y8YAk^@NPWLeM=rUid) z;3~D}s`N{BJ3mZ;)f5;GNZJk=eN$5A9ni7>bRhdUd=CGh|;dFzL%05ia-{E*RH+4~E5x;onLwtP5HQZ>b zv>CEAV|^Q|{X>Ol5%0-0DnS)KxQ|+3NGG9x9jEFdN{}fGznhneY8n670*9$QDz|5O znyHUBu0IXh+=M>1E6QT{5h=6_gS+NVt#LKMmr>VT(%mvP)*bAG+^XwC&Qm6+NCKH3 zXz~e*-O`y0w25jR2O_=lFhe+zZCs5(ygG=P8U-lkvI8B;dd;+gkd}E&z^kh7kC5`$ z>;?RMci&USD102VpFh5sbJ&4R<(Bvm7gA8?nz~ezOSl)!&X4?kn)oXhlyBF9)JyZn z?khukN2S=QRl!eI-qFF`0p8~g1L4=iBPMvq-&(ojP1#KvF)0eNavjJs($a7Z`qE}E z%Of&a-?jwM7k^X4naC7zm9i@1N12*xv%Lt7wB|I(PMsaQ9i)JRj#YuY;W0Sf&vqMx=E z^BHiUpyCcCZYDde`vRXzbtfRa?I`bN{gySsYe=Rdiz>mdmwmp>;qeT$bgl9z-Ws0# zyTjv?H#!+%Hpi;lSF7v9pT%Pt(W@;!Cj&z}l&$iG0}S_OjmX_=5j#gmcDg)Jx8hD&C7VlA#iaId*8i8|-w{kn8WWT!-nj zNNKTZOt#+P$L?ALPaRhpwpB1YOXV?#bYulAiRPZfW+2&>Bw>=!3oz;ucVaO1p6EfD zP$kc`oy(?DR=?-eRbk1~6<~8&E&-i3UyiVJwG4wVTihCv@=J)BWE7p(de}>?3Cg}| zvzoMZ2o$nrgVi-n!W_gjD)5RY_{1j#8L5yUQ#QHG@l1^BW2LH5`@f z9i^%TnVo#(_bb2eU0$kIo6xHmc$_!*!O%Lv90R(07{}r;6q@2Zw^5+z#i`b>^0_2! zuQ2{lPF>=p)c&qNepNP;Soq!TklAS`?;etl?{yZiwtNfxWVu1R%x5Nt$L!^0tL(}l z83M>{!rJJ?Ok&=ougn}Q-al~&BzxF!Uo;T22b6c`^)=D)Yp`0-O z^*VDjME3pT2pcVls!N|<`74(xz*|gMp6?r#)+6l?Jj`L2pJ&mSI2gWfBvxAY?rmUd z*%hl#FKn{X5TiAeJ9n=-UCV)177Y92Nch+iH@e9MyC+3g+spUn6%BY-K#bF9g&RVY z%#yv#)U{7}vb@Lr zZv17|!4=?(wCnA#b$P&MG2*q0K+RO7__hb(`opLDXGNx@c_QY>(qO#Xq9H7p`n@}Dy34f{D@cO*cT^w=~~yyLWdL#3Vml-+5YS70}KH`O8eWSn?3 z)NXKUwvA~4plp^(IBuMYcgM@niw z`*2Meri~X;*fBQQOq|j*Vaj@r5Sw~kV%OOye{ym)<)%hZLw`kc}_&A9#Gh$%}}?0&xt?q!xX5rHMkXS0*lp^|!|shHxW zS+Cw#Nj6)*!}a1;TB5jyIhTKnBO7$Z_8q-y#aAqL@>lO~CFHWrIuZ%O`MON9urxFj zfzY}g9uQ71!nu@;QHH%Z(tG{Nj7$Co7ptoj zu29S*ZHo&b{qF0y_bF$EAE1w=kJ4$Pm5APr3Y*dfrVg^nHc#zj2Df^1!LqV7CLS_k zrf)DOL{Ps!K1hQHpPjgL6XTtFD+PiF7L$2OP1IctnE3G%_S`EHz5E!C4wh6&4cNQ~ z%Zi&7WTot`afYKQX{W|1zD}u(7wC!h2U+k?aQRNfpAgDcPg%sZL(8GkWt|JSk?~W+ z?i$Yfvrm`9C|lC0QLDHqr8sj=jr3lc3I+-R)vOE3zAOl->9i=-9quG2#!fhENr55z)S=rL41Iuq{Jg(LY&m(yj>}v z2;E@VW$~)Rb?sz4waxYE1C5mj`{-r7O!69i4Xi#CduFrX(Z#q?vnMzpUI&o+#}Xnda+3S z9_o_NdW|>AEQepZ$kZ2pYC`k(0Cw!}w(cG!`Hhk@y0SqdZ|1-@vh!b`&3`EFlrXFe z&twON6WthNhK0Zln!|`2!I|sDPr0$JRGiHty~KM4-t>0-Qi*Bc%E@1!yl_D3N&~G4G$@Z z(0yf}jLv#}z2hfdp~!t4k0CC|r+5w5iXKmg$DOW~K6zH@u4@u+$1l!dV08)f?oW z{G|>v{Pai=cS!hPZOCMS1TkS$7H|@WKWl80S-~avcag}5GISB}?oE+WKGIOBQ6Hle zL$cKwStf$Gr*tUM3U6E)6*(4*EBgCb$_qK^X5<6tSSMAC)Cn09j_&B%R5s-~rA4%d z1zgkk#P0i_r(gMARljjJZ~H{sBqBALuubZdd4!T3?qFkS^(qmEPN-w`&b$nAH&p@Y zwyLI8B1}$*qU0l&yHUAr`(*BlWFsS&rdnk5zJ`p)efPZ@gUvV@eU4AD(BihJ7Ns7x zDKw&GN^w3I{f3IduBSYh4%UP`ml-YH1G>3nNQON-$rw3&CbYGKus6__e!ypFO zxdsd+To(~weIG{282AX}5Rn_gL+J^Bg-QSYcluMjX!a{05-$9Ea>IN0PYQs0;9X2O z@W;m|*tHI7xbB>p7a;>rvF&H&Lq6yA+K3<}IxMtT=$fzO zC_+t@eDs1Vn|2{t(dVncMm{^q zAd2HYR|5HHqsK_D{1fIHKl`dQr$ps3cYOQ7U7>~@5mre_b53)qfI>-08@4kiqec0YCV#+t0r>Ai3C zhg{EG3&NOzZYBl>CXHKeN%Dju&Kjl8E&QRrK^#q!?W$Skmx$Yb>9SS?BB+w9&IuGBPC2#8Y`` zijUt$^a5HtCUztwe+-XQRngNHOR_}C;Foyu8jZGVjeTE1E50UF{@_MO@I4Znc)J8t zST<*;VC&(HhvhD^6Teru|4M4(72GYBo9bo%yCB+!R~e&G_NmZ*u}Tz9a3q;b;s$Xl zZt5<6S3+LlskG0S=C>+wNrA|Bm&ne0lN-2;1KvL$-os7T9$a)@3#p6|2JXDa#OpR= z6s;UYQ1lCY*jX=YbRCv$Lq|VC=J#bI)#dkzSZjA3!aA3gt0X{Cqle>CSuUa)oHY+d zyNwhq0NPb55fjPEqBEn%Dr3aalM-{9eT^6}|2|GwB?X{EOL<6=BhDiSd_YYiO6Lyg z294f*Nj67KZl{{1eU0gh+G%BLU~AN`maXdekRQG--&>dz++%HJ;gae7qSw;s-=+w0x?Zf%~ zAPe-G#fq(Pb(W9%MaA_?bQ>--4&DCb4f9?$y+7->g zB{YB~llANN-B(m2w=2}_(bu1hleEYWnSY|~_u!w06f&R9_f-F^Hwmx2V0r#hj)CX$wba4&mexJo7o7={#(=*;r2imb$bb>u9W!vZr zzsOh9%K|%2WaB^E_HS>(J(fT4i&}gwh%mf?>1T8KqOXDei4m?B`qkzQy%xe?rP1mP zt7tT;`m3!ya{LFRh`Yp;yRo6X`a4&JcVLFX)WBcLbs;lfE)j%mf4C=5pkct^imU|h z`rJp3BL-wAz19o67Ma`jEu?~RalyF3x$_!0-TI7~z3E%%{wq+;=Oge_`^4A0@7LMM z8^TU4j|r%<@Ws&J!XJ41KVI(-iXzCqb9mX9B)S7?-!hD@+q0d( zF@#%~ZByXEFL(nztZQu$CnGto%en?99QJ6@RHrx;h1KD&!PeEZ(1U zY}{NDMCZBt;g!Nf{qh&k01(c!$x9GQH9KKaqC0~&jHvj0L^k$2znr7!pEI;EyOO-d zl?>;7QHq0|HQ5G@pt^HtW2T%izSE$hywh!CriPO!S#8UFPr{Mq& zCfb{vhNqQs8#x!OPbOP@o%!PPvtL}y`^N&2Am_jD_x6NWQ99-}UR@zbSH8-TqvR;o z4(n%LlF@r5__Tf*8;WL7tn%SN)e^<|ttv(vymWj- z5cgz}I&+(Z3mK6BfsQ6B*e*n$YBkQyVTd_ z2|dOj928rGSG58O_i5BfsTiRqZ8WMT0OJ$)I(!ClaSeA#Qx!JpZ&VEuA zt}%OK^>faG9B}MFttO?_8WLsODprqTfO&%aQ+Q};md8BXag{*M^r!GhYfU*}HD(Ro z;6gn2yt({TRvg)5)hqvUB$<{we=jFJS=4^cJ~oSdLc>)zt<})#j0D}U?2LkEUp25i zlvYJ_bQCz_7cJ}1l?B_hk`SUx@sD?($gPLl;!kKux#@6^R!5~Zihq#GY=1j}2y@=F za@G;$vKtYK=IHk_t+p)@Hp^OgC%j+Exe$;!{ywX_+k1RA#ivwDu4y~qWozbL)!myv zXJbuG^nKiNUc8;3$oBOB z#t>YsG(M&CbuU_&7H4lCe5~2FGlt3}a$X|C2(Am@eegB_Xfr`=W%BD#H68fHe4_mC3W0>Q8O#?nF5%?DEWVbuD@sZwBn zuD02Ne*Gws9UC^{Xfbp}U)rUU=(7t||LJrUpOCLbltaS=pHhURIPsM~a(T*PF>&v6 ztfx)Qj!{&P0Qr>}hca3lmnBiu#DRJYf@0Lr#yEnK_N-;D!0OT9iE5-vNt~)F zMi$fhP>@65CbQ%6kiGqo;8esdp!q7;#lI!8is>Rmv_ThPJ5PXmu;1SB_Sb-xcWmtp z<_W)Xy}+7octQ25Ze6i6dkELdPdru7rj6{t`9eQCZgSkn=e9I7X>MA2OJc{632w%1 zp)DlmLFSjKEqhfBz=w89NC5RAH4$yFNzcxd>NmK3C-{U9Qq+{IH8uPW!Bdn3!5B;kjS3_|rt` z?RD;>;BR&LmfnJKY`?8bAdpK7j<)P;f2!rRVI7Kt&M^MhOHDyu!|ZsvIyXIot_?fj zc7o&Y@c`yI&2NwU&Ha$qENFI(XQR$CJS)d*z15a8>()#k{inEg7dpsBXc+fu1JO=n zw>-&vglDH@{MrQ(ZhyIM{ERN>CZ@faYBsbeJlSsv++Lyy&^0?LZP%Ty=k)_l_rm5%)gY>-@-FF*;=y zvIpC_jHufWFJ>G6d=%Uc=7!6=u6?hzx5hz5;b&QFV1F)$6R-YWnww1%u~&$2DmBb-c9?BACGocVVN%eJD47w6KmU%l*>8S58<};D8cC5!&aTQv zgy`m!rSt*Gn<@*N_et*qpfJJ)-^@q=7IuConP@-00r^WzVVcuBB+>@Pg=*{&+X`}8 ziRRk~Z~a;?p5D7`g4|@duaW&D zig)uU2K^pxy~K5926)8XwLAE>V>wQy4(RdoQ{>YLJ$d3bHTH_!nRz}!D?ACAt>30w za;OtqI^_?l*r9uc*UlB``r#8-J4Mn|PujETw&`JLHMT+LcZ!rU3`!H{^=p`UTEZmK zzH2cM^n%`K5Z8_w$l}{i3B)-I#3wc_W>Lt6Vs^MIxPZi83j!Pi6tKlK?G{A$p zBaou8m7ioF~Xp??O{hSe+1Z?%78W!V}A6v+jW5ZgyR&sbXyDb?8Pk4S4kr-MjywBlDOj@rA zn%9zD?W~OA5s4CJDZX3|$q@z*q=keuDZooNT-4YL|=Hj}az8DS>JvSF(LGKfnP z)5_sJdPYd1^$w`2xH_RVi*0b!+|~eaSzRDgL4Z@*vQ>C{6_=*a?ybF`n{m>d}hLo#eS?R=P=Tz4* z?%p&laq->+kw$`rutux=hNE||Vu|Fawe7nc)`^C$)p8iL)>h93_IgSt+*to}QQXQp za@gxCxLoLNC77?~rsk_vue6QsvY;-5xp-44)V`|KuGW)zY~4PZGXgcM0x5sl#F9OH z0QO^4cJF{aHxZ@lY+eeniycf{s~QHj1SX)ymXJsSxz3Xim%ew-`5lZkseLPvxehWN zlFv#Q2}YY==^*UaYQqMdX;%bI@y~jW(=r&l@9+FKDKURy2*^^Qj>zccMlbUbg#_mu z#dittj^%_@&DC&ni=Eucxv)!?4Ur&oY<$8XqkBqjnPdsmL+;-o<0(x9(yPgB@Rrpc4K}G65+HzbRxZWKoLG6DhT|U7C_r z{Dk9hlv1Wg^(cNx26y)J+2VKNkjUfc!C@xRtO|kOl9C&gN}EXFKHLn!uyBHTcjD1O6 zsm1ry_I(2GEE^G^okT6RO)o)7rnkIuG7>psvcjA>;hk-D^Wt-8qAdy6>Lf587H`%N z7-gYfLF9pH6LLMoj?ljq&1*t{xBGsW7{eKH#nG*ZdGHOGx;r6T&iup6H<-w^Nc>KX zYI93?hPKW^%s9g0jM5L1&_iwvi*u^!ChAFZOVu@{@wGZDYO3i}^JSsmw8TVNl8Ftq z^!0cuQsB^)RqVd`R5KNh)03%g>TBg>%2kb|J1pYkYh_0=FwnEIXbkG9+c{rk^_0xR z*}th_WDf5#K@h}=*2z$hrc|t!GY*YTujKB0H#}Nr z`7I+oX$61}(9w7Yg8JGMl2##&xyulIB+@C;a&}Wum#0e4)J)MV8vZyQ5{p#YIG@0* zwMV&=3IFWQ7&oqvOxy`7PbWU43CqY)Seb8>#y^r`kk!-*MNg@xhrb-nYbZg-3-v6w zk3{9*UYei|345?)SQKvhS_O^`)j>2kNv!#KDMWBCL~w`KcK!1Im>f;+Jn3K#@;oIA zAAc*9g#54ZU=5z34%R)@7t#>_h&yGhRbw4`Y_4hS@!f%gb&9r%r~0|q1>dR+ttum#Z(nfAhg*IN=_+nGQ_rLdGcgn}A(frgUXlBe`^D)v>2O<+3 z(?kf(kCu%^cx`C-#q?M@IT|4Eb?Qf8!l=8OU8QKyOb<<|q7tFRHLA$Uj8=_}pi;kr z^WjG8C=2nf#_yGSL%;h)hjDKFTMAW>zrVi=l7+^b!0zkQWo{TnSOS>OImub7v7yPS zcu9bBUT>;Pixfa}9-)rK5NfHZGswr7KvAx-2G8VUv#yP3m^nL)OcRT(Q){dgT8#xI zCBc?j18Yo3TC|T)f#pRWKbSx;LP%qj!!i9mDatmqF@1#kZP?rMPl|PvLALujQVI$^ z0w26o5Z%@(D#$C&kh9?bulBw(s;RZxT8{@*up!t$x}bm|kU}paA|UN3RjP;q5ke?| z(0M_MN=K0nB27`G6RAO}lo)y^p?3&9gc5GBoL4>G?|aW3T;e6o7%lo&S$~b5H2B}|28ceYV50*UzTQ^s)au{ zaBhH&z=lb+Rp!j=Gi)`y8Fwf}1ZN4yrh8-K&0mLwmE`Jv^i5T5_SV($WGP$D&(YA^ zuMfMR$a{Yt;G_EJTIH+_2e*lQ)F&1Y;#RuYDNp|Gk(}zs^=kEMZh^R;`jXx-2EW23)$s9Y-$%vf_<~6yP4IGRR7_Ri#qLIAF!Z! zo#REyz@w$@ZhPY3;>~dZZC!4ZAWMm5f(=r;xqKe}!E8zdFIW;>nw-$YDSjTKbz*67 zqU*yw`B$y}G@eoG_R1>Da_@LA^P-Mu4Ar003?EV4dJ*zb1Egb>4UV;9J?-$sXZ`xa ztFp;;FS60mYkWIXwh*o*`uEc*%g-)JoWwfp__$371h4t0(ghUZZ$wT`wCwnl-J)%m zp9*>wwA#GdakvGGoNdc5+UALHX}tvju8l@Mi?q5mGPPe7oh06UUm>(*nZM&%$xDcU ztWjjlv)J(P@Mkekg~ed|Sh4q+JT-&)qYs87S$YYBRTq+mio)%GtK*3mdF8~C)@nLS z0z4wIZ+E^c!5p5b5gltL8SRd(SjNA8BRYZMUvgdQO))0VAVj5EDfu4u=<&Wc0ugLm z(95nLvQyCRD>XftvpiefsTj}erI@ZH#%CxO(<^dr5o4Tp+bQIPlvN^ZVdJh{6{>V1 zts=JNTnQQdzMiH4x-u5$X_OUkUCqd@Ti0YRudL|8n;{iHM^sj5}h(L?YJlz=~2AKZ&nfwat$y$UEcWOZL=B@zRV=!%@&!^|Jr1I zdbqoE<+yskW$2i#DSH;2yY>!)4C{X0;Kd1=XWo^f^1KR81I78;l`&u09%zY(cdt%+t)zxD?Ojt0Z7M zCcV*)B@Ds5`Jo~64T7Rvz`GZ(YVq&)W4n0bL*3Yl--QN&bj4P`PcqmT(I8k$M+Xv2 zOWV)>hf|BE5`MqbIe=Zn*pp3bCBY{Vrx+hozl;npM3%TK&9_CXH_1!pX&iGA%)Mti zvbn^q6=8Ysof_Rp){CYPWK&o3j(eqK+2z)>w)U~-IBcG>haGL>=vi17PnXya2_)om5sVx7SY>D_}|9vCs2VSdT;?a`eaEM;1`v-9a|D@>z$s4Lj)W#*^6 zUFRxvst##!na0EmWe2usM?~=&Ns3$P_BHEXPdXGRl_8GuvtbMitNT=vmisQB9*B$9 z_AFw_$Q4ArQRWQBZmUEnNU=6qZz_>o%Dpp%x<(Gx59zJ4$n%NC!CGSY`1qMQf*Z`G zjrx|NKGuJ7KnU+@eW~^_Ermq z35d%cSaH9{QtVhqX%Q{nTE-B{m)o?BYcw2q26wP;yfRrUBn3BA-N%4sY2qagCglt{ z8)byi=_Ct%0f0OXt`#`_5{;6&mi=rV;ZBU$$*oL3ej094q6mqSARs(8Ls8`pjdBAn zyWu!C3Po=v_~1M1_>wyt2HA=gTIKGO3*xLQ#u7z*!}rHKS1}l`Zf+COtPHAqb%*qr znKgqcg!WlxeK%P; zqC_h54@YuBG_fegxhl2UQjye3wNim7HQnX*UwLd_X-@220+bccZt6<*mMNkmM zV78lRH6-OU%jDdC1eV)pciPw1Tg?BG|Gw)>k~BsUQ$ZIb7&ES98PJ`VuePiO&}8oN z9k4jPec<#legEGBSSoGU0(dvAEo@iCtI zf&{mbYBg01jSIO05om0FzcvAr$5UK3gYXIYSFIa2GP@UM!WzdQz+RwQt`2pp$zw1J zjD2|qKlR64@ovziJF5%6hxr%-R1JzKF*NN_wFaJZuG6LH4!MJwDi-jpBLHXH+WfVI ziT5aAy~2XDhBR<~W9h}}QksdnTceoU+ED21YZeFD8GIp}d}k?mn1aY&^r~>cd(*=a zh_j4OS;`7+ZC1*vF{~~#Az>QZXFOLbGfSdr9OaV;?fT4H!Iv`iP&*BZ2X5R#>!<;? zoI7S?jynlxAXQy>QJZeq0Er?Eulq%o74v??#PX7vYg4(L-K)gT&=U#87EbkbqTLmC zq-2;juK6sQfPS&5)Zu4UU2-cmDnfB837ew|z>F4_@xbQW6eP_kQ%9_P4dDgk@0o&w!AxMZMuc@ zwpUvilt0CS?9ur^v%W!tp8XsWhlG7RgYY1CE<~oJG>NiFF&Hv)?+!n7WM@JkTb|)a zRiXUs^q2IRMV7;(bW~rO?W(Kpt}mcJ0hk(rE-N~QhRz)JUJshLGdwpH;m#w4rI_2) z-HT|Nl`e+WD3|ChFe5)w{Zy0{FBO5FKu@ZMc6pxPz3o{$;SzwWnG_f`j@K2r&Mk>m zE0cgXoa~d@om^Vg)HP=_s2J~mH4X-{FKyjk)mJE=IJFj`?N{^4hdJrJH*_{CNrpcf z`T&b?=pr+11orD-#bwjUv}EUN#rFl1U##FT>n=#j`n%Ld?YK?oWGaN!d1S1mH}qYX zebIGiN5j(9nx?T26qJtBZXIFm7?0xw>FAws$AW=KyA8-0{J=6 zf~y?nR#*2uzJAEHi;hd6aZHxKFDGXx1mKSHcx~9%%b%p+ z57J@E^cZYkT32&tU|jC>DP|$O%2zMSEgLTM1{|ziMH}y|^-WJi-pE#|0%jp-qh!{G zkdNnc;co(@ZpRh46_8M!PZtgu<~}MJRWW@vgFe8Q!mR0ku_x6HlYfkuD6k|+4!O29 zR2zz>@h~Zwsu-L;{Iq*vn}T&q<0evas!p#RC`Y*Eo3>p8DUA?4#)uRF&vWap*!KYC z2Ew)o3h&*jO^|^ZJ#e*CS_;4v&AD?rUtg=Fw@N%4&3m6N%M;;@+5A`(*e3nB|M<9# z_)34V8`^WN*;R3~sBI9XuvI5=Bf#=B0m>=cy^94Hh(vWP7(w$&3wbivruUUh^oLhG zl_$vz%Ffs>^!n!a@7{FgSgIb)>>BBj^Apyje)jD+T#VC|DFikxcfA!0sGJO_dzGM3 zP|m7Leo$X9Os^~Yz(hEE-OXl_p5}}Ppqr>0V_acpzlyJIC3cprY1O!!NLx6!r7s^hH5^J(oYaWBe%%nQL4QTQ#)@oQh1M^MNxx zq@6~*+jhW9%_yaZu;f*Id0n|l9L4~>eW$U~hB(N0krjB@z+?Nyay#l${qO2tIK;eT ziJI6$n^9d!6>m}m_j@MWEj(?r{$f<>-BW5{7W<)j>yL?&K1`QbV)!LI4rxMYM{VeW zOLk{bm*Me*?qkO?S&6yN2EW4}eDWbcb0^$|i)e8opuW<2pG zyAEBco!+}2y+*atDGK>A<>X?g1gYu!>nTx~O~a|R*3{<~fwZ2Zg(-vCmi2AJXWn^6 z3du3tzI6&??mzzw(PgFZG&D&zX=`y#W5JWW;9{B-G^MwVMx1$$J-lP-xV+J>;?Gt*BY$^>I8jz$+`&zG)g*8r|=T zsrPz+q5xxYV=M8%6Q0balg#~}URmkA`joQ@k~GP;Ym+!PJ}%IXztCtQ-LV+YLReCQ zuIIf(8?}UI4X^VzvkU4d%$jI>8lGrkpVFxyAFQsHwu+7&L9m|~EYsb*@rvZo5^0-sNrPUD(q21Br7Uf1tnqlg zo%KEg;$&iMlOOasAX^RL+!d+CfKkSbyKcls?~F4uvlAxRx~96PQ9>lyjhxQ3l|vc0 zRsofSHI;Nv+}6$H6Up4?)r@Xrp5Z;cH zDwjLiUoO}50mFT*PMF>u4j;XHh*l$B5-j$z>&@9nkbP)|QumVvTb~tm1|3X5_H%a8 zvR4C^cTdo~9=Ia^ibC&x#u@Dr0hv0`jIK1KZ+^R|u3YAAR~F9jJ~mK3bHeAyZ%4Mn zGQ}^BWQt2oM4g3vIw)@Jon}`eH_G&|?(yQ4HZzg?)*?9px9e*znXA5vqv`A1zk1u; z=I%|Hy(;72L|@>9$*ybrnybaPTL{}dI6?9hjD?YfYW(il@I;@> zMIm~d8AWMeHean9(?%6HBi=#c++v5S$CSYd>7B-zV==ZuP-Bhyh5F%_fyLO}^?cFI@n-3 zO}D7=xkj%0Drh`45UvopY%$yiW4saThKbiPvu~H^;K^}oxS^BSivWAVrqqmHz_KG* zhhr||Vq(rf&HA2B01s};mRb_F%sfo9r?;d!9W}b&V9IStV8Uh$bGyaHa<9GD$W$4x zSC7(2Yd}iLTr~VT#$*RO^+XHfAYsGt^B&hb{&zqTpmc zHVz~h+xK`!!q25V_cwZ4o`YZ}d1EU~*w?$8#sa%a1c7cdFCt4eY_0#e)oS?l&yy^uLW2{sbdxeMS7R)AY1WHl127Kfz~W4;|l9Myf!>92~m6g~S6fb3@>yKLT4M;!N#Il6r2EF{z>zPtlpX!vQhCru%6LRXt_XE~Z5 zvxzRXukLn{UAI7x5-C%TmVO;^x)Wdlp0Zt&{fORJ}EPxK+vy@s!2$8aBfoVaYO~W?c2!zuFsmVXcNo z*?PSMCfNt#;^G`Zuk@nP{6lX|8JlPy@;-;E1_jXuYaT2asggPWDEj>E7U$o!Hm)!V zU(Dw`zT8aTa_SHpnlovC-rHB+psU=0oH_TmBx_+VgpAN`X1@ii1#Ch~-h5$)^h2f# z04aY{T2A@`W=M{p-?0x;3FgWA59vO|v;IbiO#7FPXg)+3^m{1@V1*X}%HBwrFETX@ zqby*(=hpq*^R#Ch2j%as$Hgti(HkC9*E|d>Jq5YBS{roxZj3^!#H@&HYdg~);|`;n z3B586(vv-@OvL_x61@eBGEwVNPejw}LPo;6ApKihwqC=>yh&o>yrf2%`xf=T%c`>` zODEGWSKRZEnsnYKMqyBojiYVCS9Id$+ucY`AD+uWS)f4yV;dJl%HzJi;rY$3b-D@rZgp9Gt#phd% zH@B}g4DOPN;%6kVTVvXK*{7JG8Wh%cb0Zv|;1gZ^&X~D|JFEWf`kS{!%dd!e$~`Ec zedFG@q;|1w!q_Fs8JFAau|;r0CsN>>B#8L(_PhGH^c8cqVlAC#vcZGay?mOhi(!S6{%|T`AYs){E=KN=Ee>eHpzVDg+ncLRU-ug3{ z_soDWYZ!b_NSvBp_;c&`%p}ads2$6Een&K6c3)1cEoFX?MgGzy75#GZIS%;!>i=P( z)YXO>dcnrF8|5O?;`*9Xnebc;I?XN|7Tj@@NdkhsYzTzm^tpf zugceC933t1Nr0gsIWaM?H~=CieiZ-#K_CEeQ4t{k6eiDOGL%f0FIHwZCYd)~C9~^%D0Q0@a&^1d3TWeDn>h4C%5=pf& z`Qj4vZ+`;NU%Y?xZo$lfnwG9Gs&N8P*MLKq>X@@w1ij=uYjM)R`xi3gm%zcmVb7lP$3nIEeCEm@C~iQCsF^WK3OGrzM~ z>V67e`3NSRL_Z@Fwh>?KVcB@zha_$eic8(iHTFamVU&9g{k&Nf5F74-aJ3WfxuMH ziXSl{s(H#U`T+d`1OEbp`~rji0u%WKCi+*5%G^(!Qg_#X=?x?Vp^n@SeSp9a(VzPT ziHK6&a(;>v5&fPQq`fKCLdX8I2iKQD2mimG?k@vuYLEOfQy@_x5pk*~7>|O+b*cx{ s*Eth9O85VvG$7R*%!&HVTxvzYy?dYPSi?aj@!L)JUCkn5(62TB7qd8hIsgCw literal 0 HcmV?d00001 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 + From 624c524f4600ef936ca68d8b29bc2a6d455ea54b Mon Sep 17 00:00:00 2001 From: manuraf Date: Wed, 15 Nov 2023 16:10:02 +0100 Subject: [PATCH 08/12] README --- libs/onboarding-sdk-crypto/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/onboarding-sdk-crypto/README.md b/libs/onboarding-sdk-crypto/README.md index d874da3fb..44329c4f6 100644 --- a/libs/onboarding-sdk-crypto/README.md +++ b/libs/onboarding-sdk-crypto/README.md @@ -18,10 +18,10 @@ The sources available inside this repository are: It will use the provided private key and certificate, you must set these env variables. -| ENV | Description | Default | -|----------------------|---------------------------------------------------------------------------------|---------| -| CRYPTO_PRIVATE_KEY | The private key (PEM) used when the pkcs7 hash signature source is local | | -| CRYPTO_CERT | The certificate (PEM) used when the pkcs7 hash signature source is local | | +| 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 From dca846be7c96547f6b07502cc35ab1ca62b32a94 Mon Sep 17 00:00:00 2001 From: manuraf Date: Wed, 15 Nov 2023 16:15:33 +0100 Subject: [PATCH 09/12] code smell --- .../onboarding/crypto/config/LocalCryptoInitializer.java | 2 ++ .../it/pagopa/selfcare/onboarding/crypto/utils/CryptoUtils.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) 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 index 770a9a0f9..8c3e5ff13 100644 --- 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 @@ -6,6 +6,8 @@ public class LocalCryptoInitializer { + private LocalCryptoInitializer(){} + public static LocalCryptoConfig initializeConfig() { LocalCryptoConfig config = new LocalCryptoConfig(); String cert = System.getProperty("crypto.key.cert"); 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 index a25f709e3..1984fdde7 100644 --- 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 @@ -6,7 +6,6 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.nio.file.LinkOption; import java.nio.file.Path; import java.security.KeyFactory; import java.security.MessageDigest; @@ -21,6 +20,7 @@ public class CryptoUtils { + private CryptoUtils() {} public static void createParentDirectoryIfNotExists(File destFile) { Path destDir = destFile.toPath().getParent(); From f09fac67c0cce085d0427f419d4ba3f26e423981 Mon Sep 17 00:00:00 2001 From: manuraf Date: Wed, 15 Nov 2023 16:26:23 +0100 Subject: [PATCH 10/12] fix labeler and upgrade setup-java@v4 --- .github/labeler.yml | 2 +- .github/workflows/code_review_libs.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index d4d332657..65d5af2c9 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -5,7 +5,7 @@ onboarding-ms: - apps/onboarding-ms/** onboarding-sdk: -- apps/onboarding-sdk/** +- libs/onboarding-sdk/** ops: - .github/** diff --git a/.github/workflows/code_review_libs.yml b/.github/workflows/code_review_libs.yml index cd6d61a87..ef858f58d 100644 --- a/.github/workflows/code_review_libs.yml +++ b/.github/workflows/code_review_libs.yml @@ -25,7 +25,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: 'temurin' From 831cb77cc5bc3554a8cacf0ab50838fddcb5fe60 Mon Sep 17 00:00:00 2001 From: manuraf Date: Wed, 15 Nov 2023 16:30:01 +0100 Subject: [PATCH 11/12] fix actions/setup-java --- .github/workflows/code_review_libs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/code_review_libs.yml b/.github/workflows/code_review_libs.yml index ef858f58d..cd6d61a87 100644 --- a/.github/workflows/code_review_libs.yml +++ b/.github/workflows/code_review_libs.yml @@ -25,7 +25,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v3 with: java-version: 17 distribution: 'temurin' From 0409e8b3f458cd17d43d648609f4d6cad0c6dca7 Mon Sep 17 00:00:00 2001 From: manuraf Date: Wed, 15 Nov 2023 16:31:39 +0100 Subject: [PATCH 12/12] fix labeler --- .github/labeler.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 65d5af2c9..20650649f 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -4,8 +4,8 @@ functions: onboarding-ms: - apps/onboarding-ms/** -onboarding-sdk: -- libs/onboarding-sdk/** +libs: +- libs ops: - .github/**