我正在开发一个需要存储静态加密的二进制信息的程序。不幸的是,我似乎无法找到一种资源来解释哪种加密方案最适合不同的应用程序。
由于加密很复杂而且我不是专家,所以我决定使用一个名为Jasypt的库来包装Java的内置加密函数。为了弄清楚我可以使用哪些算法,我写了几个单元测试。
第一个测试调用Jasypt的AlgorithmRegistry.getAllPBEAlgorithms()
函数并列出所有可用的加密算法:
PBEWITHHMACSHA1ANDAES_128
PBEWITHHMACSHA1ANDAES_256
PBEWITHHMACSHA224ANDAES_128
PBEWITHHMACSHA224ANDAES_256
PBEWITHHMACSHA256ANDAES_128
PBEWITHHMACSHA256ANDAES_256
PBEWITHHMACSHA384ANDAES_128
PBEWITHHMACSHA384ANDAES_256
PBEWITHHMACSHA512ANDAES_128
PBEWITHHMACSHA512ANDAES_256
PBEWITHMD5ANDDES
PBEWITHMD5ANDTRIPLEDES
PBEWITHSHA1ANDDESEDE
PBEWITHSHA1ANDRC2_128
PBEWITHSHA1ANDRC2_40
PBEWITHSHA1ANDRC4_128
PBEWITHSHA1ANDRC4_40
在运行时,如果您尝试使用由于某种原因不支持或违反Java加密规则的算法,Jasypt将抛出EncryptionOperationNotPossibleException
。有趣的是,如果我尝试使用每个“可用”算法来加密然后解密某些任意数据,并且只打印掉那些没有抛出异常的数据,我会得到这个精简列表:
PBEWITHMD5ANDDES
PBEWITHSHA1ANDDESEDE
PBEWITHSHA1ANDRC2_128
PBEWITHSHA1ANDRC2_40
PBEWITHSHA1ANDRC4_128
PBEWITHSHA1ANDRC4_40
可以通过拉入BouncyCastle JCE并通过执行Security.addProvider(new BouncyCastleProvider())
进行注册来扩展可用算法列表。如果我在执行此操作后重复上一个测试,我会得到一个更大的算法列表供您选择:
PBEWITHMD2ANDDES
PBEWITHMD5AND128BITAES-CBC-OPENSSL
PBEWITHMD5AND192BITAES-CBC-OPENSSL
PBEWITHMD5AND256BITAES-CBC-OPENSSL
PBEWITHMD5ANDDES
PBEWITHMD5ANDRC2
PBEWITHSHA1ANDDES
PBEWITHSHA1ANDDESEDE
PBEWITHSHA1ANDRC2
PBEWITHSHA1ANDRC2_128
PBEWITHSHA1ANDRC2_40
PBEWITHSHA1ANDRC4_128
PBEWITHSHA1ANDRC4_40
PBEWITHSHA256AND128BITAES-CBC-BC
PBEWITHSHA256AND192BITAES-CBC-BC
PBEWITHSHA256AND256BITAES-CBC-BC
PBEWITHSHAAND128BITAES-CBC-BC
PBEWITHSHAAND128BITRC2-CBC
PBEWITHSHAAND128BITRC4
PBEWITHSHAAND192BITAES-CBC-BC
PBEWITHSHAAND2-KEYTRIPLEDES-CBC
PBEWITHSHAAND256BITAES-CBC-BC
PBEWITHSHAAND3-KEYTRIPLEDES-CBC
PBEWITHSHAAND40BITRC2-CBC
PBEWITHSHAAND40BITRC4
PBEWITHSHAANDIDEA-CBC
PBEWITHSHAANDTWOFISH-CBC
不幸的是,现在我不知道这些算法中哪一个最适合我的应用程序。我知道AES是正确的方法,看起来PBEWITHSHA256AND256BITAES-CBC-BC
是具有最长密钥长度的AES实现,但我不知道去哪里确认这种怀疑。
这些方案中的哪一个会提供最高的安全级别,哪些方面存在明显的安全问题?
编辑:我希望能够在不要求最终用户安装无限加密文件的情况下分发我的代码,因为这几乎肯定超出了不那么精通技术的用户的能力。我真正想要的是在不使用无限强度管辖权文件的情况下获得的最强加密。
答案 0 :(得分:4)
首先,您应该安装Oracle的无限加密文件。执行此操作后,您应该拥有更少的EncryptionOperationNotPossibleException
秒,并且可以获得更高的安全级别。
此外,对于最高级别的加密,我根本不会使用JaSypt,因为it doesn't seem to provide any integrity checking or authentication of ciphertext。对于那些似乎无关紧要的机密性。但在实践中,您需要根据您的威胁模型进行检查。
如果您决定使用JaSypt(我个人不喜欢),您应该选择PBEWITHHMACSHA512ANDAES_256
作为最高级别的可能性。确保您了解PBKDF2和工作因素(迭代计数,例如setKeyObtentionIterations
。
你不需要Bouncy Castle。 AES被认为是安全的;所有Bouncy Castle都会 - 使用JaSypt的例子 - 正在添加模糊的密码。在上面的列表中,AES之外的所有内容都不如AES安全。
答案 1 :(得分:-3)
您遇到的原始问题是如何加密数据。使用什么算法和什么java库?
package cryptography;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;
public class BasicAESEncrypt {
private final byte[] SALT = {
(byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
(byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03
};
private Cipher ecipher;
private Cipher dcipher;
Encoder encoder = Base64.getEncoder();
Decoder decoder = Base64.getDecoder();
BasicAESEncrypt(String password) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidParameterSpecException, InvalidAlgorithmParameterException{
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password.toCharArray(), SALT, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
ecipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = ecipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
}
public String encrypt(String encrypt) throws Exception {
byte[] bytes = encrypt.getBytes("UTF8");
byte[] encrypted = encrypt(bytes);
return encoder.encodeToString(encrypted);
}
public byte[] encrypt(byte[] plain) throws Exception {
return ecipher.doFinal(plain);
}
public String decrypt(String encrypt) throws Exception {
byte[] decodedData = decoder.decode(encrypt);
byte[] decrypted = decrypt(decodedData);
return new String(decrypted, "UTF8");
}
public byte[] decrypt(byte[] encrypt) throws Exception {
return dcipher.doFinal(encrypt);
}
public static void main(String[] args) throws Exception {
String message = "Wire message for encryption";
String password = "TopSecretKey";
try {
BasicAESEncrypt app = new BasicAESEncrypt(password);
String encrypted = app.encrypt(message);
System.out.println("Encrypted string is: " + encrypted);
String decrypted = app.decrypt(encrypted);
System.out.println("Decrypted string is: " + decrypted);
} catch (InvalidKeyException | NoSuchAlgorithmException | InvalidKeySpecException | NoSuchPaddingException
| InvalidParameterSpecException | InvalidAlgorithmParameterException e1) {
e1.printStackTrace();
}
try {
} catch (Exception e) {
e.printStackTrace();
}
}
}