Skip to content

Commit

Permalink
feat: Onboarding SDK Crypto Module for Cryptographic Operations (#40)
Browse files Browse the repository at this point in the history
  • Loading branch information
manuraf authored Nov 15, 2023
1 parent 17704ce commit 18fafff
Show file tree
Hide file tree
Showing 21 changed files with 827 additions and 16 deletions.
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

0 comments on commit 18fafff

Please sign in to comment.