У меня есть конфиденциальные данные, которые необходимо зашифровать при сохранении в базе данных, чтобы никто не мог прочитать их, даже если они заглянут в базу данных.Таким образом, подход, который я использовал, заключается в использовании 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;
Существует более одного столбца с таким чувствительнымdata.
Мой код шифрования / дешифрования выглядит следующим образом.Я использую ключ длиной 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.
Любая помощь очень ценится.