Last active
September 22, 2020 06:45
-
-
Save satorg/b92b978015efbbfd70b5cad349c396a6 to your computer and use it in GitHub Desktop.
Simple Password Based Encryptor/Decryptor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.nio.ByteBuffer | |
import java.security.{Key, SecureRandom} | |
import javax.crypto.spec.{IvParameterSpec, PBEKeySpec, SecretKeySpec} | |
import javax.crypto.{Cipher, SecretKeyFactory} | |
import scala.util.chaining._ | |
class SPBCryptor(iterationCount: Int = SPBCryptor.DefaultIterationCount) { | |
import SPBCryptor._ | |
def createKey(password: Array[Char], salt: Array[Byte]): Key = { | |
new PBEKeySpec(password, salt, iterationCount, KeyLength) | |
.pipe { mkSecretKeyFactory.generateSecret } | |
.getEncoded | |
.pipe { mkSecretKeySpec } | |
} | |
def encrypt(password: Array[Char], message: Array[Byte], saltSize: Int = DefaultSaltSize): Array[Byte] = { | |
val salt = new Array[Byte](saltSize).tap { mkSecureRandom.nextBytes } | |
val (iv, encrypted) = | |
mkCipher | |
.tap { | |
_.init( | |
Cipher.ENCRYPT_MODE, | |
createKey(password, salt)) | |
} | |
.pipe { cipher => | |
(cipher.getIV, cipher.doFinal(message)) | |
} | |
ByteBuffer | |
.allocate(Integer.BYTES + salt.length + Integer.BYTES + iv.length + encrypted.length) | |
.putInt(salt.length).put(salt) | |
.putInt(iv.length).put(iv) | |
.put(encrypted) | |
.array() | |
} | |
def decrypt(password: Array[Char], message: Array[Byte]): Array[Byte] = { | |
val (salt, iv, encrypted) = | |
ByteBuffer.wrap(message).pipe { buffer => | |
( | |
new Array[Byte](buffer.getInt()).tap { buffer.get }, | |
new Array[Byte](buffer.getInt()).tap { buffer.get }, | |
new Array[Byte](buffer.remaining()).tap { buffer.get } | |
) | |
} | |
mkCipher | |
.tap { | |
_.init( | |
Cipher.DECRYPT_MODE, | |
createKey(password, salt), | |
new IvParameterSpec(iv) | |
) | |
} | |
.doFinal(encrypted) | |
} | |
} | |
object SPBCryptor { | |
final val DefaultIterationCount = 1000 | |
final val DefaultSaltSize = 16 | |
private final val KeyLength = 256 | |
private def mkSecureRandom = SecureRandom.getInstanceStrong | |
private def mkSecretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256") | |
private def mkSecretKeySpec(keyBytes: Array[Byte]) = new SecretKeySpec(keyBytes, "AES") | |
private def mkCipher = Cipher.getInstance("AES/CTR/NoPadding") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment