VaultManagerImpl.java
/*
* Copyright 2010-2019 James Pether Sörling
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $Id$
* $HeadURL$
*/
package com.hack23.cia.service.impl.action.application.encryption;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jcajce.provider.digest.SHA3;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.hack23.cia.model.internal.application.secure.impl.EncryptedValue;
import com.hack23.cia.model.internal.application.secure.impl.EncryptedValue_;
import com.hack23.cia.model.internal.application.user.impl.UserAccount;
import com.hack23.cia.service.data.api.EncryptedValueDAO;
/**
* The Class VaultManagerImpl.
*/
@Service
@Transactional(propagation = Propagation.REQUIRED)
public final class VaultManagerImpl implements VaultManager {
/** The Constant IV_BYTE_SIZE. */
private static final int IV_BYTE_SIZE = 12;
/** The Constant TAG_BIT_LENGTH. */
private static final int TAG_BIT_LENGTH = 128;
/** The Constant KEY_SIZE_IN_BYTES. */
private static final int KEY_SIZE_IN_BYTES = 32;
/** The Constant ENCRYPT_VALUE. */
private static final String ENCRYPT_VALUE = "encryptValue";
/** The Constant DECRYPT_VALUE. */
private static final String DECRYPT_VALUE = "decryptValue";
/** The Constant LOGGER. */
private static final Logger LOGGER = LoggerFactory
.getLogger(VaultManagerImpl.class);
/** The Constant AES_GCM_NO_PADDING. */
private static final String AES_GCM_NO_PADDING = "AES/GCM/NoPadding";
/** The algorithm. */
private static final String ALGORITHM = "AES";
/** The encrypted value DAO. */
private final EncryptedValueDAO encryptedValueDAO;
/**
* Instantiates a new vault manager impl.
*
* @param encryptedValueDAO the encrypted value DAO
*/
@Autowired
public VaultManagerImpl(final EncryptedValueDAO encryptedValueDAO) {
super();
this.encryptedValueDAO = encryptedValueDAO;
}
static {
Security.setProperty("crypto.policy", "unlimited");
Security.addProvider(new BouncyCastleProvider());
}
@Override
public String getEncryptedValue(final String password, final UserAccount userExist) {
if (userExist != null) {
final EncryptedValue encryptedValue = encryptedValueDAO.findFirstByProperty(EncryptedValue_.userId,
userExist.getUserId());
if (encryptedValue != null) {
return decryptValue(password, userExist.getUserId(), encryptedValue.getStorage());
}
}
return null;
}
@Override
public String encryptValue(final String password, final String userId, final String value) {
if (password != null && userId != null && value!=null) {
try {
final Key buildKey = buildKey(userId, password);
final SecureRandom secureRandom = new SecureRandom();
final byte[] iv = new byte[IV_BYTE_SIZE];
secureRandom.nextBytes(iv);
final Cipher cipher = Cipher.getInstance(AES_GCM_NO_PADDING);
final GCMParameterSpec parameterSpec = new GCMParameterSpec(TAG_BIT_LENGTH, iv);
cipher.init(Cipher.ENCRYPT_MODE, buildKey, parameterSpec);
final byte[] cipherText = cipher.doFinal(value.getBytes(StandardCharsets.UTF_8));
final ByteBuffer byteBuffer = ByteBuffer.allocate(4 + iv.length + cipherText.length);
byteBuffer.putInt(iv.length);
byteBuffer.put(iv);
byteBuffer.put(cipherText);
return Hex.toHexString(byteBuffer.array());
} catch (final GeneralSecurityException e) {
LOGGER.error(ENCRYPT_VALUE,e);
return null;
}
} else {
return null;
}
}
@Override
public String decryptValue(final String password, final String userId, final String value) {
if (password != null && userId != null && value!=null) {
try {
final Key buildKey = buildKey(userId, password);
final ByteBuffer byteBuffer = ByteBuffer.wrap(Hex.decode(value.getBytes(StandardCharsets.UTF_8)));
final int ivLength = byteBuffer.getInt();
final byte[] iv = new byte[ivLength];
byteBuffer.get(iv);
final byte[] cipherText = new byte[byteBuffer.remaining()];
byteBuffer.get(cipherText);
final Cipher cipher = Cipher.getInstance(AES_GCM_NO_PADDING);
cipher.init(Cipher.DECRYPT_MODE, buildKey, new GCMParameterSpec(TAG_BIT_LENGTH, iv));
return new String(cipher.doFinal(cipherText),StandardCharsets.UTF_8);
} catch (final GeneralSecurityException e) {
LOGGER.error(DECRYPT_VALUE,e);
return null;
}
} else {
return null;
}
}
/**
* Builds the key.
*
* @param userid
* the userid
* @param password
* the password
* @return the key
*/
private static Key buildKey(final String userid, final String password) {
return new SecretKeySpec(Arrays.copyOf(
new SHA3.Digest512().digest((userid + ".uuid" + password).getBytes(StandardCharsets.UTF_8)), KEY_SIZE_IN_BYTES),
ALGORITHM);
}
}