diff --git a/docs-core/src/main/java/com/sismics/docs/core/model/jpa/User.java b/docs-core/src/main/java/com/sismics/docs/core/model/jpa/User.java index 6febd559..3a525ed5 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/model/jpa/User.java +++ b/docs-core/src/main/java/com/sismics/docs/core/model/jpa/User.java @@ -47,6 +47,12 @@ public class User { @Column(name = "USE_PASSWORD_C", nullable = false, length = 100) private String password; + /** + * User's private key. + */ + @Column(name = "USE_PRIVATEKEY_C", nullable = false, length = 100) + private String privateKey; + /** * Email address. */ @@ -256,6 +262,22 @@ public class User { public void setDeleteDate(Date deleteDate) { this.deleteDate = deleteDate; } + + /** + * Getter de privateKey. + * @return privateKey + */ + public String getPrivateKey() { + return privateKey; + } + + /** + * Setter de privateKey. + * @param privateKey privateKey + */ + public void setPrivateKey(String privateKey) { + this.privateKey = privateKey; + } @Override public String toString() { diff --git a/docs-core/src/main/java/com/sismics/docs/core/util/EncryptionUtil.java b/docs-core/src/main/java/com/sismics/docs/core/util/EncryptionUtil.java new file mode 100644 index 00000000..90641c26 --- /dev/null +++ b/docs-core/src/main/java/com/sismics/docs/core/util/EncryptionUtil.java @@ -0,0 +1,91 @@ +package com.sismics.docs.core.util; + +import java.io.InputStream; +import java.math.BigInteger; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.Security; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +/** + * Encryption utilities. + * + * @author bgamard + */ +public class EncryptionUtil { + + /** + * Salt. + */ + private static final String SALT = "LEpxZmm2SMu2PeKzPNrar2rhVAS6LrrgvXKeL9uyXC4vgKHg"; + + /** + * Generate a private key. + * + * @return New random private key + * @throws NoSuchAlgorithmException + */ + public static String generatePrivateKey() throws NoSuchAlgorithmException { + SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); + return new BigInteger(176, random).toString(32); + } + + /** + * Encrypt an InputStream using the specified private key. + * + * @param is InputStream to encrypt + * @param privateKey Private key + * @return Encrypted stream + * @throws Exception + */ + public static InputStream encryptStream(InputStream is, String privateKey) throws Exception { + checkBouncyCastleProvider(); + return new CipherInputStream(is, getCipher(privateKey, Cipher.ENCRYPT_MODE)); + } + + /** + * Decrypt an InputStream using the specified private key. + * + * @param is InputStream to encrypt + * @param privateKey Private key + * @return Encrypted stream + * @throws Exception + */ + public static InputStream decryptStream(InputStream is, String privateKey) throws Exception { + checkBouncyCastleProvider(); + return new CipherInputStream(is, getCipher(privateKey, Cipher.DECRYPT_MODE)); + } + + /** + * Initialize a Cipher. + * + * @param privateKey Private key + * @param mode Mode (encrypt or decrypt) + * @return Cipher + * @throws Exception + */ + private static Cipher getCipher(String privateKey, int mode) throws Exception { + PBEKeySpec keySpec = new PBEKeySpec(privateKey.toCharArray(), SALT.getBytes(), 2000, 256); + SecretKeyFactory skf = SecretKeyFactory.getInstance("PBEWITHSHA256AND256BITAES-CBC-BC"); + SecretKey desKey = skf.generateSecret(keySpec); + Cipher cipher = Cipher.getInstance("AES/CTR/NOPADDING"); + cipher.init(mode, desKey); + return cipher; + } + + /** + * Initialize the Bouncy Castle provider if necessary. + */ + private static void checkBouncyCastleProvider() { + if (Security.getProvider("BouncyCastleProvider") == null) { + Security.insertProviderAt(new BouncyCastleProvider(), 1); + } + } +} diff --git a/docs-core/src/main/java/com/sismics/docs/core/util/FileUtil.java b/docs-core/src/main/java/com/sismics/docs/core/util/FileUtil.java index 9d19bb4c..7fc7c78a 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/util/FileUtil.java +++ b/docs-core/src/main/java/com/sismics/docs/core/util/FileUtil.java @@ -3,18 +3,11 @@ package com.sismics.docs.core.util; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; -import javax.crypto.Cipher; -import javax.crypto.CipherInputStream; -import javax.crypto.CipherOutputStream; -import javax.crypto.SecretKey; -import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.PBEKeySpec; import javax.imageio.ImageIO; import net.sourceforge.tess4j.Tesseract; @@ -28,7 +21,6 @@ import org.imgscalr.Scalr.Mode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.io.ByteStreams; import com.sismics.docs.core.model.jpa.Document; import com.sismics.docs.core.model.jpa.File; import com.sismics.util.ImageUtil; @@ -207,41 +199,4 @@ public class FileUtil { thumbnailFile.delete(); } } - -// Security.insertProviderAt(new BouncyCastleProvider(), 1); -// String key = "pwd"; -// -// FileInputStream fis = new FileInputStream("plain.jpg"); -// FileOutputStream fos = new FileOutputStream("encrypted.jpg"); -// encrypt(key, fis, fos); -// -// FileInputStream fis2 = new FileInputStream("encrypted.jpg"); -// FileOutputStream fos2 = new FileOutputStream("decrypted.jpg"); -// decrypt(key, fis2, fos2); - - public static void encrypt(String key, InputStream is, OutputStream os) throws Throwable { - encryptOrDecrypt(key, Cipher.ENCRYPT_MODE, is, os); - } - - public static void decrypt(String key, InputStream is, OutputStream os) throws Throwable { - encryptOrDecrypt(key, Cipher.DECRYPT_MODE, is, os); - } - - public static void encryptOrDecrypt(String key, int mode, InputStream is, OutputStream os) throws Throwable { - - PBEKeySpec keySpec = new PBEKeySpec(key.toCharArray(), "salt".getBytes(), 2000, 256); - SecretKeyFactory skf = SecretKeyFactory.getInstance("PBEWITHSHA256AND256BITAES-CBC-BC"); - SecretKey desKey = skf.generateSecret(keySpec); - Cipher cipher = Cipher.getInstance("AES/CTR/NOPADDING"); - - if (mode == Cipher.ENCRYPT_MODE) { - cipher.init(Cipher.ENCRYPT_MODE, desKey); - CipherInputStream cis = new CipherInputStream(is, cipher); - ByteStreams.copy(cis, os); - } else if (mode == Cipher.DECRYPT_MODE) { - cipher.init(Cipher.DECRYPT_MODE, desKey); - CipherOutputStream cos = new CipherOutputStream(os, cipher); - ByteStreams.copy(is, cos); - } - } } diff --git a/docs-core/src/test/java/com/sismics/docs/core/util/TestEncryptUtil.java b/docs-core/src/test/java/com/sismics/docs/core/util/TestEncryptUtil.java new file mode 100644 index 00000000..1ef27276 --- /dev/null +++ b/docs-core/src/test/java/com/sismics/docs/core/util/TestEncryptUtil.java @@ -0,0 +1,51 @@ +package com.sismics.docs.core.util; + +import java.io.InputStream; + +import junit.framework.Assert; + +import org.bouncycastle.util.io.Streams; +import org.junit.Test; + +import com.google.common.base.Strings; +import com.google.common.io.ByteStreams; + +/** + * Test of the encryption utilities. + * + * @author bgamard + */ +public class TestEncryptUtil { + + /** + * Test private key. + */ + String pk = "OnceUponATime"; + + @Test + public void generatePrivateKeyTest() throws Exception { + String key = EncryptionUtil.generatePrivateKey(); + System.out.println(key); + Assert.assertFalse(Strings.isNullOrEmpty(key)); + } + + @Test + public void encryptStreamTest() throws Exception { + InputStream inputStream = EncryptionUtil.encryptStream(this.getClass().getResourceAsStream("/file/udhr.pdf"), pk); + byte[] encryptedData = Streams.readAll(inputStream); + byte[] assertData = Streams.readAll(this.getClass().getResourceAsStream("/file/udhr_encrypted.pdf")); + Assert.assertTrue(ByteStreams.equal( + ByteStreams.newInputStreamSupplier(encryptedData), + ByteStreams.newInputStreamSupplier(assertData))); + } + + @Test + public void decryptStreamTest() throws Exception { + InputStream inputStream = EncryptionUtil.decryptStream(this.getClass().getResourceAsStream("/file/udhr_encrypted.pdf"), pk); + byte[] encryptedData = Streams.readAll(inputStream); + byte[] assertData = Streams.readAll(this.getClass().getResourceAsStream("/file/udhr.pdf")); + Assert.assertTrue(ByteStreams.equal( + ByteStreams.newInputStreamSupplier(encryptedData), + ByteStreams.newInputStreamSupplier(assertData))); + } +} diff --git a/docs-core/src/test/resources/file/udhr.pdf b/docs-core/src/test/resources/file/udhr.pdf new file mode 100644 index 00000000..f1929247 Binary files /dev/null and b/docs-core/src/test/resources/file/udhr.pdf differ diff --git a/docs-core/src/test/resources/file/udhr_encrypted.pdf b/docs-core/src/test/resources/file/udhr_encrypted.pdf new file mode 100644 index 00000000..eef89c02 Binary files /dev/null and b/docs-core/src/test/resources/file/udhr_encrypted.pdf differ diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/UserResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/UserResource.java index ccb6e401..bcf9a8ab 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/UserResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/UserResource.java @@ -7,6 +7,7 @@ import com.sismics.docs.core.dao.jpa.UserDao; import com.sismics.docs.core.dao.jpa.dto.UserDto; import com.sismics.docs.core.model.jpa.AuthenticationToken; import com.sismics.docs.core.model.jpa.User; +import com.sismics.docs.core.util.EncryptionUtil; import com.sismics.docs.core.util.jpa.PaginatedList; import com.sismics.docs.core.util.jpa.PaginatedLists; import com.sismics.docs.core.util.jpa.SortCriteria; @@ -28,6 +29,8 @@ import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.NewCookie; import javax.ws.rs.core.Response; + +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -76,6 +79,11 @@ public class UserResource extends BaseResource { user.setUsername(username); user.setPassword(password); user.setEmail(email); + try { + user.setPrivateKey(EncryptionUtil.generatePrivateKey()); + } catch (NoSuchAlgorithmException e) { + throw new ServerException("PrivateKeyError", "Error while generating a private key", e); + } user.setCreateDate(new Date()); if (localeId == null) {