cert-encoder.git

commit d648729baf797b2d84f8afdfc9e49c7670af47e9

Author: Paolo Lulli <paolo@lulli.net>

Adjust README

%!v(PANIC=String method: strings: negative Repeat count)


diff --git a/README.md b/README.md
index cc255ce73c652d631f35276fd858b4cbc166e5d8..770961c8dedc1fad8962cb68a747512a6eb724d7 100644
--- a/README.md
+++ b/README.md
@@ -1,26 +1,47 @@
 # Cert-Encoder
 
-An easy library to encrypt and decrypt both with symmetric encryption and PKI
+Cert-Encoder is a Java library that implements two strategies for encrypting/decrypting data:
+
+-   A symmetric schema using AES-CBC-256
+-   A schema based on a public/private keypair (PKCS#7 / CMS)
+
+The asymmetric schema is meant to be intoperable with [X509Crypt](https://kevwe.com/project/project:x509crypt)
+and can encode/decode data within your application that you encoded on the shell, ultimately making use of
+of the command `openssl smime`
+
+## Symmetric Encryption
+
+Symmetric Encryption is the easiest option, in case you can rely on the security of the key and 
+you don't have any specific need with regard to distributed systems.
+
+## Asymmetric Encryption
+
+In analogy with [X509Crypt](https://kevwe.com/project/project:x509crypt) which is thought to 
+encrypt a file in a format that only the desired recipient will be able to read/decrypt.
+While  [X509Crypt](https://kevwe.com/project/project:x509crypt) is meant to be used on the commandline,
+Cert-Encoder is meant to be embedded into your Java application.
 
-## What it this
+## Use Case
 
-Cert-Encoder is a Java library that implements two strategy for encrypting/decrypting data:
+Remote machines can encrypt the data on a server without having to disclose a private key,
+which is sensible information.
 
--   A symmetric schema using AES-CBC-256
--   A schema based on a public/private keypair (PKCS#7 / CMS)
+With PKI it is possible to host on the same storage content that is encrypted for distinct recipients
+without disclosing the keys.
+
+Each recipient cannot decrypt a message that is not aimed at them, recipients can only decrypt their own data.
 
 ## How To Build the library
 
 ```
-cd cert-signer
+cd cert-encoder
 mvn clean install
 ```
-
 
 ## License
 
 The code is freely available under GPL License
 see: [COPYING](/cert-encoder.git/tree/master/COPYING)
 
-Additional commercial support and licensing is available on request. Just issue a [support request](https://kevwe.com/message)
+Additional commercial support and licensing is available on request. You can issue a [support request](https://kevwe.com/message)
 and mention you are interested in [cert-encoder]()




diff --git a/src/main/java/net/lulli/encrypt/pki/PkiEncryptionManager.java b/src/main/java/net/lulli/encrypt/pki/PkiEncryptionManager.java
deleted file mode 100644
index 0b876e9ddb57da97cdc24c45d4dc608663bdec2f..0000000000000000000000000000000000000000
--- a/src/main/java/net/lulli/encrypt/pki/PkiEncryptionManager.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * 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());
-        }
-    }
-}




diff --git a/src/main/java/net/lulli/encrypt/pki/PkiManager.java b/src/main/java/net/lulli/encrypt/pki/PkiManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..2c454e2e6749e2ccc92b16d223342238dbe49ef6
--- /dev/null
+++ b/src/main/java/net/lulli/encrypt/pki/PkiManager.java
@@ -0,0 +1,190 @@
+/*
+ * 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 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<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());
+        }
+    }
+}




diff --git a/src/main/java/net/lulli/encrypt/symmetric/SymmetricEncryptionManager.java b/src/main/java/net/lulli/encrypt/symmetric/SymmetricEncryptionManager.java
index bdb4020af4000dde3af31f241e63df2f9abdc3b1..0dda06f70fd557cfb4ce92ca2ec035875510890b 100644
--- a/src/main/java/net/lulli/encrypt/symmetric/SymmetricEncryptionManager.java
+++ b/src/main/java/net/lulli/encrypt/symmetric/SymmetricEncryptionManager.java
@@ -85,6 +85,7 @@
             // Decrypt.
             var cipherDecrypt = Cipher.getInstance("AES/CBC/PKCS5Padding");
             cipherDecrypt.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
+
             return cipherDecrypt.doFinal(encryptedBytes);
 
         } catch (Exception e) {




diff --git a/src/test/java/EncryptionTest.java b/src/test/java/EncryptionTest.java
index c21b639281ae888c6a4eeafef15427f13b453ca1..3c5607cd1bb203fe68f49895f784acb8b77473c5 100644
--- a/src/test/java/EncryptionTest.java
+++ b/src/test/java/EncryptionTest.java
@@ -16,23 +16,23 @@  * along with this program. If not, see .
  */
 
 import net.lulli.encrypt.Pems;
-import net.lulli.encrypt.pki.PkiEncryptionManager;
+import net.lulli.encrypt.pki.PkiManager;
 import net.lulli.encrypt.symmetric.SymmetricEncryptionManager;
 import org.junit.Test;
 
 import java.io.File;
 import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.util.Arrays;
 
 public class EncryptionTest {
-
     @Test
     public void symmetricEncryptDecrypt() {
         var symmetricEncryptionManager = SymmetricEncryptionManager.INSTANCE;
         var plainText = "This is some long cleartext";
-        var key = "abcdefghijklmnopqrstuvz".getBytes(Charset.forName("UTF-8"));
+        var key = "abcdefghijklmnopqrstuvz".getBytes(StandardCharsets.UTF_8);
 
         byte[] encrypted = symmetricEncryptionManager.encrypt(plainText, key);
         byte[] decrypted = symmetricEncryptionManager.decrypt(encrypted, key);
@@ -42,7 +42,7 @@     }
 
     @Test
     public void pkiEncryptDecrypt() throws Exception {
-        var pkiEncryptionManager = PkiEncryptionManager.INSTANCE;
+        var pkiEncryptionManager = PkiManager.INSTANCE;
 
         var outputEncryptedFile = File.createTempFile("pkiEncrDecr.", ".enc");
         var outputDencryptedFile = File.createTempFile("pkiEncrDecr", ".dec");
@@ -69,7 +69,7 @@     }
 
     @Test
     public void pkiSign() throws Exception {
-        var pkiEncryptionManager = PkiEncryptionManager.INSTANCE;
+        var pkiEncryptionManager = PkiManager.INSTANCE;
 
         var plainText = "this is a sample text";
         var certString = Files.readString(Paths.get(getClass().getResource("local.crt").toURI()), Charset.forName("utf-8"));