/* * 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 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.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.security.KeyStore; import java.security.PrivateKey; import java.security.Security; import java.security.cert.X509Certificate; import java.util.ArrayList; public class PkiManager { public static final PkiManager INSTANCE = new PkiManager(); private PkiManager() { } private 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 = sign(cert, 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()); } } public byte[] sign(X509Certificate cert, PrivateKey privatekey, byte[] buffer) { try { Security.addProvider(new BouncyCastleProvider()); ArrayList certList = new ArrayList(); certList.add(cert); Store certs = new JcaCertStore(certList); var cmsSignedDataGenerator = new CMSSignedDataGenerator(); var sha1signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(privatekey); cmsSignedDataGenerator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).build(sha1signer, cert)); cmsSignedDataGenerator.addCertificates(certs); var cmsProcessableByteArray = new CMSProcessableByteArray(buffer); var cmsSignedData = cmsSignedDataGenerator.generate(cmsProcessableByteArray, false); return cmsSignedData.getEncoded(); } catch (Exception e) { throw new IllegalStateException(e.getMessage()); } } public byte[] encrypt(byte[] data, X509Certificate x509Certificate) { try { Security.addProvider(new BouncyCastleProvider()); if ((null == data) || (null == x509Certificate)) { throw new IllegalStateException("Null parameters"); } var cmsEnvelopedDataGenerator = new CMSEnvelopedDataGenerator(); cmsEnvelopedDataGenerator.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(x509Certificate).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(); } catch (Exception e) { throw new IllegalStateException(e.getMessage()); } } public byte[] decrypt(PrivateKey privateKey, byte[] encryptedData) { try { var decryptedStream = decryptToInputStream(privateKey, encryptedData); return decryptedStream.readAllBytes(); } catch (Exception e) { throw new IllegalStateException(e.getMessage()); } } public void decryptToFile(PrivateKey privateKey, byte[] encryptedData, String destinationFile) { try { var decryptedDestination = new File(destinationFile); var decryptedStream = decryptToInputStream(privateKey, encryptedData); Files.copy(decryptedStream, decryptedDestination.toPath()); } catch (Exception e) { throw new IllegalStateException(e.getMessage()); } } private InputStream decryptToInputStream(PrivateKey privateKey, byte[] encryptedData) { try { Security.addProvider(new BouncyCastleProvider()); var cmsEnvelopedDataParser = new CMSEnvelopedDataParser(encryptedData); var singleRecipient = getSingleRecipient(cmsEnvelopedDataParser); var jceKeyTransEnvelopedRecipient = new JceKeyTransEnvelopedRecipient(privateKey); return singleRecipient.getContentStream(jceKeyTransEnvelopedRecipient).getContentStream(); } 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()); } } }