我有敏感数据,保存到数据库时需要加密,这样即使他们查看数据库,也没人能读取它。因此,我遵循的方法是使用AttributeConverter
。
我创建了这样的课程
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
@Converter
public class SensitiveDataConverter implements AttributeConverter<String, String> {
private final EncryptDecryptUtils encryptDecryptUtils;
/**
* Instantiates a new vulnerable data converter.
* Not using {@link javax.inject.Inject} because @Inject doesn't
* work with AttributeConverter
*
*/
public SensitiveDataConverter() {
this.encryptDecryptUtils = EncryptDecryptUtils.getInstance();
}
@Override
public String convertToDatabaseColumn(String attribute) {
return attribute != null ? encryptDecryptUtils.encrypt(attribute): null;
}
@Override
public String convertToEntityAttribute(String dbData) {
return dbData != null ? encryptDecryptUtils.decrypt(dbData): null;
}
在我的Entity类中,我将敏感字段标记为
@Column
@Convert(converter = SensitiveDataConverter.class)
private String sensitiveData;
有不止一列包含此类敏感数据。
我的加密/解密代码如下所示。我正在使用128长键。
private static final byte[] IV = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
private static final IvParameterSpec IV_PARAMETER_SPEC = new IvParameterSpec(IV);
private static final int KEY_LENGTH = 128;
private final SecretKeySpec secretKeySpec;
private final Cipher cipher;
private static volatile EncryptDecryptUtils instance;
private String secretKey = "someSecretkey";
private String salt = "someSalt";
private EncryptDecryptUtils() {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(secretKey.toCharArray(), salt.getBytes(StandardCharsets.UTF_8),
65536, KEY_LENGTH);
SecretKey tmp = factory.generateSecret(spec);
secretKeySpec = new SecretKeySpec(tmp.getEncoded(), "AES");
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
} catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchPaddingException e) {
LOG.error("Error while encrypting: {}", e);
throw new EncryptDecryptException("Error while initializing encryption mechanism", e);
}
}
public static EncryptDecryptUtils getInstance() {
if (instance == null) {
synchronized (EncryptDecryptUtils .class) {
if (instance == null) {
instance = new EncryptDecryptUtils();
}
}
}
return instance;
}
public String encrypt(String stringToEncrypt){
try {
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, IV_PARAMETER_SPEC);
return Base64.getEncoder().encodeToString(cipher.doFinal(stringToEncrypt.getBytes(StandardCharsets.UTF_8)));
} catch (BadPaddingException | InvalidKeyException | IllegalBlockSizeException
| InvalidAlgorithmParameterException e) {
LOG.error("Error while encrypting: {}", stringToEncrypt, e);
throw new EncryptDecryptException("Error while encrypting sensitive data", e);
}
}
public String decrypt(String stringToDecrypt){
try {
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, IV_PARAMETER_SPEC);
return new String(cipher.doFinal(Base64.getDecoder().decode(stringToDecrypt)), StandardCharsets.UTF_8);
} catch (BadPaddingException | InvalidKeyException | IllegalBlockSizeException
| InvalidAlgorithmParameterException e) {
LOG.error("Error while decrypting: {}", stringToDecrypt, e);
throw new EncryptDecryptException("Error while decrypting sensitive data", e);
}
}
整个代码流运行正常。但是不时地在解密时我会看到抛出该异常
Caused by: javax.crypto.BadPaddingException: Invalid PKCS#5 padding length: <somenumber>
at iaik.security.cipher.f.b(Unknown Source)
at iaik.security.cipher.a.a(Unknown Source)
at iaik.security.cipher.a.engineDoFinal(Unknown Source)
at javax.crypto.Cipher.doFinal(Cipher.java:2164)
at com.bmw.scmaer.scenario.util.EncryptDecryptUtils.decrypt(EncryptDecryptUtils.java:xxx)
如果我再次重新运行相同的流程。有用。它是间歇性发生的。因此,我无法找出根本原因。
我正在将JPA和eclipselink用作ORM。
非常感谢您的帮助。