package org.ripple.power.wallet;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.List;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.ripple.power.CoinUtils;
import org.ripple.power.Helper;
import org.ripple.power.config.LSystem;
import org.ripple.power.utils.ByteUtils;
public class Passphrase {
private static final String KEYGEN = "PBKDF2WithHmacSHA1";
private static final String AES = "AES/CBC/NoPadding";
private static final int AES_BLOCK_SIZE = 16;
private static final int AES_KEY_LENGTH = 128;
public static byte[] decrypt(Passphrase passphrase, byte[] encryptedData)
throws Exception {
if (encryptedData.length == 0) {
return new byte[0];
}
byte[] byteHolder1;
byte[] byteHolder2 = null;
try {
List<Cipher> ciphers = cipherList(passphrase.getPassphraseHash(),
Cipher.DECRYPT_MODE);
byteHolder1 = ciphers.get(2).doFinal(encryptedData);
byteHolder2 = ciphers.get(1).doFinal(byteHolder1);
byteHolder1 = ciphers.get(0).doFinal(byteHolder2);
byteHolder2 = unPad4AES(byteHolder1);
} catch (Exception ex) {
ex.printStackTrace();
}
return byteHolder2;
}
public static byte[] encrypt(Passphrase passphrase, byte[] unencryptedData)
throws Exception {
if (unencryptedData.length == 0) {
return new byte[0];
}
byte[] byteHolder1;
byte[] byteHolder2 = null;
try {
List<Cipher> ciphers = cipherList(passphrase.getPassphraseHash(),
Cipher.ENCRYPT_MODE);
byteHolder1 = pad4AES(unencryptedData);
byteHolder2 = ciphers.get(0).doFinal(byteHolder1);
byteHolder1 = ciphers.get(1).doFinal(byteHolder2);
byteHolder2 = ciphers.get(2).doFinal(byteHolder1);
} catch (Exception ex) {
ex.printStackTrace();
}
return byteHolder2;
}
private static byte[] unPad4AES(byte[] paddedUnencryptedData)
throws BadPaddingException {
if (paddedUnencryptedData.length == 0) {
throw new BadPaddingException();
}
if ((paddedUnencryptedData.length % AES_BLOCK_SIZE) != 0) {
throw new BadPaddingException();
}
byte lastByte = paddedUnencryptedData[paddedUnencryptedData.length - 1];
if ((lastByte < 0) || (lastByte >= AES_BLOCK_SIZE)) {
throw new BadPaddingException();
}
for (int i = 1; i <= (lastByte + AES_BLOCK_SIZE); i++) {
byte aByte = paddedUnencryptedData[paddedUnencryptedData.length - i];
if (aByte != lastByte) {
throw new BadPaddingException();
}
}
int unencryptedSize = paddedUnencryptedData.length - lastByte
- AES_BLOCK_SIZE;
byte[] unencryptedData = new byte[unencryptedSize];
System.arraycopy(paddedUnencryptedData, 0, unencryptedData, 0,
unencryptedSize);
return unencryptedData;
}
private static byte[] pad4AES(byte[] unencryptedData) {
int bytesToPad = AES_BLOCK_SIZE
- (unencryptedData.length % AES_BLOCK_SIZE);
byte bytesToPadValue = Integer.valueOf(bytesToPad).byteValue();
int totalBytes = unencryptedData.length + bytesToPad + AES_BLOCK_SIZE;
byte[] paddedUnencryptedData = new byte[totalBytes];
System.arraycopy(unencryptedData, 0, paddedUnencryptedData, 0,
unencryptedData.length);
Arrays.fill(paddedUnencryptedData, unencryptedData.length, totalBytes,
bytesToPadValue);
return paddedUnencryptedData;
}
private static IvParameterSpec ivParameterSpec16(byte[] salt, int level)
throws Exception {
byte[] iv = { 1, 1, 30, 1, 99, 2, 90, 1, 0, 2, 13, 32, 20, 3, 1, 70 };
iv[level] = salt[0];
iv[level + 1] = salt[10 * level];
iv[level + 3] = salt[16 * level];
iv[level + 5] = (byte) (0xff & (salt[1 + level] ^ salt[7 * level]));
iv[level + 7] = salt[17 * level];
iv[level + 10] = salt[20 * level];
iv[level + 11] = (byte) (0xff & (salt[13 * level] ^ salt[7 - level]));
return new IvParameterSpec(iv);
}
private static List<Cipher> cipherList(byte[] passphraseBytes, int mode)
throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, InvalidAlgorithmParameterException, Exception {
Cipher aes1 = Cipher.getInstance(AES);
Cipher aes2 = Cipher.getInstance(AES);
Cipher aes3 = Cipher.getInstance(AES);
aes1.init(
mode,
keyGenAES(passphraseBytes, "cping@passsaltyN3SS&Wha9dste",
18913), ivParameterSpec16(passphraseBytes, 1));
aes2.init(
mode,
keyGenAES(passphraseBytes, "cping!wordsaltyN74G@337q0877",
23944), ivParameterSpec16(passphraseBytes, 2));
aes3.init(
mode,
keyGenAES(passphraseBytes, "cping#privatesaltyN9A9!14Ra1",
19781), ivParameterSpec16(passphraseBytes, 3));
return Arrays.asList(aes1, aes2, aes3);
}
private static SecretKey keyGenAES(byte[] passphraseBytes,
String saltString, int iterations) throws Exception {
SecretKey key = null;
try {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(KEYGEN);
byte[] salt = saltString.getBytes();
salt[1] = passphraseBytes[2];
salt[2] = passphraseBytes[10];
salt[3] = (byte) (0xff & (passphraseBytes[60] ^ passphraseBytes[50]));
PBEKeySpec pbeKeySpec = new PBEKeySpec(ByteUtils.toHexString(
passphraseBytes).toCharArray(), salt, iterations,
AES_KEY_LENGTH);
key = keyFactory.generateSecret(pbeKeySpec);
key = new SecretKeySpec(key.getEncoded(), "AES");
} catch (Exception ex) {
ex.printStackTrace();
}
return key;
}
private byte[] passphraseHash;
private static final String STATIC_SALT = LSystem.getAppPassword();
private static final int PASSWORD_ITERATIONS = 20011;
public static final int MINIMUM_PASSPHRASE_LENGTH = 8;
public static final int DEFAULT_CACHE_TIME_IN_SECONDS = 10 * 60;
public Passphrase() {
this.passphraseHash = null;
}
public Passphrase(String passphrase) throws Exception {
this();
this.setPassphrase(passphrase);
}
public void validatePassphrase(String passphrase) throws Exception {
if (passphrase == null
|| passphrase.length() < MINIMUM_PASSPHRASE_LENGTH) {
throw new Exception();
}
}
public final void setPassphrase(String passphrase) throws Exception {
validatePassphrase(passphrase);
try {
this.passphraseHash = Helper.hash512(passphrase, STATIC_SALT,
PASSWORD_ITERATIONS);
} catch (Exception ex) {
clear();
throw ex;
}
}
public byte[] getPassphraseHash() throws Exception {
if (this.passphraseHash == null) {
throw new Exception("passphraseHash == null");
}
return this.passphraseHash;
}
public void clear() {
if (this.passphraseHash != null) {
Arrays.fill(this.passphraseHash, (byte) 0b00000000);
}
this.passphraseHash = null;
}
public boolean isClear() {
return this.passphraseHash == null;
}
public static String encodeToHex(String context, String password)
throws Exception {
Passphrase passphrase = new Passphrase(password);
byte[] buffer = Passphrase.encrypt(passphrase,
context.getBytes(LSystem.encoding));
return CoinUtils.toHex(buffer);
}
public static String decodeToHex(String hash, String password)
throws Exception {
Passphrase passphrase = new Passphrase(password);
byte[] buffer = Passphrase.decrypt(passphrase, CoinUtils.fromHex(hash));
return new String(buffer, LSystem.encoding);
}
public static void main(String[] args) {
try {
String context = "中华民族万岁";
String password = "dsdfuj5439gtfgfh";
System.out.println(decodeToHex(encodeToHex(context, password),
password));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}