I'm developing some RESTFull web services for a project. I use the Spring framework and use gradle for build. The problem is , I want to encrypt and decrypt data table when write and read data. I already have a algorithm(class) to encrypt and decrypt data with AES etc. What I need is, how annotate this method to hibernate entity class, am I need to create bean for this class ?
Ex:-
@Column(columnDefinition= "LONGBLOB", name = "card_no")
@ColumnTransformer(
read="decrypt(card_no)",
write="encrypt(?)")
private String cardNo;
Like this I want to add my own encryption/decryption java method to here.
If you have access to JPA 2.1, I would advocate for the use of an @Convert
annotation with an AttributeConverter
implementation.
An AttributeConverter
defines a contract between the state of an entity property when it is serialized to the datastore and when it is deserialized from the datastore.
public class CreditCard {
@Convert(converter = CreditCardNumberConverter.class)
private String creditCardNumber;
}
Your converter implementation might look like this
public class CreditCardNumberConverter implements AttributeConverter<String, String> {
@Override
public String convertToDatabaseColumn(String attribute) {
/* perform encryption here */
}
@Override
public String convertToEntityAttribute(String dbData) {
/* perform decryption here */
}
}
If you are not able to leverage JPA 2.1, an EntityListener
or the use of @PrePersist
, @PreUpdate
, and @PostLoad
may be used in order to perform similar logic for encrypting and decrypting the database value.
Just be sure that if you decide to use an EntityListener
or any of the Pre/Post
callback method annotations, store the decrypted result in a transient field and use that field as your business layer's usage, such as follows:
public class CreditCard {
// this field could have package private get/set methods
@Column(name = "card_number", length = 25, nullable = false)
private String encrpytedCardNumber;
// this is the public operated upon field
@Transient
private String cardNumber;
@PostLoad
public void decryptCardNumber() {
// decrypts card number during DATABASE READ
this.cardNumber = EncryptionUtils.decrypt(encryptedCardNumber);
}
@PrePersist
@PreUpdate
public void encryptCardNumber() {
// encrypts card number during INSERT/UPDATE
this.encryptedCardNumber = EncryptionUtils.encrypt(cardNumber);
}
}
Doing the above keeps entity state consistent in the object as to what exists in your database, without having Hibernate believing that the entity has changed immediately upon loading the database data.
You could do this in several ways.
Following is a simple example. Please change accordingly.
public class CustomListener{
@Inject
private EncryptorBean encryptor;
@PostLoad
@PostUpdate
public void decrypt(Object pc) {
if (!(pc instanceof)) {
return;
}
MyObj obj = (MyObj) pc;
if (obj.getCardNo() != null) {
obj.setCardNo(
encryptor.decryptString(user.getEncryptedCardNo);
}
}
@PrePersist
@PreUpdate
public void encrypt(Object pc) {
if (!(pc instanceof MyObj)) {
return;
}
MyObj obj = (MyObj ) pc;
if (obj.getCardNo() != null) {
user.setEncryptedCardNo(
encryptor.encryptString(user.getCardNo());
}
}
}
With this approach, you might have to take some precaution to avoid encrypting a already encrypted cardNo
value. An additional Transient
property could be used to hold the state whether the cardNo
is already encrypted or not.
Or Simply Implementing this feature in the getters and setters of the entity property.
public String getCardNo(){
return EncrypUtil.decrypt(this.cardNo);
}
public void setCardNo(String cardNo){
this.cardNo = EncrypUtil.encrypt(cardNo);
}
You could also use JPA vendor specific interceptors. i.e HibernateInterceptors
public class CustomInterceptor extends EmptyInterceptor{
public boolean onSave(Object entity,Serializable id,
Object[] state,String[] propertyNames,Type[] types)
throws CallbackException {
if (entity instanceof MyObj){
// check if already encrypted or not.
//(A transient property could be useful)
entity.setCardNo(EncrypUtils.encrypt(entity.getCardNo()));
}
You could also use @Convert
annotation and specify a converter
@Convert(converter = CCConverter.class)
private String creditCardNumber;
CCConverter
class should be an implementation of AttributeConverter
Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With