/*
* This file is part of cert-encoder
* Copyright (c) 2024 Paolo Lulli.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package net.lulli.encrypt.pki;
import net.lulli.encrypt.Pems;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.*;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.encoders.Base64;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
public class PkiEncryptionManager {
public static final PkiEncryptionManager INSTANCE = new PkiEncryptionManager();
private PkiEncryptionManager() {}
public void signFile(File fileToSign) {
try {
char[] password = "yourpass".toCharArray();
String certificateAlias = "";
String keyAlias = "";
var keystoreFile = "full/path/to/your/original/certificate.pfx";
Security.addProvider(new BouncyCastleProvider());
KeyStore ks = loadKeyStore(keystoreFile, password);
X509Certificate cert = (X509Certificate) ks.getCertificate(certificateAlias);
PrivateKey privatekey = (PrivateKey) ks.getKey(keyAlias, password);
String signatureFileName = fileToSign.getName().toString() + ".signature";
byte[] buffer = loadFileIntoBuffer(fileToSign);
byte[] signeddata = generateSignature(cert, cert.getPublicKey(), privatekey, buffer);
writeSignatureToFile(signatureFileName, signeddata);
} catch (Exception e) {
throw new IllegalStateException(e.getMessage());
}
}
private static byte[] loadFileIntoBuffer(File fileToSign) {
try {
byte[] buffer = new byte[(int) fileToSign.length()];
try (DataInputStream in = new DataInputStream(new FileInputStream(fileToSign))) {
in.readFully(buffer);
}
return buffer;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage());
}
}
private static byte[] generateSignature(X509Certificate cert, PublicKey publicKey, PrivateKey privatekey, byte[] buffer) {
try {
ArrayList certList = new ArrayList();
certList.add(cert);
Store> certs = new JcaCertStore(certList);
CMSSignedDataGenerator signGen = new CMSSignedDataGenerator();
ContentSigner sha1signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(privatekey);
signGen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).build(sha1signer, cert));
signGen.addCertificates(certs);
CMSTypedData content = new CMSProcessableByteArray(buffer);
CMSSignedData signedData = signGen.generate(content, false);
byte[] signeddata = signedData.getEncoded();
return signeddata;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage());
}
}
public byte[] encryptData(byte[] data, X509Certificate encryptionCertificate) {
try {
Security.addProvider(new BouncyCastleProvider());
if (null != data && null != encryptionCertificate) {
var cmsEnvelopedDataGenerator = new CMSEnvelopedDataGenerator();
//JceKeyTransRecipientInfoGenerator jceKey = new JceKeyTransRecipientInfoGenerator(encryptionCertificate);
cmsEnvelopedDataGenerator.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(encryptionCertificate).setProvider("BC"));
var cmsProcessableByteArray = new CMSProcessableByteArray(data);
var outputEncryptor = new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES256_CBC).setProvider("BC").build();
var cmsEnvelopedData = cmsEnvelopedDataGenerator.generate(cmsProcessableByteArray, outputEncryptor);
return cmsEnvelopedData.getEncoded();
}
return null;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage());
}
}
public void decryptCms(PrivateKey privateKey, byte[] encryptedData, String destinationFile) {
try {
var decryptedDestination = new File(destinationFile);
//byte[] encryptedData = Files.readAllBytes(encrypted.toPath());
var cmsEnvelopedDataParser = new CMSEnvelopedDataParser(encryptedData);
var singleRecipient = getSingleRecipient(cmsEnvelopedDataParser);
var jceKeyTransEnvelopedRecipient = new JceKeyTransEnvelopedRecipient(privateKey);
try (InputStream decryptedStream = singleRecipient.getContentStream(jceKeyTransEnvelopedRecipient).getContentStream()) {
Files.copy(decryptedStream, decryptedDestination.toPath());
}
System.out.println(String.format("Decrypted to file :'%s'", decryptedDestination.getAbsolutePath()));
} catch (Exception e) {
throw new IllegalStateException(e.getMessage());
}
}
private static RecipientInformation getSingleRecipient(CMSEnvelopedDataParser parser) {
var recInfos = parser.getRecipientInfos().getRecipients();
var recipientIterator = recInfos.iterator();
if (!recipientIterator.hasNext()) {
throw new RuntimeException("Could not find recipient");
}
return recipientIterator.next();
}
private static void writeSignatureToFile(String fileName, byte[] signeddata) {
try {
var fileOutputStream = new FileOutputStream(fileName);
byte[] outputString = Base64.encode(signeddata);
int fullLines = (int) Math.floor(outputString.length / 64);
for (int i = 0; i < fullLines; i++) {
fileOutputStream.write(outputString, i * 64, 64);
fileOutputStream.write("\r\n".getBytes());
}
fileOutputStream.write(outputString, fullLines * 64, outputString.length % 64);
fileOutputStream.close();
} catch (Exception e) {
throw new IllegalStateException(e.getMessage());
}
}
private static KeyStore loadKeyStore(String keystoreFile, char[] password) {
try {
var keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(new FileInputStream(keystoreFile), password);
return keyStore;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage());
}
}
/*
public static void main(String[] args) throws Exception {
var plainText = "this is a sample text";
X509Certificate x509Certificate = Pems.certificateFromFile("/home/plulli/.x509crypt/local/certs/local.crt");
//var enc = EncDec.encrypt(plainText, x509Certificate.getPublicKey().getEncoded());
byte[] enc = encryptData(plainText.getBytes(), x509Certificate);
System.out.printf("Cleartext: [%s]\n Encrypted: [%s]\n", plainText, new String(enc));
Files.write(Paths.get("/tmp/output2.txt.enc"), enc);
PrivateKey privateKey = Pems.privateKeyFromFile("/home/plulli/.x509crypt/local/certs/local.key");
decryptCms(privateKey, enc, "/tmp/output2.txt");
}
*/
}