tink icon indicating copy to clipboard operation
tink copied to clipboard

Please consider adding Password-based encryption!

Open evalsinca opened this issue 7 years ago • 9 comments

In our use case, we want to explicitly set the bytes of a symmetric key to a specific set of bytes (we have come up with two kludgy ways of accomplishing this with the existing library) but having a first-class password based encryption will be useful. In our particular use case, passwords will be generated by the system (not the user) so that the traditional fears of passwords being "weak", etc. do not apply (if that helps).

evalsinca avatar Sep 06 '18 08:09 evalsinca

Would you mind sharing the workaround you found in the meantime ?

jeantil avatar Sep 11 '18 15:09 jeantil

Per @thaidn you can also use the subtle api directly (the AesGcmJce class) - see his response to the thread I had first created.

https://stackoverflow.com/questions/52171198/how-to-create-symmetric-encryption-key-with-google-tink

evalsinca avatar Sep 11 '18 16:09 evalsinca

Closed by accident... :-)

evalsinca avatar Sep 11 '18 16:09 evalsinca

@evalsinca I imagine that you're alluding to writing code that uses AesGcmJce along those lines.

avernet avatar Feb 25 '19 19:02 avernet

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

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/

java-crypto avatar Nov 21 '19 10:11 java-crypto

Per @thaidn you can also use the subtle api directly (the AesGcmJce class) - see his response to the thread I had first created.

https://stackoverflow.com/questions/52171198/how-to-create-symmetric-encryption-key-with-google-tink

@evalsinca Just letting you know that the link you pasted (above) that is supposed to go to stackoverflow actually goes to github. I'm going to provide the link with what I think your intention was:

=> https://stackoverflow.com/questions/52171198/how-to-create-symmetric-encryption-key-with-google-tink

vahidpaz avatar Mar 18 '20 18:03 vahidpaz

Today I have encountered the discussion on stackoverflow and the suggestion to use AesGcm directly...

Can anyone (who is more familiar with cryptography and tink than me :) ) please, have a look as to whether this C++ implementation of the suggestion makes sense?

DimanNe avatar Apr 05 '20 20:04 DimanNe

in the code above by java-crypto new PBEKeySpec(...) it takes bits not bytes as input... though I guess 256 bit, not byte what he wanted... and 1 byte = 8 bits (128 byte != 512 bit)

i am not sure you should publish crypto code if you are not precise...

r-jo avatar Jan 03 '22 18:01 r-jo

+1

Please add this feature in the near future. It would be a great improvement for Tink.

BigPanda97 avatar Apr 30 '22 21:04 BigPanda97

We currently do not have plans to design or implement password based encryption.

tholenst avatar Jan 26 '23 15:01 tholenst

(Closed)

tholenst avatar Jan 26 '23 15:01 tholenst