Reputation: 37657
Until now, I was using jasypt to encrypt a string before storing it on disk on app closing, and later when opening the app for decrypt the string after retrieving it from disk.
It was super easy with jasypt, this was the code:
private static final String JASYPT_PWD = "mypassword";
public static String encryptString(String string) {
StrongTextEncryptor textEncryptor = new StrongTextEncryptor();
textEncryptor.setPassword(JASYPT_PWD);
return textEncryptor.encrypt(string);
}
public static String decryptString(String string) {
StrongTextEncryptor textEncryptor = new StrongTextEncryptor();
textEncryptor.setPassword(JASYPT_PWD);
return textEncryptor.decrypt(string);
}
It worked perfectly, but now, jasypt is deprecated and I'm trying to migrate to the Google Tink library, the problem is that Google Tink seems to be much more complex for just encrypt and decrypt a string as easily as with jasypt.
I can't find in the Tink repo readme the simple way to encrypt and decrypt a string, just can find more complex operations which in fact I can't understand because my knowledge in encryption is totally empty. Because of that I was using a very easy library like jasypt.
This is the Tink repo: https://github.com/Google/tink
Is there an easy way, similar to my jasypt code, to encrypt and decrypt a string with Tink?
Upvotes: 9
Views: 12886
Reputation: 49251
Note: The post refers to Tink version 1.2.2. The posted code is partially incompatible with later versions.
The StrongTextEncryptor
-class in your jasypt
-example-code uses the PBEWithMD5AndTripleDES
-algorithm. This algorithm uses the symmetric-key block cipher Triple DES
and derives the key from the password using the MD5
hash function. The latter is called password-based encryption and this isn't supported in Tink
(at least as at 08/2018), see How to create symmetric encryption key with Google Tink?. Thus, it's impossible in Tink
to encrypt by means of a password and the concept used so far in the jasypt
-code couldn't be implemented. If password-based encryption is to be used in any case that is a deal-breaker for Tink
.
Another approach is to directly use a key. Tink
has the AesGcmJce
-class which uses AES-GCM
for encryption. Here the key must have a length of either 128 Bit or 256 bit:
String plainText = "This is a plain text which needs to be encrypted!";
String aad = "These are additional authenticated data (optional)";
String key = "ThisIsThe32ByteKeyForEncryption!"; // 256 bit
// Encryption
AesGcmJce agjEncryption = new AesGcmJce(key.getBytes());
byte[] encrypted = agjEncryption.encrypt(plainText.getBytes(), aad.getBytes());
// Decryption
AesGcmJce agjDecryption = new AesGcmJce(key.getBytes());
byte[] decrypted = agjDecryption.decrypt(encrypted, aad.getBytes());
The use is simple and furthermore the used cipher (AES-GCM
) is secure. However, the Tink
-developers themselves don't recommend this approach because the AesGcmJce
-class belongs to the com.google.crypto.tink.subtle
-package which may change at any time without further notice, (see also here, section Important Warnings). Therefore, also this approach isn't optimal.
Well, how does Tink
typically use symmetric encryption? This is shown in the following snippet from:
String plainText = "This is a plain text which needs to be encrypted!";
String aad = "These are additional authenticated data (optional)";
AeadConfig.register();
KeysetHandle keysetHandle = KeysetHandle.generateNew(AeadKeyTemplates.AES256_GCM);
Aead aead = AeadFactory.getPrimitive(keysetHandle);
// Encryption
byte[] ciphertext = aead.encrypt(plainText.getBytes(), aad.getBytes());
// Decryption
byte[] decrypted = aead.decrypt(ciphertext, aad.getBytes());
The generateNew
-method generates a new key. However, the creation isn't based on a password or a byte-sequence and because of this, a key generated for the encryption can't be easily reconstructed for decryption. Therefore, the key used for encryption has to be persisted to a storage system, e.g. the file system, so it can be used later for decryption. Tink
allows the storing of cleartext keys (which is of course not recommended). A more secure approach is the encryption of keys with master keys stored in a remote key management system (this is in more detail explained Tink
's JAVA-HOWTO, sections Storing Keysets and Loading Existing Keysets).
Tink
's key management concept (with the aim of avoiding accidental leakage of sensitive key material) makes it also somehow cumbersome (this may change in later versions). That's why I said in my comment that I'm not sure if Tink
fits your ideas concerning simplicity.
Upvotes: 16
Reputation: 199
Disclaimer: I'm Tink's lead developer.
If you're working on an Android app, you can check out AndroidKeysetManager
. There's a hello world example that you can copy from.
In general whenever you want to encrypt something, the first question you should ask yourself is where you're going to store keys. It doesn't make a lot of senses to store keys in the same place (and with the same ACL) where you store your encrypted data. Keys should be stored in a different location (or with different ACL).
Tink's Key Management API is a bit more complex because we want to steer users to storing keys in the right location.
Upvotes: 10
Reputation: 6414
I was looking for a simple way to encrypt a short text message with a Password Based Encryption (PBE) and use Google Tink for the cryptographic part and found that Tink doesn't provide PBE natively. To solve this problem I made a simple class that does all the work with PBE, keyhandling and encryption/decryption.
The usage in a program is very simple and you need only 4 lines of code to use it:
AeadConfig.register(); // tink initialisation
TinkPbe tpbe = new TinkPbe(); // tink pbe initialisation
String ciphertextString = tpbe.encrypt(passwordChar, plaintextString); // encryption
String decryptedtextString = tpbe.decrypt(passwordChar, ciphertextString); // decryption
On my Github you find two sample programms to show how to implement the class (with and without GUI): https://github.com/java-crypto/H-Google-Tink/tree/master/H%20Tink%20Textencryption%20PBE
Here is the sourcecode of the TinkPbe.java-class:
package tinkPbe;
/*
*
* Diese Klasse gehört zu diesen beiden Hauptklassen
* This class belongs to these main classes:
* TinkPbeConsole.java | TinkPbeGui.java
*
* Herkunft/Origin: http://javacrypto.bplaced.net/
* Programmierer/Programmer: Michael Fehr
* Copyright/Copyright: frei verwendbares Programm (Public Domain)
* Copyright: This is free and unencumbered software released into the public domain.
* Lizenttext/Licence: <http://unlicense.org>
* getestet mit/tested with: Java Runtime Environment 8 Update 191 x64
* getestet mit/tested with: Java Runtime Environment 11.0.1 x64
* Datum/Date (dd.mm.jjjj): 20.11.2019
* Funktion: verschlüsselt und entschlüsselt einen Text mittels Google Tink
* im Modus AES GCM 256 Bit. Der Schlüssel wird mittels PBE
* (Password based encryption) erzeugt.
* Function: encrypts and decrypts a text message with Google Tink.
* Used Mode is AES GCM 256 Bit. The key is generated with PBE
* (Password based encryption).
*
* Sicherheitshinweis/Security notice
* Die Programmroutinen dienen nur der Darstellung und haben keinen Anspruch auf eine korrekte Funktion,
* insbesondere mit Blick auf die Sicherheit !
* Prüfen Sie die Sicherheit bevor das Programm in der echten Welt eingesetzt wird.
* The program routines just show the function but please be aware of the security part -
* check yourself before using in the real world !
*
* Das Programm benötigt die nachfolgenden Bibliotheken (siehe Github Archiv):
* The programm uses these external libraries (see Github Archive):
* jar-Datei/-File: tink-1.2.2.jar
* https://mvnrepository.com/artifact/com.google.crypto.tink/tink/1.2.2
* jar-Datei/-File: protobuf-java-3.10.0.jar
* https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java/3.10.0
* jar-Datei/-File: json-20190722.jar
* https://mvnrepository.com/artifact/org.json/json/20190722
*
*/
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import com.google.crypto.tink.Aead;
import com.google.crypto.tink.CleartextKeysetHandle;
import com.google.crypto.tink.JsonKeysetReader;
import com.google.crypto.tink.KeysetHandle;
import com.google.crypto.tink.aead.AeadFactory;
public class TinkPbe {
public static String encrypt(char[] passwordChar, String plaintextString)
throws GeneralSecurityException, IOException {
byte[] keyByte = pbkdf2(passwordChar);
String valueString = buildValue(keyByte);
String jsonKeyString = writeJson(valueString);
KeysetHandle keysetHandleOwn = CleartextKeysetHandle.read(JsonKeysetReader.withString(jsonKeyString));
// initialisierung
Aead aead = AeadFactory.getPrimitive(keysetHandleOwn);
// verschlüsselung
byte[] ciphertextByte = aead.encrypt(plaintextString.getBytes("utf-8"), null); // no aad-data
return Base64.getEncoder().encodeToString(ciphertextByte);
}
public static String decrypt(char[] passwordChar, String ciphertextString)
throws GeneralSecurityException, IOException {
byte[] keyByte = pbkdf2(passwordChar);
String valueString = buildValue(keyByte);
String jsonKeyString = writeJson(valueString);
KeysetHandle keysetHandleOwn = CleartextKeysetHandle.read(JsonKeysetReader.withString(jsonKeyString));
// initialisierung
Aead aead = AeadFactory.getPrimitive(keysetHandleOwn);
// verschlüsselung
byte[] plaintextByte = aead.decrypt(Base64.getDecoder().decode(ciphertextString), null); // no aad-data
return new String(plaintextByte, StandardCharsets.UTF_8);
}
private static byte[] pbkdf2(char[] passwordChar)
throws NoSuchAlgorithmException, InvalidKeySpecException, UnsupportedEncodingException {
final byte[] passwordSaltByte = "11223344556677881122334455667788".getBytes("UTF-8");
final int PBKDF2_ITERATIONS = 10000; // anzahl der iterationen, höher = besser = langsamer
final int SALT_SIZE_BYTE = 256; // grösse des salts, sollte so groß wie der hash sein
final int HASH_SIZE_BYTE = 256; // größe das hashes bzw. gehashten passwortes, 128 byte = 512 bit
byte[] passwordHashByte = new byte[HASH_SIZE_BYTE]; // das array nimmt das gehashte passwort auf
PBEKeySpec spec = new PBEKeySpec(passwordChar, passwordSaltByte, PBKDF2_ITERATIONS, HASH_SIZE_BYTE);
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
passwordHashByte = skf.generateSecret(spec).getEncoded();
return passwordHashByte;
}
private static String buildValue(byte[] gcmKeyByte) {
// test for correct key length
if ((gcmKeyByte.length != 16) && (gcmKeyByte.length != 32)) {
throw new NumberFormatException("key is not 16 or 32 bytes long");
}
// header byte depends on keylength
byte[] headerByte = new byte[2]; // {26, 16 }; // 1A 10 for 128 bit, 1A 20 for 256 Bit
if (gcmKeyByte.length == 16) {
headerByte = new byte[] { 26, 16 };
} else {
headerByte = new byte[] { 26, 32 };
}
byte[] keyByte = new byte[headerByte.length + gcmKeyByte.length];
System.arraycopy(headerByte, 0, keyByte, 0, headerByte.length);
System.arraycopy(gcmKeyByte, 0, keyByte, headerByte.length, gcmKeyByte.length);
String keyBase64 = Base64.getEncoder().encodeToString(keyByte);
return keyBase64;
}
private static String writeJson(String value) {
int keyId = 1234567; // fix
String str = "{\n";
str = str + " \"primaryKeyId\": " + keyId + ",\n";
str = str + " \"key\": [{\n";
str = str + " \"keyData\": {\n";
str = str + " \"typeUrl\": \"type.googleapis.com/google.crypto.tink.AesGcmKey\",\n";
str = str + " \"keyMaterialType\": \"SYMMETRIC\",\n";
str = str + " \"value\": \"" + value + "\"\n";
str = str + " },\n";
str = str + " \"outputPrefixType\": \"TINK\",\n";
str = str + " \"keyId\": " + keyId + ",\n";
str = str + " \"status\": \"ENABLED\"\n";
str = str + " }]\n";
str = str + "}";
return str;
}
}
Please keep in mind that using a plaintext-String means that your plaintext is inmutable and undeletable in your heap until the Garbage Collector destroys them.
A more detailed description is vailable on my website: http://javacrypto.bplaced.net/h-tink-string-encryption-using-pbe-and-gui/
Upvotes: 4