到现在为止,我一直在使用jasypt对字符串进行加密,然后将其存储在应用程序关闭上的磁盘上,然后在从磁盘上检索它后打开app以解密字符串。
使用jasypt非常容易,这是代码:
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 Readme中找不到加密和解密字符串的简单方法,只是可以找到更复杂的操作,实际上我无法理解,因为我的加密知识是完全空的。因此,我使用了一个非常简单的库。
这是Tink Repo:https://github.com/google/tink
是否有一种简单的方法,类似于我的jasypt代码,可以加密和解密tink的字符串?指的是Tink版本1.2.2。发布的代码与以后版本的部分不兼容。
jasypt
中的StrongTextEncryptor
-CLASS使用PBEWithMD5AndTripleDES
-Algorithm。该算法使用Symmetric-Key Block Cipher Triple DES
,并使用MD5
哈希功能从密码派生密钥。后者称为基于密码的加密,并且Tink
中不支持此(至少在08/2018),请参阅如何使用Google Tink创建对称加密密钥?因此,在Tink
中不可能通过密码加密,并且无法实现jasypt
代码中使用的概念。如果在任何情况下都应使用基于密码的加密来使用Tink
的交易。
另一种方法是直接使用密钥。Tink
具有使用AES-GCM
进行加密的AesGcmJce
-CLASS。在这里,钥匙的长度必须为128位或256位:
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
-开发人员本身不推荐此方法,因为AesGcmJce
-CLASS属于com.google.crypto.tink.subtle
-Package,它可能在任何时候随时更改,恕不另行通知,重要警告)。因此,这种方法也不是最佳的。
好吧,Tink
通常如何使用对称加密?这在以下片段中显示为:
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生成a new 键。但是,该创建不是基于密码或字节序列的,因此,为加密生成的密钥无法轻易重建用于解密。因此,用于加密的密钥必须持续到存储系统,例如文件系统,以后可以用于解密。Tink
允许存储clearText键(当然不建议使用)。一种更安全的方法是使用存储在远程密钥管理系统中的主密钥的密钥加密(这是更详细的说明Tink
的Java-Howto,节存储Keysets 和加载现有的KeySets)。
Tink
的密钥管理概念(旨在避免意外泄漏敏感的关键材料)也使它在某种程度上繁琐(这可能会在以后的版本中改变)。这就是为什么我在评论中说,我不确定Tink
是否适合您关于简单性的想法。
免责声明:我是Tink的首席开发人员。/p>
如果您正在使用Android应用程序,则可以查看AndroidKeysetManager
。有一个Hello World示例,您可以复制。
通常,每当您想加密某些内容时,您应该问自己的第一个问题是要存储钥匙的位置。将钥匙存储在同一位置(以及与同一ACL)中的钥匙并不具有很多感官,您可以在其中存储加密的数据。密钥应存储在不同的位置(或使用不同的ACL)。
Tink的密钥管理API更为复杂,因为我们想引导用户将密钥存储在正确的位置。
我正在寻找一种简单的方法来加密使用基于密码的加密(PBE)的简短短信,并将Google Tink用于加密部分,并发现Tink不提供PBE,。为了解决这个问题,我完成了一个简单的课程,可以使用PBE,键入和加密/解密来完成所有工作。
程序中的用法非常简单,您只需要4行代码即可使用:
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/tink/master/master/master/h tink%20Textencryption%20pbe
这是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;
}
}
请记住,使用明文 - 字符串表示您的纯文本在堆中是不明显的,直到垃圾收集器会破坏它们为止。
。更详细的描述可在我的网站上访问:http://javacrypto.bplace.net/h-tink-string-encryption-encryption-using-using-pbe-and-gui/