Hibernate - Store a column as encrypted, and decrypt only on runtime - java

I have a database column that needs to be encrypted, when passed from a hibernate backed webapp. The webapp is on tomcat 6, Hibernate 4, and Mysql as the backing store.
The problem however is that the password to encrypt/decrypt this field will only be available at runtime of the program. Initially I had hoped to use the AES_ENCRYPT/DECRYPT methods, outlined quite well here:
DataBase encryption in Hibernate
and here:
http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/mapping.html#mapping-column-read-and-write
(Though this does refer to version 3.6 of hibernate, I believe it should be the same in 4.0).
However, since this uses the following notation:
#Column(columnDefinition= "LONGBLOB", name="encryptedBody")
#ColumnTransformer(
read="AES_DECRYPT(encryptedBody, 'password')",
write="AES_ENCRYPT(?, 'password')")
public byte[] getEncryptedBody() {
return encryptedBody;
}
public void setEncryptedBody(byte[] encryptedBody) {
this.encryptedBody = encryptedBody;
}
This requires that the password be specified in the annotation itself, and cannot be a variable.
Is there a way to use the database methods through hibernate in this manner, but with the password as a variable? Is there a better approach?

Currently there is not a way to parameterize the pieces of the read/write fragments. They are more meant as general purpose solutions. We have discussed adding support for #Encrypted in Hibernate that would roughly act like you suggest. #Encrypted would give more flexibility, like in-vm crypto versus in-db crypto, parameterization, etc.
JPA 2.1 also has a feature you could use, called attribute converters. They would only be able to apply in-vm crypto however.

You can Use Hibernate #Type attribute,Based on your requirement you can customize the annotation and apply on top of the fied. like :
public class PhoneNumberType implements UserType {
#Override
public int[] sqlTypes() {
return new int[]{Types.INTEGER, Types.INTEGER, Types.INTEGER};
}
#Override
public Class returnedClass() {
return PhoneNumber.class;
}
// other methods
}
First, the null SafeGet method:
#Override
public Object nullSafeGet(ResultSet rs, String[] names,
SharedSessionContractImplementor session, Object owner) throws HibernateException,
SQLException {
int countryCode = rs.getInt(names[0]);
if (rs.wasNull())
return null;
int cityCode = rs.getInt(names[1]);
int number = rs.getInt(names[2]);
PhoneNumber employeeNumber = new PhoneNumber(countryCode, cityCode, number);
return employeeNumber;
}
Next, the null SafeSet method:
#Override
public void nullSafeSet(PreparedStatement st, Object value,
int index, SharedSessionContractImplementor session)
throws HibernateException, SQLException {
if (Objects.isNull(value)) {
st.setNull(index, Types.INTEGER);
} else {
PhoneNumber employeeNumber = (PhoneNumber) value;
st.setInt(index,employeeNumber.getCountryCode());
st.setInt(index+1,employeeNumber.getCityCode());
st.setInt(index+2,employeeNumber.getNumber());
}
}
Finally, we can declare our custom PhoneNumberType in our OfficeEmployee entity class:
#Entity
#Table(name = "OfficeEmployee")
public class OfficeEmployee {
#Columns(columns = { #Column(name = "country_code"),
#Column(name = "city_code"), #Column(name = "number") })
#Type(type = "com.baeldung.hibernate.customtypes.PhoneNumberType")
private PhoneNumber employeeNumber;
// other fields and methods
}
This might solve your problem, This will work for all database. if you want more info refer :: https://www.baeldung.com/hibernate-custom-types
similarly you have to do UTF-8 encoding/Decoding and ISO-8859-1 Decoding/encoding

Related

Spring JPA #Converter - How to encrypt entity using it's own id as salt?

So I have this case where the JPA entity is encrypted using it's own ID as salt.
Here is an example of doing the en/decrypt without annotation, I have to "manually" create custom get/setter to each encrypted fields.
StandardDbCipher is just my cipher class that accepts a salt during construction (which is in this case is the ID field). The password is already fixed in some other file.
#Entity
public class Applicant implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private String id;
private String profilePic;
private String contact;
private String personalInfo;
#Transient
private StandardDbCipher cipher;
private StandardDbCipher getCipher() {
if (cipher == null) {
cipher = new StandardDbCipher(id);
}
return cipher;
}
private String encrypt (String plain) {
return getCipher().decrypt(plain);
}
private String decrypt (String crypt) {
return getCipher().encrypt(crypt);
}
public String getProfilePic() {
return decrypt(profilePic);
}
public void setProfilePic(String profilePic) {
this.profilePic = encrypt(profilePic);
}
public String getContact() {
return decrypt(contact);
}
public void setContact(String contact) {
this.contact = encrypt(contact);
}
public String getPersonalInfo() {
return decrypt(personalInfo);
}
public void setPersonalInfo(String personalInfo) {
this.personalInfo = encrypt(personalInfo);
}
}
I would like to simplify the code and reduce boilerplate using #Converter, but couldn't figure out how to put the ID as salt? Any ideas? Maybe other annotation?
If it's something that you need to do in many entities, then I think you can try Aspect Oriented Programming (the most known implementation is AspectJ). Spring also has an integration with that (I've not worked with that as I'm not using Spring). The idea is that you can have some intercepting code that would be executed before or after a call to methods of your objects (in your case getter/setter methods of your entities) and inside them you can manipulate the actual object/parameters/return values.
You can call your encrypt method before the execution of setter method and pass the encrypted value to your setter. For the decryption, you run your decrypt method after the execution of the getter method.
By doing so, your entities would remain as a simple POJO and you don't need to provide a converter for each.
. Here are some tutorial demonstrating the AOP concept:
#AspectJ Based AOP with Spring
Introduction to Spring AOP
Update:
Another solution could be to use JPA Entity Listeners. You can do the encryption on #PrePersist and the decryption on #PostLoad callbacks in your entity or use a single listener class for all such entities. You would just need to annotate your POJOs like this:
#Entity
#EntityListeners(class=EncDecListener.class)
public class Applicant implements Serializable {
}
public class EncDecListener {
#PreUpdate
public void encrypt(Applicatnt a) {
// do encryption
}
#PostLoad
public void decrypt(Applicatnt a) {
// do decryption
}
}

Lombok annotation and jdbcOperation

I have a bean:
#Data
class Sample {
#NonNull
private final String name,
#NonNull
private final String rollNumber,
#NonNull
private final String standard,
}
I am saving this bean data to database in the table sample.
The problem:
I am using jdbcOperation to fetch data from the database. In my use case I want to retrieve only two fields from the database say name and standard for a particular view(I'm using Spring MVC). As all the three fields in the bean are marked with NonNull annotation from Lombok, I can not create an object inside the RowMapper overriden implementation.
private static final class SampleRowMapper implements RowMapper<Sample> {
#Override
#Nonnull
public Sample mapRow(ResultSet rs, int rowNum) throws SQLException {
Sample sample =
new Sample(
rs.getString("name"),
rs.getString("standard")
null); // error as annotation doesn't allow null values
return sample;
}
}
If I remove the annotation there are other cases that will be missed out(say saving an object of Sample with no null values, I don't want rollNumber to be null). What should be done to fulfill all use cases?

Spring Boot with JDBC: Nested Object Modelling

i have the following TableStructure in a PostgreSQL DB which is supposed to be the DB Backend for my WebApp:
init_db.sql
CREATE TABLE article (
id integer NOT NULL,
name character varying NOT NULL,
type_id integer NOT NULL
);
CREATE TABLE article_type (
id integer NOT NULL,
type_desc character varying NOT NULL
);
ALTER TABLE ONLY article
ADD CONSTRAINT
article_type_id_fkey FOREIGN KEY (type_id) REFERENCES article_type(id);
The basic access to this works (via DataSource Object defined in application.properties and letting Spring Boot handle the rest). I'm having now difficulties in understanding how to access/model this best in Spring Boot. Currently my Model Classes look like this:
ArticleType.java
public class ArticleType {
private Integer id;
private String name;
// Getters and Setters
}
andArticle.java
public class Article {
private Integer id;
private String name;
private String desc;
private ArticleType article_type;
// Getters and Setters
}
Following this example, i was constructing those classes:
ArticleTypeRepository.java
#Repository
public class ArticleTypeRepository {
#Autowired
protected JdbcTemplate jdbc;
public ArticleType getArticleType(int id) {
return jdbc.queryForObject("SELECT * FROM article.article_type WHERE id=?", articleTypeMapper, id);
}
private static final RowMapper<ArticleType> articleTypeMapper = new RowMapper<ArticleType>() {
public ArticleType mapRow(ResultSet rs, int rowNum) throws SQLException {
ArticleType articletype = new ArticleType();
articletype.setId(rs.getInt("id"));
articletype.setName(rs.getString("type_desc"));
return articletype;
}
};
and for the following file my question arises:ArticleRepository.java
#Repository
public class ArticleRepository {
#Autowired
protected JdbcTemplate jdbc;
public Article getArticle(int id) {
return jdbc.queryForObject("SELECT * FROM article.article WHERE id=?", articleMapper, id);
}
private static final RowMapper<Article> articleMapper = new RowMapper<Article>() {
public Article mapRow(ResultSet rs, int rowNum) throws SQLException {
Article article = new Article();
article.setId(rs.getInt("id"));
article.setName(rs.getString("name"));
// The following line is the one in question
// ArticleType at = getArticleType(Integer.parseInt(rs.getString("type_id")));
article.setArticle_type(at);
article.setDesc(rs.getString("description"));
return article;
}
};
What is the best practice to get the ArticleType here for the Article? Is this anyway good practice to retrieve those objects? Or should I just use a plain String object in the Article Object and query this with a view or something? I looked through the internet for "Spring Boot JDBC Nested Object Java Access Modeling" and the alike, but couldn't find any real hints or tutorials to this specific question, which makes me wonder if i'm doing something conceptually completely wrong. Any hints are appreciated (tutorials, doc's, paradigms how to do this properly, etc.)
I'll double post M. Deinum 's answer here, since it got me rolling until i switched to Hibernate/JPA:
By creating a query that returns everything you need. Write a select
statement that joins both tables.

jOOQ: Allowed-Character constraints?

I am considering moving from Hibernate to jOOQ but I can't find e.g.
how to have Pattern-Constraints on a String like this in Hibernate:
#NotEmpty(message = "Firstname cannot be empty")
#Pattern(regexp = "^[a-zA-Z0-9_]*$", message = "First Name can only contain characters.")
private String firstname;
How would I do that in jOOQ?
The "jOOQ way"
The "jOOQ way" to do such validation would be to create either:
A CHECK constraint in the database.
A trigger in the database.
A domain in the database.
After all, if you want to ensure data integrity, the database is where such constraints and integrity checks belong (possibly in addition to functionally equivalent client-side validation). Imagine a batch job, a Perl script, or even a JDBC statement that bypasses JSR-303 validation. You'll find yourself with corrupt data in no time.
If you do want to implement client-side validation, you can still use JSR-303 on your DTOs, which interact with your UI, for instance. But you will have to perform validation before passing the data to jOOQ for storage (as artbristol explained).
Using a Converter
You could, however, use your own custom type by declaring a Converter on individual columns and by registering such Converter with the source code generator.
Essentially, a Converter is:
public interface Converter<T, U> extends Serializable {
U from(T databaseObject);
T to(U userObject);
Class<T> fromType();
Class<U> toType();
}
In your case, you could implement your annotations as such:
public class NotEmptyAlphaNumericValidator implements Converter<String, String> {
// Validation
public String to(String userObject) {
assertNotEmpty(userObject);
assertMatches(userObject, "^[a-zA-Z0-9_]*$");
return userObject;
}
// Boilerplate
public String from(String databaseObject) { return databaseObject; }
public Class<String> fromType() { return String.class; }
public Class<String> toType() { return String.class; }
}
Note that this is more of a workaround, as Converter hasn't been designed for this use-case, even if it can perfectly implement it.
Using formal client-side validation
There's also a pending feature request #4543 to add more support for client-side validation. As of jOOQ 3.7, this is not yet implemented.
I recommend you don't try to use jOOQ in a 'hibernate/JPA' way. Leave the jOOQ generated classes as they are and map to your own domain classes manually, which you are free to annotate however you like. You can then call a JSR validator before you attempt to persist them.
For example, jOOQ might generate the following class
public class BookRecord extends UpdatableRecordImpl<BookRecord> {
private String firstname;
public void setId(Integer value) { /* ... */ }
public Integer getId() { /* ... */ }
}
You can create your own domain object
public class Book {
#NotEmpty(message = "Firstname cannot be empty")
#Pattern(regexp = "^[a-zA-Z0-9_]*$", message = "First Name can only contain characters.")
private String firstname;
public void setId(Integer value) { /* ... */ }
public Integer getId() { /* ... */ }
}
and map by hand once you've retrieved a BookRecord, in your DAO layer
Book book = new Book();
book.setId(bookRecord.getId());
book.setFirstname(bookRecord.getFirstname());
This seems quite tedious (and ORM tries to spare you this tedium) but actually it scales quite well to complicated domain objects, in my opinion, and it's always easy to figure out the flow of data in your application.

How to get old entity value in #HandleBeforeSave event to determine if a property is changed or not?

I'm trying to get the old entity in a #HandleBeforeSave event.
#Component
#RepositoryEventHandler(Customer.class)
public class CustomerEventHandler {
private CustomerRepository customerRepository;
#Autowired
public CustomerEventHandler(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
#HandleBeforeSave
public void handleBeforeSave(Customer customer) {
System.out.println("handleBeforeSave :: customer.id = " + customer.getId());
System.out.println("handleBeforeSave :: new customer.name = " + customer.getName());
Customer old = customerRepository.findOne(customer.getId());
System.out.println("handleBeforeSave :: new customer.name = " + customer.getName());
System.out.println("handleBeforeSave :: old customer.name = " + old.getName());
}
}
In the event I try to get the old entity using the findOne method but this return the new event. Probably because of Hibernate/Repository caching in the current session.
Is there a way to get the old entity?
I need this to determine if a given property is changed or not. In case the property is changes I need to perform some action.
If using Hibernate, you could simply detach the new version from the session and load the old version:
#RepositoryEventHandler
#Component
public class PersonEventHandler {
#PersistenceContext
private EntityManager entityManager;
#HandleBeforeSave
public void handlePersonSave(Person newPerson) {
entityManager.detach(newPerson);
Person currentPerson = personRepository.findOne(newPerson.getId());
if (!newPerson.getName().equals(currentPerson.getName)) {
//react on name change
}
}
}
Thanks Marcel Overdijk, for creating the ticket -> https://jira.spring.io/browse/DATAREST-373
I saw the other workarounds for this issue and want to contribute my workaround as well, cause I think it´s quite simple to implement.
First, set a transient flag in your domain model (e.g. Account):
#JsonIgnore
#Transient
private boolean passwordReset;
#JsonIgnore
public boolean isPasswordReset() {
return passwordReset;
}
#JsonProperty
public void setPasswordReset(boolean passwordReset) {
this.passwordReset = passwordReset;
}
Second, check the flag in your EventHandler:
#Component
#RepositoryEventHandler
public class AccountRepositoryEventHandler {
#Resource
private PasswordEncoder passwordEncoder;
#HandleBeforeSave
public void onResetPassword(Account account) {
if (account.isPasswordReset()) {
account.setPassword(encodePassword(account.getPassword()));
}
}
private String encodePassword(String plainPassword) {
return passwordEncoder.encode(plainPassword);
}
}
Note: For this solution you need to send an additionally resetPassword = true parameter!
For me, I´m sending a HTTP PATCH to my resource endpoint with the following request payload:
{
"passwordReset": true,
"password": "someNewSecurePassword"
}
You're currently using a spring-data abstraction over hibernate.
If the find returns the new values, spring-data has apparently already attached the object to the hibernate session.
I think you have three options:
Fetch the object in a separate session/transaction before the current season is flushed. This is awkward and requires very subtle configuration.
Fetch the previous version before spring attached the new object. This is quite doable. You could do it in the service layer before handing the object to the repository. You can, however not save an object too an hibernate session when another infect with the same type and id it's known to our. Use merge or evict in that case.
Use a lower level hibernate interceptor as described here. As you see the onFlushDirty has both values as parameters. Take note though, that hibernate normally does not query for previous state of you simply save an already persisted entity. In stead a simple update is issued in the db (no select). You can force the select by configuring select-before-update on your entity.
Create following and extend your entities with it:
#MappedSuperclass
public class OEntity<T> {
#Transient
T originalObj;
#Transient
public T getOriginalObj(){
return this.originalObj;
}
#PostLoad
public void onLoad(){
ObjectMapper mapper = new ObjectMapper();
try {
String serialized = mapper.writeValueAsString(this);
this.originalObj = (T) mapper.readValue(serialized, this.getClass());
} catch (Exception e) {
e.printStackTrace();
}
}
}
I had exactly this need and resolved adding a transient field to the entity to keep the old value, and modifying the setter method to store the previous value in the transient field.
Since json deserializing uses setter methods to map rest data to the entity, in the RepositoryEventHandler I will check the transient field to track changes.
#Column(name="STATUS")
private FundStatus status;
#JsonIgnore
private transient FundStatus oldStatus;
public FundStatus getStatus() {
return status;
}
public FundStatus getOldStatus() {
return this.oldStatus;
}
public void setStatus(FundStatus status) {
this.oldStatus = this.status;
this.status = status;
}
from application logs:
2017-11-23 10:17:56,715 CompartmentRepositoryEventHandler - beforeSave begin
CompartmentEntity [status=ACTIVE, oldStatus=CREATED]
Spring Data Rest can't and likely won't ever be able to do this due to where the events are fired from. If you're using Hibernate you can use Hibernate spi events and event listeners to do this, you can implement PreUpdateEventListener and then register your class with the EventListenerRegistry in the sessionFactory. I created a small spring library to handle all of the setup for you.
https://github.com/teastman/spring-data-hibernate-event
If you're using Spring Boot, the gist of it works like this, add the dependency:
<dependency>
<groupId>io.github.teastman</groupId>
<artifactId>spring-data-hibernate-event</artifactId>
<version>1.0.0</version>
</dependency>
Then add the annotation #HibernateEventListener to any method where the first parameter is the entity you want to listen to, and the second parameter is the Hibernate event that you want to listen for. I've also added the static util function getPropertyIndex to more easily get access to the specific property you want to check, but you can also just look at the raw Hibernate event.
#HibernateEventListener
public void onUpdate(MyEntity entity, PreUpdateEvent event) {
int index = getPropertyIndex(event, "name");
if (event.getOldState()[index] != event.getState()[index]) {
// The name changed.
}
}
Just another solution using model:
public class Customer {
#JsonIgnore
private String name;
#JsonIgnore
#Transient
private String newName;
public void setName(String name){
this.name = name;
}
#JsonProperty("name")
public void setNewName(String newName){
this.newName = newName;
}
#JsonProperty
public void getName(String name){
return name;
}
public void getNewName(String newName){
return newName;
}
}
Alternative to consider. Might be reasonable if you need some special handling for this use-case then treat it separately. Do not allow direct property writing on the object. Create a separate endpoint with a custom controller to rename customer.
Example request:
POST /customers/{id}/identity
{
"name": "New name"
}
I had the same problem, but I wanted the old entity available in the save(S entity) method of a REST repository implementation (Spring Data REST).
What I did was to load the old entity using a 'clean' entity manager from which I create my QueryDSL query:
#Override
#Transactional
public <S extends Entity> S save(S entity) {
EntityManager cleanEM = entityManager.getEntityManagerFactory().createEntityManager();
JPAQuery<AccessControl> query = new JPAQuery<AccessControl>(cleanEM);
//here do what I need with the query which can retrieve all old values
cleanEM.close();
return super.save(entity);
}
The following worked for me. Without starting a new thread the hibernate session will provide the already updated version. Starting another thread is a way to have a separate JPA session.
#PreUpdate
Thread.start {
if (entity instanceof MyEntity) {
entity.previous = myEntityCrudRepository.findById(entity?.id).get()
}
}.join()
Just let me know if anybody would like more context.
Don't know if you're still after an answer, and this is probably a bit 'hacky', but you could form a query with an EntityManager and fetch the object that way ...
#Autowired
EntityManager em;
#HandleBeforeSave
public void handleBeforeSave(Customer obj) {
Query q = em.createQuery("SELECT a FROM CustomerRepository a WHERE a.id=" + obj.getId());
Customer ret = q.getSingleResult();
// ret should contain the 'before' object...
}

Categories

Resources