首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用Tink轻松地加密和解密字符串?

如何使用Tink轻松地加密和解密字符串?
EN

Stack Overflow用户
提问于 2019-03-08 20:24:53
回答 3查看 9.8K关注 0票数 8

到目前为止,我一直在使用jasypt加密字符串,然后在应用程序关闭时将其存储在磁盘上,然后在打开应用程序以便从磁盘检索字符串后解密该字符串。

使用jasypt非常容易,这是代码:

代码语言:javascript
复制
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);
}

它工作得很好,但是现在,jasypt已经被废弃了,我正在尝试迁移到Google Tink库,问题是Google Tink似乎要复杂得多,只需加密和解密字符串就像使用jasypt一样容易。

我在Tink repo自述中找不到加密和解密字符串的简单方法,只能找到更复杂的操作,实际上我无法理解这些操作,因为我的加密知识是完全空白的。正因为如此,我使用了一个非常简单的库,比如jasypt。

这是叮当回购:https://github.com/Google/tink

是否有一种与我的jasypt代码类似的简单方法,可以用Tink加密和解密字符串?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-03-10 08:56:28

注:这篇文章指的是 Tink版本1.2.2。发布的代码与较晚的版本部分不兼容。

StrongTextEncryptor-class在您的jasypt示例代码中使用PBEWithMD5AndTripleDES-algorithm。该算法使用对称密钥分组密码Triple DES,并使用MD5哈希函数从密码中导出密钥。后者称为基于密码的加密,这在Tink中是不支持的(至少在08/2018年),参见如何使用Google创建对称加密密钥?。因此,在Tink中不可能使用密码进行加密,而且jasypt-code中迄今使用的概念无法实现。如果在任何情况下都要使用基于密码的加密,那将是Tink的一个破折号。

另一种方法是直接使用密钥。TinkAesGcmJce-class,它使用AES-GCM进行加密。在这里,键必须有128位或256位的长度:

代码语言:javascript
复制
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());

使用简单,并且使用的密码(AES-GCM)是安全的。但是,Tink-developers本身并不推荐这种方法,因为AesGcmJce-class属于com.google.crypto.tink.subtle-package 如有任何更改,恕不另行通知。 (也请参阅这里,重要警告部分)。因此,这种方法也不是最优的。

那么,Tink通常如何使用对称加密呢?下面的代码片段从…显示了这一点

代码语言:javascript
复制
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());

generateNew-method生成一个新密钥。然而,创建并不是基于密码或字节序列,正因为如此,为加密而生成的密钥无法很容易地重构为解密。因此,用于加密的密钥必须持久化到存储系统,例如文件系统,以便以后可以用于解密。Tink允许存储明文键(当然不建议这样做)。一种更安全的方法是使用存储在远程密钥管理系统中的主密钥对密钥进行加密(更详细地解释了TinkJAVA-HOWTO、存储密钥集和加载现有密钥集的部分)。

Tink的密钥管理理念(目的是避免敏感密钥材料的意外泄漏)也让它变得有些麻烦(这在后来的版本中可能会改变)。这就是为什么我在评论中说,我不确定Tink是否符合你关于简单性的观点。

票数 15
EN

Stack Overflow用户

发布于 2019-03-19 01:07:02

免责声明:我是丁克的首席开发人员。

如果您正在开发安卓应用程序,可以查看AndroidKeysetManager。有你可以复制的你好世界的例子

一般来说,当你想要加密的时候,你应该问自己的第一个问题是你要把密钥存储在哪里。将密钥存储在存储加密数据的同一个位置(以及使用相同的ACL)并不会有多大的意义。密钥应该存储在不同的位置(或使用不同的ACL)。

丁克的密钥管理API有点复杂,因为我们想引导用户使用将密钥存储在正确的位置

票数 8
EN

Stack Overflow用户

发布于 2019-11-21 10:04:15

我在寻找一种简单的方法,用基于密码的加密( PBE )加密短消息,并将Google用于加密部分,并发现Tink不提供本地PBE。为了解决这个问题,我创建了一个简单的类,它使用PBE、密钥处理和加密/解密完成所有的工作。

程序中的用法非常简单,您只需4行代码即可使用它:

代码语言:javascript
复制
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

在我的Github上,您可以找到两个示例程序来演示如何实现这个类(有和不带GUI):https://github.com/java-crypto/H-Google-Tink/tree/master/H%20Tink%20Textencryption%20PBE

下面是TinkPbe.java类的源代码:

代码语言:javascript
复制
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;
    }
}

请记住,使用plaintext-String意味着在垃圾收集器销毁它们之前,您的明文在堆中是不可变的和不可删除的。

更详细的描述可在我的网站上提供:http://javacrypto.bplaced.net/h-tink-string-encryption-using-pbe-and-gui/

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55070513

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档