4
votes

Comment crypter et décrypter facilement une chaîne à l'aide de Tink?

Jusqu'à présent, j'utilisais jasypt pour crypter une chaîne avant de la stocker sur le disque lors de la fermeture de l'application, et plus tard lors de l'ouverture de l'application pour décrypter la chaîne après l'avoir récupérée à partir du disque.

C'était super facile avec jasypt, ceci était le 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);
}

Cela fonctionnait parfaitement, mais maintenant, jasypt est obsolète et j'essaye de migrer vers la bibliothèque Google Tink , le Le problème est que Google Tink semble être beaucoup plus complexe pour chiffrer et déchiffrer une chaîne aussi facilement qu'avec jasypt.

Je ne trouve pas dans le fichier readme du repo Tink le moyen simple de chiffrer et de déchiffrer une chaîne , je peux juste trouver des opérations plus complexes que je ne peux pas comprendre parce que mes connaissances en cryptage sont totalement vides. Pour cette raison, j'utilisais une bibliothèque très simple comme jasypt.

Voici le dépôt Tink: https://github.com/Google/tink

Existe-t-il un moyen simple, similaire à mon code jasypt, de crypter et décrypter une chaîne avec Tink?

p >


3 commentaires

En raison du concept de gestion des clés de Tink, la génération de clés est relativement restrictive, par ex. ce n'est pas si facile de générer une clé à partir d'une séquence d'octets. De plus, le cryptage basé sur un mot de passe n'est pas pris en charge (à 08/2018). Cela est traité en détail ici . Ainsi, je ne suis pas sûr que Tink corresponde à vos idées concernant la simplicité. En dehors de cela, encr./decr. avec Tink est simple, voir par ex. ici , chapitre "Chiffrement par clé symétrique".


@Topaco c'est encore trop complexe, peut-être pouvez-vous poster une réponse avec un échantillon sur la façon d'y parvenir? Vous semblez être une experte à Tink. Je vous remercie


Je ne suis vraiment pas un expert de Tink et je l'ai testé plutôt superficiellement. C'est pourquoi je n'ai certainement pas un aperçu complet des fonctionnalités. Dans ma réponse, j'ai résumé certains aspects qui peuvent être pertinents pour votre question.


3 Réponses :


9
votes

Le StrongTextEncryptor -class dans votre jasypt -example-code utilise l'algorithme PBEWithMD5AndTripleDES . Cet algorithme utilise le chiffrement par blocs à clé symétrique Triple DES et dérive la clé du mot de passe en utilisant la fonction de hachage MD5 . Ce dernier est appelé cryptage par mot de passe et ce n'est pas pris en charge dans Tink (au moins à 08/2018), voir Comment créer une clé de chiffrement symétrique avec Google Tink? . Ainsi, il est impossible dans Tink de crypter au moyen d'un mot de passe et le concept utilisé jusqu'à présent dans le code jasypt n'a pas pu être implémenté. Si le cryptage basé sur un mot de passe doit être utilisé dans tous les cas, c'est un facteur décisif pour Tink .

Une autre approche consiste à utiliser directement une clé. Tink a le AesGcmJce -classe qui utilise AES-GCM pour le chiffrement. Ici, la clé doit avoir une longueur de 128 ou 256 bits:

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());

L'utilisation est simple et de plus le chiffrement utilisé ( AES-GCM ) est sécurisé. Cependant, les développeurs de Tink eux-mêmes ne recommandent pas cette approche car la classe AesGcmJce appartient à la classe com.google.crypto .tink.subtle -package qui peut changer à à tout moment sans autre préavis , (voir aussi ici , section Avertissements importants ). Par conséquent, cette approche n'est pas non plus optimale.

Eh bien, comment Tink utilise-t-il généralement le cryptage symétrique? Ceci est illustré dans l'extrait suivant de :

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());

La méthode generateNew génère une nouvelle clé. Cependant, la création n'est pas basée sur un mot de passe ou une séquence d'octets et à cause de cela, une clé générée pour le chiffrement ne peut pas être facilement reconstruite pour le déchiffrement. Par conséquent, la clé utilisée pour le chiffrement doit être conservée dans un système de stockage, par ex. le système de fichiers, afin qu'il puisse être utilisé plus tard pour le décryptage. Tink permet le stockage de clés en clair (ce qui n'est bien sûr pas recommandé). Une approche plus sécurisée est le chiffrement des clés avec des clés principales stockées dans un système de gestion de clés à distance (ceci est expliqué plus en détail par Tink JAVA-HOWTO , sections Stockage des clés et Chargement des clés existantes ).

Le concept de gestion des clés de Tink (dans le but d'éviter les fuites accidentelles de matériel clé sensible) le rend également quelque peu encombrant (cela peut changer ultérieurement versions). C'est pourquoi j'ai dit dans mon commentaire que je ne suis pas sûr si Tink correspond à vos idées concernant la simplicité.


0 commentaires

3
votes

Clause de non-responsabilité: je suis le développeur principal de Tink.

Si vous travaillez sur une application Android, vous pouvez consulter AndroidKeysetManager . Il y a un exemple Hello world à partir duquel vous pouvez copier.

En général, chaque fois que vous voulez crypter quelque chose, la première question que vous devez vous poser est de savoir où vous allez stocker les clés. Cela ne fait pas beaucoup de sens de stocker les clés au même endroit (et avec la même ACL) où vous stockez vos données cryptées. Les clés doivent être stockées dans un emplacement différent (ou avec une ACL différente).

L'API de gestion des clés de Tink est un peu plus complexe car nous voulons guider les utilisateurs pour stocker les clés au bon endroit .


1 commentaires

s'il vous plaît, voulez-vous ajouter un moyen simple de crypter et de décrypter des chaînes pour des utilisations simples comme jasypt? Parfois, le cryptage est nécessaire pour des données pas trop sensibles, juste pour s'adapter à un petit et pas complexe requis, et dans ce cas, votre bibliothèque est trop complexe



0
votes

Je cherchais un moyen simple de crypter un court message texte avec un cryptage par mot de passe (PBE) et d'utiliser Google Tink pour la partie cryptographique et j'ai constaté que Tink ne fournissait pas le PBE de manière native. Pour résoudre ce problème, j'ai créé une classe simple qui fait tout le travail avec PBE, la gestion des clés et le cryptage / décryptage.

L'utilisation dans un programme est très simple et vous n'avez besoin que de 4 lignes de code pour l'utiliser:

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;
    }
}

Sur mon Github, vous trouvez deux exemples de programmes pour montrer comment implémenter la classe (avec et sans GUI): https://github.com/java-crypto/H-Google-Tink/tree/master/H%20Tink % 20Textencryption% 20PBE

Voici le code source de la classe TinkPbe.java:

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

Veuillez garder à l'esprit que l'utilisation d'un texte en clair - Chaîne signifie que votre texte brut est inmuable et indélébile dans votre tas jusqu'à ce que le garbage collector les détruit.

Une description plus détaillée est disponible sur mon site Web: http://javacrypto.bplaced.net/h-ti nk-string-encryption-using-pbe-and-gui /


0 commentaires