/**
* Copyright 2014 Sunny Gleason and original author or authors
*
* 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.
*/
package io.kazuki.v0.internal.helper;
import io.kazuki.v0.store.Key;
import io.kazuki.v0.store.sequence.KeyImpl;
import java.nio.ByteBuffer;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.KeySpec;
import java.util.concurrent.ConcurrentHashMap;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import com.google.common.base.Throwables;
public class KeyObfuscator {
private static final String keyString = System.getProperty("key.encrypt.password", "changeme");
private static final byte[] saltBytes = System.getProperty("key.encrypt.salt", "asalt")
.getBytes();
private static final byte[] ivBytes;
private static final Cipher cipher;
static {
try {
ivBytes =
Hex.decodeHex(System.getProperty("key.encrypt.iv", "0123456789ABCDEF").toCharArray());
cipher = Cipher.getInstance("DES/CBC/NoPadding");
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
private static final AlgorithmParameterSpec paramSpec = new IvParameterSpec(ivBytes);
private static ConcurrentHashMap<String, SecretKey> keyCache =
new ConcurrentHashMap<String, SecretKey>();
public static synchronized String encrypt(String type, Long id) {
StringBuilder encryptedIdentifier = new StringBuilder();
encryptedIdentifier.append("@");
encryptedIdentifier.append(type);
encryptedIdentifier.append(":");
try {
byte[] plain = ByteBuffer.allocate(8).putLong(id).array();
cipher.init(Cipher.ENCRYPT_MODE, getKey(type), paramSpec);
byte[] encrypted = cipher.doFinal(plain);
encryptedIdentifier.append(Hex.encodeHex(encrypted));
return encryptedIdentifier.toString();
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
public static synchronized Key decrypt(String encryptedText) {
if (encryptedText == null || encryptedText.length() == 0 || !encryptedText.contains(":")) {
throw new IllegalArgumentException("Invalid key");
}
if (!encryptedText.startsWith("@")) {
return KeyImpl.valueOf(encryptedText);
}
String[] parts = encryptedText.substring(1).split(":");
if (parts.length != 2 || parts[1].length() != 16) {
throw new IllegalArgumentException("Invalid key");
}
String type = parts[0];
try {
byte[] encrypted = Hex.decodeHex(parts[1].toCharArray());
cipher.init(Cipher.DECRYPT_MODE, getKey(type), paramSpec);
byte[] decrypted = cipher.doFinal(encrypted);
Long id = ByteBuffer.allocate(8).put(decrypted).getLong(0);
return KeyImpl.createInternal(type, id);
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
private static SecretKey getKey(String type) throws Exception {
if (!keyCache.containsKey(type)) {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
String password = keyString + ":" + type;
KeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes, 1024, 64);
SecretKey tmp = factory.generateSecret(spec);
SecretKey key = new SecretKeySpec(tmp.getEncoded(), "DES");
keyCache.put(type, key);
}
return keyCache.get(type);
}
}