Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Onboarding SDK Crypto Module for Cryptographic Operations #40

Merged
merged 12 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ functions:
onboarding-ms:
- apps/onboarding-ms/**

onboarding-sdk:
- apps/onboarding-sdk/**
libs:
- libs

ops:
- .github/**
Expand Down
38 changes: 38 additions & 0 deletions libs/onboarding-sdk-crypto/.gitignore
Original file line number Diff line number Diff line change
@@ -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
60 changes: 60 additions & 0 deletions libs/onboarding-sdk-crypto/README.md
Original file line number Diff line number Diff line change
@@ -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 <i>local</i> | |
| crypto.key.private | The certificate (PEM) used when the pkcs7 hash signature source is <i>local</i> | |


## Installation

To use this library in your projects, you can add the dependency to your pom.xml if you're using Maven:

```shell script
<dependency>
<groupId>it.pagopa.selfcare</groupId>
<artifactId>onboarding-sdk-crypto</artifactId>
<version>0.1.0</version>
</dependency>
```
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);
}
```
72 changes: 72 additions & 0 deletions libs/onboarding-sdk-crypto/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>it.pagopa.selfcare</groupId>
<artifactId>onboarding-sdk-pom</artifactId>
<version>0.1.0</version>
<relativePath>../onboarding-sdk-pom</relativePath>
</parent>
<artifactId>onboarding-sdk-crypto</artifactId>
<name>onboarding-sdk-crypto</name>
<url>http://maven.apache.org</url>

<dependencies>

<dependency>
<groupId>jakarta.xml.soap</groupId>
<artifactId>jakarta.xml.soap-api</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>jakarta.xml.ws</groupId>
<artifactId>jakarta.xml.ws-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-rt</artifactId>
<version>4.0.2</version>
</dependency>
<dependency>
<groupId>jakarta.activation</groupId>
<artifactId>jakarta.activation-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>com.sun.xml.messaging.saaj</groupId>
<artifactId>saaj-impl</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>

<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.27</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package it.pagopa.selfcare.onboarding.crypto;

import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;

public interface Pkcs7HashSignService extends SignatureInterface {
}
Original file line number Diff line number Diff line change
@@ -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);
}
}

}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading
Loading