Commons Security is a Java library with several encryption features for Java 8
or higher build on top of JCA/JCE
with BouncyCastle
as provider. The library hides the implementation behind a simple and easy to use API. Typically a simplified API requires that some decisions has been made already, like which algorithm to use, encryption strength, etc. However do not worry, look behind the simplified API and you will find more configuration options.
-
Asymmetric encryption and decryption with public key and private key.
RSA
is supported. -
Symmetric encryption and decryption with one (same) key.
AES/CTR
andAES/GCM
is supported. -
Shamir’s Secret Sharing is a way to split a secret into shares. The secret can be recreated by putting the minimum required amount of shares back together.
-
Password Encoder uses proven algorithms for hashing passwords in a safe and secure way.
-
Digest or message digest is the result of hashing and is a way to integrity check our data. A typical use case could be to compare content (hashed) with a stored hash to verify if the data has been altered.
-
Digital Signature and verification using RSA keypair. User sign data with their private key and others can verify the signature with originators public key.
You may use the simple delegate class CryptoUtils
or you can access classes like RsaCipher
, AesCipher
, Shamir
, etc. directly. The latter might give you more options. Implementation alternatives are shown in examples below.
This is some common classes used when working with Symmetric and Asymmetric encryption but also Password Encoding.
-
PlainText
is a placeholder for unencrypted text.
PlainText plainText = new PlainText("My secret plaintext");
-
CipherText
is a placeholder for encrypted text.
CipherText cipherText = new CipherText("ymEIVhbBPhWAIzDx7MalbeLoccwnw=");
-
Password
is a placeholder for a string (the password) used for either encryption or decryption.
Password password = new Password("SecretPassword123!");
-
PublicKey
holds the public key derived fromjava.security.KeyPair
and can be used for encryption or signature verification.
PublicKey publicKey = keyPair.getPublicKey();
-
PrivateKey
holds the private key derived fromjava.security.KeyPair
and can be used for decryption or for creating a signature.
PrivateKey privateKey = keyPair.getPrivateKey();
RSA[1] (Rivest–Shamir–Adleman) is a public-key cryptosystem that is widely used for secure data transmission. In a public-key cryptosystem, the encryption key is public and distinct from the decryption key, which is kept secret (private).
It involves the use of two mathematically related keys. The public key (the one that’s known to everybody) and the private key (which is only known by you) are required for encrypting and decrypting the message. The public key can be derived from the private key but not the other way.
Asymmetric encryption use a public key for encryption and a private key for decryption. Useful when you want to share secrets with others. You may encrypt a file with someone’s public key, so they will be able to decrypt with their matching private key.
🔥
|
Used to share small files or text with other users. For large content (more than 117 bytes) use AES symmetric encryption. The AES secret key can later be shared by using Public Key Encryption like RSA encryption or Deffie-Hellman[2]. |
RSA requires a private key and a matching public key. Supported key sizes (encryption strength) are 1024
, 2048
, 3072
and 4096
.
âť—
|
Key size 3072 or higher is recommended for better security.
|
🔥
|
Any secret encrypted with a public key can only be decrypted with the associated private key. |
KeyUtils
to generate a 4096
bit KeyPair
KeyPair keyPair = KeyUtils.generateKeyPair4096();
RsaCipher rsaCipher = new RsaCipher();
KeyPair keyPair = rsaCipher.generateKeyPair(KeySize.BIT_4096);
CryptoUtils
KeyPair keyPair = CryptoUtils.generateKeyPair4096();
CipherText cipherText = CryptoUtils.rsaEncrypt("My secret", keyPair.getPublicKey());
PlainText plainText = CryptoUtils.rsaDecrypt(cipherText, keyPair.getPrivateKey());
RsaCipher rsaCipher = new RsaCipher();
KeyPair keyPair = rsaCipher.generateKeyPair(KeySize.BIT_4096);
CipherText cipherText = rsaCipher.encrypt("My secret", keyPair.getPublicKey());
PlainText plainText = rsaCipher.decrypt(cipherText, keyPair.getPrivateKey());
The Advanced Encryption Standard (AES)[3], also known by its original name Rijndael is a specification for the encryption of electronic data established by the U.S. National Institute of Standards and Technology (NIST) in 2001.
There’s a single shared key that’s used for encryption and decryption.
🔥
|
Useful for encryption larger files compared to asymmetric encryption. A symmetric key can be shared (distributed) by using RSA public key encryption, Deffie-Hellman or even Sharmir’s Secret Sharing if needed. |
Modes supported: GCM
, CTR
Strength supported: 128
, 192
, 256
CryptoUtils
with GCM
@256
-bitPassword password = new Password("SecretPassword123!");
CiperText ciperText = CryptoUtils.aesEncrypt("Secret text", password);
PlainText plainText = CryptoUtils.aesDecrypt(cipherText, password);
CTR
@192
-bit)Password password = new Password("SecretPassword123!");
AesCipher aesCipher = new AesCipher(EncryptionMode.CTR, EncryptionStrength.BIT_192);
CiperText ciperText = aesCipher.encrypt("Secret text", password);
PlainText plainText = aesCipher.decrypt(cipherText, password);
Shamir’s Secret Sharing[4] is used to share a secret in a distributed way, most often to secure other encryption keys. These shares are used to reconstruct the original secret.
To unlock the secret via Shamir’s secret sharing, a minimum number of shares are needed. This is called the threshold, and is used to denote the minimum number of shares needed to unlock the secret.
CryptoUtils
int totalShares = 5;
int minShares = 2; // threshold
Shares shares = CryptoUtils.getShamirShares("Secret text", totalShares, minShares);
Secret secret = CryptoUtils.getShamirSecret(shares); // where shares >= minShares
int totalShares = 5;
int minShares = 2; // threshold
Shares shares = Shamir.getShares("Secret text", totalShares, minShares);
Secret secret = Shamir.getSecret(shares); // where shares >= minShares
A bank have a vault full of money. The bank’s policy requires that nobody should be able to open the vault alone. Five employees are selected to have access to the vault and there must be at least two (2) employees at any time when opening the vault.
-
Split phase: Five (5) keys are being distributed.
Bob
,Alice
,Eve
,Tom
andLisa
all get one share each using Shamir’s Secret Sharing to split the secret into five shares. -
Join phase: It’s time to open the safe. The requirement is two (2) shares to open the vault.
Bob
andAlice
bring their shares. By using Shamir’s Secret Sharing the shares from both will be joined and the secret recreated.
Digest or message digest is the result of hashing[7] data or content. The hashing is a one-way compression function to convert inputs of different lengths into a fixed-length output (hash value). SHA-2
@SHA-256
is the default used by this library. Up to SHA-2
@SHA-512
is possible with JDK 8
. Stronger and newer hashing like SHA3
is not supported out of the box with JDK 8
. That would require JDK 9
or higher. However BouncyCastle[8] has been added as main provider and SHA-3
is therefore available.
âť—
|
Do not use Message Digest for password storage. For that you should use KDF[9] algorithms like ARGON2 , BCRYPT , SCRYPT or PBKDF2 . Se Password Encoder below.
|
The following hashing algorithms are not recommended or included: MD4
, MD5
, SHA-0
and SHA-1
link:src/main/java/io/github/avec112/security/crypto/digest/DigestAlgorithm.java[role=include]
final byte[] digest = DigestUtils.digest(data);
final String digest = DigestUtils.base64Digest(data);
final String digest = DigestUtils.hexDigest(data);
SHA3-384
)final byte[] digest = DigestUtils.digest(data, DigestAlgorithm.SHA3_384);
Key Derivation Functions (KDF)[9] from a password must be able to stand attacks like brute-forcing, dictionary attacks, rainbow attacks and more. Attempts to reverse hashed password values is common.
ARGON2
, BCRYPT
, SCRYPT
and PBKDF2
are common algorithms used for password hashing since they are much more robust when attacked.
🔥
|
ARGON2 is recommended as the most secure hash algorithm for passwords and is the default implementation for this API.
|
A plaintext password should always be encoded in case of a breach. After a password is encoded it may be stored for future matching.
link:src/main/java/io/github/avec112/security/crypto/password/PasswordEncoderType.java[role=include]
ARGON2
)final String encodedPassword = PasswordEncoderUtils.encode(password);
enum PasswordEncoderType
final String encodedPassword = PasswordEncoderUtils.encode(password, PasswordEncoderType.BCRYPT);
When a user is authenticated they must input their password. This plaintext password will be matched against the stored encoded password.
ARGON2
)final boolean isMatching = PasswordEncoderUtils.matches(rawPassword, encodedPassword);
enum PasswordEncoderType
final boolean isMatching = PasswordEncoderUtils.matches(rawPassword, encodedPassword, PasswordEncoderType.BCRYPT);
A digital signature[12] is a mathematical scheme for verifying the authenticity of digital messages or documents. A valid digital signature, where the prerequisites are satisfied, gives a recipient very strong reason to believe that the message was created by a known sender (authentication), and that the message was not altered in transit.
More to come
-
Make API fluently as much as possible
-
Replacing MultipleMissingArgumentsError with own class without dependency to opentest4j and only throwing/supporting RuntimeException
-
Support more algorithms for hashing digests[10] including password hashing
-
Implement support for ECC as option over RSA[15]. ECC is faster and stronger than RSA.
-
Look into Deffie-Hellman and/or other better key exchange alternatives
-
Consider adding support for generating and validation passwords with help of Passay[16] library
-
Consider making this project a library on Maven Central[17]