cert-encoder.git

ref: cdc5706e613a0aaf3a9c9e622e26cff1ad0b7324

src/main/java/net/lulli/encrypt/pki/PkiEncryptionManager.java


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/*
 * 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 <http://www.gnu.org/licenses/>.
 */

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 PkiEncryptionManager {
    public static final PkiEncryptionManager INSTANCE = new PkiEncryptionManager();

    private PkiEncryptionManager() {
    }

    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<X509Certificate> certList = new ArrayList<X509Certificate>();
            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());
        }
    }
}