diff --git a/core-java-modules/core-java-security-algorithms/pom.xml b/core-java-modules/core-java-security-algorithms/pom.xml new file mode 100644 index 0000000000..db1bf09ead --- /dev/null +++ b/core-java-modules/core-java-security-algorithms/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + core-java-security-algorithms + 0.1.0-SNAPSHOT + core-java-security-algorithms + jar + + + com.baeldung.core-java-modules + core-java-modules + 0.0.1-SNAPSHOT + + + + + commons-codec + commons-codec + ${commons-codec.version} + + + org.bouncycastle + bcprov-jdk15on + ${bouncycastle.version} + + + + org.assertj + assertj-core + ${assertj-core.version} + test + + + + javax.xml.bind + jaxb-api + ${jaxb-api.version} + + + + + + 1.60 + 1.11 + + 3.18.0 + 2.3.1 + + + \ No newline at end of file diff --git a/core-java-modules/core-java-security-algorithms/src/main/java/com/baeldung/aes/AESUtil.java b/core-java-modules/core-java-security-algorithms/src/main/java/com/baeldung/aes/AESUtil.java new file mode 100644 index 0000000000..2952eef625 --- /dev/null +++ b/core-java-modules/core-java-security-algorithms/src/main/java/com/baeldung/aes/AESUtil.java @@ -0,0 +1,156 @@ +package com.baeldung.aes; + +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.BadPaddingException; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKeyFactory; +import javax.crypto.SealedObject; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.Serializable; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.util.Base64; + +public class AESUtil { + + public static String encrypt(String algorithm, String input, SecretKey key, IvParameterSpec iv) + throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, + InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.ENCRYPT_MODE, key, iv); + byte[] cipherText = cipher.doFinal(input.getBytes()); + return Base64.getEncoder() + .encodeToString(cipherText); + } + + public static String decrypt(String algorithm, String cipherText, SecretKey key, IvParameterSpec iv) + throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, + InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.DECRYPT_MODE, key, iv); + byte[] plainText = cipher.doFinal(Base64.getDecoder() + .decode(cipherText)); + return new String(plainText); + } + + public static SecretKey generateKey(int n) throws NoSuchAlgorithmException { + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); + keyGenerator.init(n); + SecretKey key = keyGenerator.generateKey(); + return key; + } + + public static SecretKey getKeyFromPassword(String password, String salt) + throws NoSuchAlgorithmException, InvalidKeySpecException { + SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); + KeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), 65536, 256); + SecretKey secret = new SecretKeySpec(factory.generateSecret(spec) + .getEncoded(), "AES"); + return secret; + } + + public static IvParameterSpec generateIv() { + byte[] iv = new byte[16]; + new SecureRandom().nextBytes(iv); + return new IvParameterSpec(iv); + } + + public static void encryptFile(String algorithm, SecretKey key, IvParameterSpec iv, + File inputFile, File outputFile) throws IOException, NoSuchPaddingException, + NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, + BadPaddingException, IllegalBlockSizeException { + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.ENCRYPT_MODE, key, iv); + FileInputStream inputStream = new FileInputStream(inputFile); + FileOutputStream outputStream = new FileOutputStream(outputFile); + byte[] buffer = new byte[64]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + byte[] output = cipher.update(buffer, 0, bytesRead); + if (output != null) { + outputStream.write(output); + } + } + byte[] outputBytes = cipher.doFinal(); + if (outputBytes != null) { + outputStream.write(outputBytes); + } + inputStream.close(); + outputStream.close(); + } + + public static void decryptFile(String algorithm, SecretKey key, IvParameterSpec iv, + File encryptedFile, File decryptedFile) throws IOException, NoSuchPaddingException, + NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, + BadPaddingException, IllegalBlockSizeException { + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.DECRYPT_MODE, key, iv); + FileInputStream inputStream = new FileInputStream(encryptedFile); + FileOutputStream outputStream = new FileOutputStream(decryptedFile); + byte[] buffer = new byte[64]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + byte[] output = cipher.update(buffer, 0, bytesRead); + if (output != null) { + outputStream.write(output); + } + } + byte[] output = cipher.doFinal(); + if (output != null) { + outputStream.write(output); + } + inputStream.close(); + outputStream.close(); + } + + public static SealedObject encryptObject(String algorithm, Serializable object, SecretKey key, + IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException, + InvalidAlgorithmParameterException, InvalidKeyException, IOException, IllegalBlockSizeException { + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.ENCRYPT_MODE, key, iv); + SealedObject sealedObject = new SealedObject(object, cipher); + return sealedObject; + } + + public static Serializable decryptObject(String algorithm, SealedObject sealedObject, SecretKey key, + IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException, + InvalidAlgorithmParameterException, InvalidKeyException, ClassNotFoundException, + BadPaddingException, IllegalBlockSizeException, IOException { + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.DECRYPT_MODE, key, iv); + Serializable unsealObject = (Serializable) sealedObject.getObject(cipher); + return unsealObject; + } + + public static String encryptPasswordBased(String plainText, SecretKey key, IvParameterSpec iv) + throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, + InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, key, iv); + return Base64.getEncoder() + .encodeToString(cipher.doFinal(plainText.getBytes())); + } + + public static String decryptPasswordBased(String cipherText, SecretKey key, IvParameterSpec iv) + throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, + InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); + cipher.init(Cipher.DECRYPT_MODE, key, iv); + return new String(cipher.doFinal(Base64.getDecoder() + .decode(cipherText))); + } + +} diff --git a/core-java-modules/core-java-security-algorithms/src/main/java/com/baeldung/aes/Student.java b/core-java-modules/core-java-security-algorithms/src/main/java/com/baeldung/aes/Student.java new file mode 100644 index 0000000000..13cd1c5e9d --- /dev/null +++ b/core-java-modules/core-java-security-algorithms/src/main/java/com/baeldung/aes/Student.java @@ -0,0 +1,40 @@ +package com.baeldung.aes; + +import java.io.Serializable; +import java.util.Objects; + +public class Student implements Serializable { + private String name; + private int age; + + public Student(String name, int age) { + this.name = name; + this.age = age; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Student student = (Student) o; + return age == student.age && Objects.equals(name, student.name); + } +} diff --git a/core-java-modules/core-java-security-algorithms/src/test/java/com/baeldung/aes/AESUtilUnitTest.java b/core-java-modules/core-java-security-algorithms/src/test/java/com/baeldung/aes/AESUtilUnitTest.java new file mode 100644 index 0000000000..531c20ca79 --- /dev/null +++ b/core-java-modules/core-java-security-algorithms/src/test/java/com/baeldung/aes/AESUtilUnitTest.java @@ -0,0 +1,101 @@ +package com.baeldung.aes; + +import org.assertj.core.api.WithAssertions; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import javax.crypto.SealedObject; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; + +class AESUtilUnitTest implements WithAssertions { + + @Test + void givenString_whenEncrypt_thenSuccess() + throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, + BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException { + // given + String input = "baeldung"; + SecretKey key = AESUtil.generateKey(128); + IvParameterSpec ivParameterSpec = AESUtil.generateIv(); + String algorithm = "AES/CBC/PKCS5Padding"; + + // when + String cipherText = AESUtil.encrypt(algorithm, input, key, ivParameterSpec); + String plainText = AESUtil.decrypt(algorithm, cipherText, key, ivParameterSpec); + + // then + Assertions.assertEquals(input, plainText); + } + + @Test + void givenFile_whenEncrypt_thenSuccess() + throws NoSuchAlgorithmException, IOException, IllegalBlockSizeException, InvalidKeyException, + BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException { + // given + SecretKey key = AESUtil.generateKey(128); + String algorithm = "AES/CBC/PKCS5Padding"; + IvParameterSpec ivParameterSpec = AESUtil.generateIv(); + File inputFile = Paths.get("src/test/resources/baeldung.txt") + .toFile(); + File encryptedFile = new File("classpath:baeldung.encrypted"); + File decryptedFile = new File("document.decrypted"); + + // when + AESUtil.encryptFile(algorithm, key, ivParameterSpec, inputFile, encryptedFile); + AESUtil.decryptFile(algorithm, key, ivParameterSpec, encryptedFile, decryptedFile); + + // then + assertThat(inputFile).hasSameTextualContentAs(decryptedFile); + encryptedFile.delete(); + decryptedFile.delete(); + } + + @Test + void givenObject_whenEncrypt_thenSuccess() + throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, + InvalidAlgorithmParameterException, NoSuchPaddingException, IOException, BadPaddingException, + ClassNotFoundException { + // given + Student student = new Student("Baeldung", 20); + SecretKey key = AESUtil.generateKey(128); + IvParameterSpec ivParameterSpec = AESUtil.generateIv(); + String algorithm = "AES/CBC/PKCS5Padding"; + + // when + SealedObject sealedObject = AESUtil.encryptObject(algorithm, student, key, ivParameterSpec); + Student object = (Student) AESUtil.decryptObject(algorithm, sealedObject, key, ivParameterSpec); + + // then + assertThat(student).isEqualTo(object); + } + + @Test + void givenPassword_whenEncrypt_thenSuccess() + throws InvalidKeySpecException, NoSuchAlgorithmException, IllegalBlockSizeException, + InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException { + // given + String plainText = "www.baeldung.com"; + String password = "baeldung"; + String salt = "12345678"; + IvParameterSpec ivParameterSpec = AESUtil.generateIv(); + SecretKey key = AESUtil.getKeyFromPassword(password, salt); + + // when + String cipherText = AESUtil.encryptPasswordBased(plainText, key, ivParameterSpec); + String decryptedCipherText = AESUtil.decryptPasswordBased(cipherText, key, ivParameterSpec); + + // then + Assertions.assertEquals(plainText, decryptedCipherText); + } +} diff --git a/core-java-modules/core-java-security-algorithms/src/test/java/com/baeldung/cipher/AvailableCiphersUnitTest.java b/core-java-modules/core-java-security-algorithms/src/test/java/com/baeldung/cipher/AvailableCiphersUnitTest.java new file mode 100644 index 0000000000..fa38aca272 --- /dev/null +++ b/core-java-modules/core-java-security-algorithms/src/test/java/com/baeldung/cipher/AvailableCiphersUnitTest.java @@ -0,0 +1,35 @@ +package com.baeldung.cipher; + +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.security.Provider; +import java.security.Security; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class AvailableCiphersUnitTest { + private final Logger logger = LoggerFactory.getLogger(AvailableCiphersUnitTest.class); + + @Test + public void whenGetServices_thenGetAllCipherAlgorithms() { + for (Provider provider : Security.getProviders()) { + for (Provider.Service service : provider.getServices()) { + logger.info(service.getAlgorithm()); + } + } + } + + @Test + public void whenGetServicesWithFilter_thenGetAllCompatibleCipherAlgorithms() { + List algorithms = Arrays.stream(Security.getProviders()) + .flatMap(provider -> provider.getServices().stream()) + .filter(service -> "Cipher".equals(service.getType())) + .map(Provider.Service::getAlgorithm) + .collect(Collectors.toList()); + + algorithms.forEach(logger::info); + } +} diff --git a/core-java-modules/core-java-security-algorithms/src/test/java/com/baeldung/des/TripleDESUnitTest.java b/core-java-modules/core-java-security-algorithms/src/test/java/com/baeldung/des/TripleDESUnitTest.java new file mode 100644 index 0000000000..6e5df8e619 --- /dev/null +++ b/core-java-modules/core-java-security-algorithms/src/test/java/com/baeldung/des/TripleDESUnitTest.java @@ -0,0 +1,90 @@ +package com.baeldung.des; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +public class TripleDESUnitTest { + + @Test + public void given3DesKey_whenEncryptAndDecryptString_thenCompareResults() throws Exception { + byte[] secretKey = "9mng65v8jf4lxn93nabf981m".getBytes(); + byte[] iv = "a76nb5h9".getBytes(); + + String secretMessage = "Baeldung secret message"; + + SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "DESede"); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + Cipher encryptCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding"); + encryptCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec); + byte[] secretMessagesBytes = secretMessage.getBytes(StandardCharsets.UTF_8); + byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessagesBytes); + + Cipher decryptCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding"); + decryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec); + byte[] decryptedMessageBytes = decryptCipher.doFinal(encryptedMessageBytes); + String decryptedMessage = new String(decryptedMessageBytes, StandardCharsets.UTF_8); + + Assertions.assertEquals(secretMessage, decryptedMessage); + } + + @Test + public void given3DesKey_whenEncryptAndDecryptFile_thenCompareResults() throws Exception { + byte[] secretKey = "9mng65v8jf4lxn93nabf981m".getBytes(); + byte[] iv = "a76nb5h9".getBytes(); + + SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "DESede"); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + + String originalContent = "some secret message"; + Path tempFile = Files.createTempFile("temp", "txt"); + writeString(tempFile, originalContent); + + byte[] fileBytes = Files.readAllBytes(tempFile); + Cipher encryptCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding"); + encryptCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec); + byte[] encryptedFileBytes = encryptCipher.doFinal(fileBytes); + try (FileOutputStream stream = new FileOutputStream(tempFile.toFile())) { + stream.write(encryptedFileBytes); + } + + encryptedFileBytes = Files.readAllBytes(tempFile); + Cipher decryptCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding"); + decryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec); + byte[] decryptedFileBytes = decryptCipher.doFinal(encryptedFileBytes); + try (FileOutputStream stream = new FileOutputStream(tempFile.toFile())) { + stream.write(decryptedFileBytes); + } + + String fileContent = readString(tempFile); + + Assertions.assertEquals(originalContent, fileContent); + } + + private void writeString(Path path, String content) throws Exception { + try (BufferedWriter writer = Files.newBufferedWriter(path)) { + writer.write(content); + } + } + + private String readString(Path path) throws Exception { + StringBuilder resultStringBuilder = new StringBuilder(); + try (BufferedReader br = new BufferedReader(new FileReader(path.toFile()))) { + String line; + while ((line = br.readLine()) != null) { + resultStringBuilder.append(line); + } + } + return resultStringBuilder.toString(); + } +} diff --git a/core-java-modules/core-java-security-algorithms/src/test/java/com/baeldung/rsa/RsaUnitTest.java b/core-java-modules/core-java-security-algorithms/src/test/java/com/baeldung/rsa/RsaUnitTest.java new file mode 100644 index 0000000000..9659fd8871 --- /dev/null +++ b/core-java-modules/core-java-security-algorithms/src/test/java/com/baeldung/rsa/RsaUnitTest.java @@ -0,0 +1,92 @@ +package com.baeldung.rsa; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import javax.crypto.Cipher; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; + +public class RsaUnitTest { + + @Test + public void givenRsaKeyPair_whenEncryptAndDecryptString_thenCompareResults() throws Exception { + KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); + generator.initialize(2048); + KeyPair pair = generator.generateKeyPair(); + PrivateKey privateKey = pair.getPrivate(); + PublicKey publicKey = pair.getPublic(); + + String secretMessage = "Baeldung secret message"; + Cipher encryptCipher = Cipher.getInstance("RSA"); + encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] secretMessageBytes = secretMessage.getBytes(StandardCharsets.UTF_8); + byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessageBytes); + + Cipher decryptCipher = Cipher.getInstance("RSA"); + decryptCipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] decryptedMessageBytes = decryptCipher.doFinal(encryptedMessageBytes); + String decryptedMessage = new String(decryptedMessageBytes, StandardCharsets.UTF_8); + + Assertions.assertEquals(secretMessage, decryptedMessage); + } + + @Test + public void givenRsaKeyPair_whenEncryptAndDecryptFile_thenCompareResults() throws Exception { + KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); + generator.initialize(2048); + KeyPair pair = generator.generateKeyPair(); + PrivateKey privateKey = pair.getPrivate(); + PublicKey publicKey = pair.getPublic(); + + String originalContent = "some secret message"; + Path tempFile = Files.createTempFile("temp", "txt"); + writeString(tempFile, originalContent); + + byte[] fileBytes = Files.readAllBytes(tempFile); + Cipher encryptCipher = Cipher.getInstance("RSA"); + encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] encryptedFileBytes = encryptCipher.doFinal(fileBytes); + try (FileOutputStream stream = new FileOutputStream(tempFile.toFile())) { + stream.write(encryptedFileBytes); + } + + encryptedFileBytes = Files.readAllBytes(tempFile); + Cipher decryptCipher = Cipher.getInstance("RSA"); + decryptCipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] decryptedFileBytes = decryptCipher.doFinal(encryptedFileBytes); + try (FileOutputStream stream = new FileOutputStream(tempFile.toFile())) { + stream.write(decryptedFileBytes); + } + + String fileContent = readString(tempFile); + + Assertions.assertEquals(originalContent, fileContent); + } + + private void writeString(Path path, String content) throws Exception { + try (BufferedWriter writer = Files.newBufferedWriter(path)) { + writer.write(content); + } + } + + private String readString(Path path) throws Exception { + StringBuilder resultStringBuilder = new StringBuilder(); + try (BufferedReader br = new BufferedReader(new FileReader(path.toFile()))) { + String line; + while ((line = br.readLine()) != null) { + resultStringBuilder.append(line); + } + } + return resultStringBuilder.toString(); + } +}