package wow.doge.chatto.service import wow.doge.chatto.model.MessageCipher; import java.util.Base64; import javax.crypto.Cipher;; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; import java.security.SecureRandom import scala.util.Try class EncryptionServiceImpl extends EncryptionService { override def encrypt( password: String, plainText: String ): String Either MessageCipher = { val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256") val secureRandom = new SecureRandom() val saltLength = 12 val keyLength = 128 val iterationCount = 10000 val tagSize = 128 val encode = (bytes: Array[Byte]) => Base64.getEncoder().encodeToString(bytes) val salt = Array(saltLength.toByte) secureRandom.nextBytes(salt) val nonce = Array(12.toByte) secureRandom.nextBytes(nonce) // val spec = // new PBEKeySpec(password.toCharArray(), salt, iterationCount, keyLength) // val tmp = factory.generateSecret(spec) // val secretKey = new SecretKeySpec(tmp.getEncoded(), "AES") // val cipher = Cipher.getInstance("AES/GCM/NoPadding") // cipher.init( // Cipher.ENCRYPT_MODE, // secretKey, // new GCMParameterSpec(128, nonce) // ) // val cipherTextByte = cipher.doFinal(plainText.getBytes) val messageCipher = for { factory <- { Try(SecretKeyFactory.getInstance("PBKDFWithHmacSHA56")).toOption .toRight("Failed to get skf instance") } spec <- { Try( new PBEKeySpec( password.toCharArray(), salt, iterationCount, keyLength ) ).toOption.toRight("Failed to get pbekeyspec") } secret <- Try(factory.generateSecret(spec)).toOption .toRight("Failed to get secret") secretKey <- Try(new SecretKeySpec(secret.getEncoded(), "AES")).toOption .toRight("Failed to get secret key") cipher <- Try(Cipher.getInstance("AES/GCM/NoPadding")).toOption .toRight("Failed to get cipher instance") _ <- Right( cipher.init( Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(128, nonce) ) ) cipherTextByte <- Try(cipher.doFinal(plainText.getBytes())).toOption .toRight("Failed to generate cipher") messageCipher = MessageCipher( v = 1, salt = encode(salt), mode = "gcm", iterations = iterationCount, cipher = "aes", adata = "", cipherText = encode(cipherTextByte), iv = encode(nonce), keySize = keyLength, tagSize = tagSize ) } yield (messageCipher) messageCipher } override def decrypt( password: String, messageCipher: MessageCipher ): Try[String] = { val decode = (text: String) => { Base64.getDecoder().decode(text) } Try { val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256") val spec = new PBEKeySpec( password.toCharArray(), decode(messageCipher.salt), messageCipher.iterations, messageCipher.keySize ); val tmp = factory.generateSecret(spec); val secretKey = new SecretKeySpec(tmp.getEncoded(), "AES"); val cipher = Cipher.getInstance("AES/GCM/NoPadding") cipher.init( Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(128, decode(messageCipher.iv)) ); new String(cipher.doFinal(decode(messageCipher.cipherText))); } } } object EncryptionServiceImpl { def apply() = new EncryptionServiceImpl() }