Skip to content

Java library with encryption features like RSA, AES, Shamir's Secret Sharing, and more.

License

Notifications You must be signed in to change notification settings

Avec112/commons-security

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

38 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Commons Security: Encryption done easy

Overview

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.

Features
  • Asymmetric encryption and decryption with public key and private key. RSA is supported.

  • Symmetric encryption and decryption with one (same) key. AES/CTR and AES/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.

Some common classes

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 from java.security.KeyPair and can be used for encryption or signature verification.
    PublicKey publicKey = keyPair.getPublicKey();

  • PrivateKey holds the private key derived from java.security.KeyPair and can be used for decryption or for creating a signature.
    PrivateKey privateKey = keyPair.getPrivateKey();

RSA asymmetric encryption

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 KeyPair

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.
Using KeyUtils to generate a 4096 bit KeyPair
KeyPair keyPair = KeyUtils.generateKeyPair4096();
Alternative
RsaCipher rsaCipher = new RsaCipher();
KeyPair keyPair = rsaCipher.generateKeyPair(KeySize.BIT_4096);

RSA Encryption and Decryption

RSA encryption and decryption using CryptoUtils
KeyPair keyPair = CryptoUtils.generateKeyPair4096();
CipherText cipherText = CryptoUtils.rsaEncrypt("My secret", keyPair.getPublicKey());
PlainText plainText = CryptoUtils.rsaDecrypt(cipherText, keyPair.getPrivateKey());
Alternative
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());

AES symmetric encryption

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

AES Encryption and Decryption

AES encryption and decryption using CryptoUtils with GCM@256-bit
Password password = new Password("SecretPassword123!");
CiperText ciperText = CryptoUtils.aesEncrypt("Secret text", password);
PlainText plainText = CryptoUtils.aesDecrypt(cipherText, password);
Alternative (here as 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

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.

This implementation is based on the work of Coda Hale[5] and his project Shamir[6].

Shamir’s Secret Sharing using CryptoUtils
int totalShares = 5;
int minShares = 2; // threshold

Shares shares = CryptoUtils.getShamirShares("Secret text", totalShares, minShares);
Secret secret = CryptoUtils.getShamirSecret(shares); // where shares >= minShares
Alternative
int totalShares = 5;
int minShares = 2; // threshold

Shares shares = Shamir.getShares("Secret text", totalShares, minShares);
Secret secret = Shamir.getSecret(shares); // where shares >= minShares

Shamir example

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 and Lisa 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 and Alice bring their shares. By using Shamir’s Secret Sharing the shares from both will be joined and the secret recreated.

Digest (hashing)

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.
Non supported hashing algorithms

The following hashing algorithms are not recommended or included: MD4, MD5, SHA-0 and SHA-1

Supported hashing algorithms
link:src/main/java/io/github/avec112/security/crypto/digest/DigestAlgorithm.java[role=include]
Worth reading
  • Read more about Secure Hash Algorithms[10]

  • Nice article about hashing security[11]

Example

Raw bytes
final byte[] digest = DigestUtils.digest(data);
Base64 encoded
final String digest = DigestUtils.base64Digest(data);
Hex encoded
final String digest = DigestUtils.hexDigest(data);
Other hashing algorithms (example SHA3-384)
final byte[] digest = DigestUtils.digest(data, DigestAlgorithm.SHA3_384);

Password Encoder

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.

Encoding

A plaintext password should always be encoded in case of a breach. After a password is encoded it may be stored for future matching.

Supported password encoders
link:src/main/java/io/github/avec112/security/crypto/password/PasswordEncoderType.java[role=include]
Password encode (default ARGON2)
final String encodedPassword = PasswordEncoderUtils.encode(password);
Alternative encoding with use of enum PasswordEncoderType
final String encodedPassword = PasswordEncoderUtils.encode(password, PasswordEncoderType.BCRYPT);

Matching

When a user is authenticated they must input their password. This plaintext password will be matched against the stored encoded password.

Password matching (default ARGON2)
final boolean isMatching = PasswordEncoderUtils.matches(rawPassword, encodedPassword);
Alternative matching with use of enum PasswordEncoderType
final boolean isMatching = PasswordEncoderUtils.matches(rawPassword, encodedPassword, PasswordEncoderType.BCRYPT);

Signature and verification

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

  • Practical Cryptography for Developers[13] by Svetlin Nakov

  • Password1 - Why we moved to 256-bit AES keys[14] by Jeffrey Goldberg in 2013

TODO

  • 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]

About

Java library with encryption features like RSA, AES, Shamir's Secret Sharing, and more.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages