AES in java can run in windows but not in linux [duplicate] - java

This question already has an answer here:
Better way to create AES keys than seeding SecureRandom
(1 answer)
Closed 6 years ago.
I just finished an AES Class for decrypt or encrpyt ,and it runs well on windows but can't run on linux throwing errors as following :
Given final block not properly padded
the full code as following :
/**
* AESTest.java
*
* #author liuyincan
* #Time 2013-12-12 下午1:25:44
*/
public class AES {
public static String generateKey(int len) {
try {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(len);
Key key = keyGen.generateKey();
return ParserStringUtils.toHexString(key.getEncoded());
} catch (Exception e) {
return null;
}
}
/**
* 加密
*
* #param content
* 待加密内容
* #param key
* 加密的密钥
* #return
*/
public static String encode(String content, String key) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(key.getBytes()));
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] byteRresult = cipher.doFinal(byteContent);
StringBuffer sb = new StringBuffer();
for (int i = 0; i < byteRresult.length; i++) {
String hex = Integer.toHexString(byteRresult[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
/**
* 解密
*
* #param content
* 待解密内容
* #param key
* 解密的密钥
* #return
*/
public static String decode(String content, String key) {
if (content.length() < 1)
return null;
byte[] byteRresult = new byte[content.length() / 2];
for (int i = 0; i < content.length() / 2; i++) {
int high = Integer.parseInt(content.substring(i * 2, i * 2 + 1), 16);
int low = Integer.parseInt(content.substring(i * 2 + 1, i * 2 + 2), 16);
byteRresult[i] = (byte) (high * 16 + low);
}
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(key.getBytes()));
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] result = cipher.doFinal(byteRresult);
return new String(result);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
please help me to fix the problem or else my boss will fire me ,thanks a lot

Cipher.getInstance("AES"); - this gives the default implementation of AES.
Somewhere between Oracle Java 6 and late Java 7 this changed form AES/ECB/NoPadding to AES/ECB/PKCS5Padding.
Change such lines to:
Cipher.getInstance("AES/ECB/PKCS5Padding");

Related

Ruby: Large data RSA Encryption

so I need to encrypt strings using RSA.
The example of the string (this is verified correct string):
"1658135277&{\"user_id\":\"f257bf74-8c14-4144-aca5-69e1d8819d9d\",\"user_id_type\":\"3\",\"user_name\":\"{\\\"first_name\\\":\\\"SEAN\\\",\\\"middle_name\\\":\\\"\\\",\\\"last_name\\\":\\\"YEO\\\",\\\"full_name\\\":\\\"SEAN YEO\\\"}\",\"user_dob\":\"1993-03-31\",\"nationality\":\"SG\",\"user_address\":\"{\\\"region\\\":\\\"SG\\\",\\\"address_line1\\\":\\\"200 Jalan Sultan, 03-02, Textile Centre\\\"}\"}"
The ruby code:
def get_base64_encryption(unix_timestamp, params)
str = "#{unix_timestamp}&#{params.to_json}"
public_key = OpenSSL::PKey::RSA.new(PUBLIC_KEY_PEM)
encrypted_str = public_key.public_encrypted(str, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
Base64.strict_encode64(encrypted_str)
end
But this way, it will raise error OpenSSL::PKey::RSAError (data too large for key size)
The thing is, I need to encrypt this for integration with 3rd party API. So there's no way I can use other encryption.
The partner actually gave example code, but in java:
public class RSAUtil {
private static final String KEY_ALGORITHM = "RSA";
private static final String RSA_ALGORITHM = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
// rsa secret key length 256 bytes(2048bit)
private static final int MAX_DECRYPT_BLOCK = 256;
// rsa encrypt data length
private static final int MAX_ENCRYPT_BLOCK = 200;
/**
* rsa encrypt data
* #param message
* #param publicKeyStr
* #return
*/
public static String rsaEncryptOAEP(String message, String publicKeyStr) {
try {
System.out.println("message_size:" + message.length());
Decoder decoder = Base64.getDecoder();
byte[] publicKeyStrByte = decoder.decode(publicKeyStr);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyStrByte);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(keySpec);
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] inputData = message.getBytes(StandardCharsets.UTF_8);
int inputLen = inputData.length;
byte[] cache = null;
byte[] out = null;
// split data by block size
for (int offSet = 0; offSet < inputLen; offSet += MAX_ENCRYPT_BLOCK) {
if (offSet + MAX_ENCRYPT_BLOCK < inputLen) {
cache = cipher.doFinal(inputData, offSet, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(inputData, offSet, inputLen - offSet);
}
out = mergeBytes(out, cache);
}
return Base64.getEncoder().encodeToString(out);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
System.out.println("current java env not support RSA v1.5/OAEP");
e.printStackTrace();
} catch (InvalidKeyException | InvalidKeySpecException e) {
System.out.println("invalid key");
e.printStackTrace();
} catch (IllegalBlockSizeException | BadPaddingException e) {
System.out.println("block size more than 214 ");
e.printStackTrace();
}
return "";
}
/**
* merge 2 bytes
*
* #param first
* #param last
* #return
*/
private static byte[] mergeBytes(byte[] first, byte[] last) {
if (first == null) {
return last;
}
byte[] result = new byte[first.length + last.length];
System.arraycopy(first, 0, result, 0, first.length);
System.arraycopy(last, 0, result, first.length, last.length);
return result;
}
}
But I still failed to translate the example code to ruby, especially because I couldn't find how to encrypt data in streaming fashion using RSA in ruby. Anyone can help? Thank you in advance.
For everyone who comes up with this problem, here is the answer:
def get_base64_encryption(unix_timestamp, params)
str = "#{unix_timestamp}&#{params.to_json}"
public_key = OpenSSL::PKey::RSA.new(PUBLIC_KEY_PEM)
encrypted_str = ''
io = StringIO.new(str)
until io.eof?
chunk = io.read(200)
encrypted_str << public_key.public_encrypt(chunk, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
end
Base64.strict_encode64(encrypted_str)
end
OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING is equal to RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING source

javax.crypto.BadPaddingException: Decryption Error - Not able to decrypt multiple blocks from file

Hoping somebody can point me in the right direction here.
I'm not particularly familiar with encryption (in Java or otherwise), but I said that I'd help somebody out with a task they have that specifies using RSA encrypt/decrypt.
To familiarise myself with what is trying to be accomplished, I've put together a little example program that:
Generates 6 random numbers, and puts them in a comma-separated string
Encrypts the sequence of numbers
Appends the encrypted numbers sequence to a file - the same file can be written to more than once if the program is run multiple times
Reads the encrypted file - should get all sequences that have been written
Decrypt each sequence and print out a collection of comma-separated strings
This works fine on the first attempt - i.e. when the first sequence is written into an empty file - and the correct number sequence is returned.
When the file contains more than one encrypted sequence, this causes the decrypt routine to crash with a BadPaddingException. I've done a step-through at this point, and each of the byte[] (representing an encrypted sequence) to be decrypted is 128 bytes, so it's not like an irregular number of bytes is causing the problem.
I know that RSA isn't the recommended way to go about it anymore, but this is the specification that I'm having to get my head around.
I'm also aware that there's a ton of SO questions to do with RSA and BadPaddingException, but I haven't come across one that deals with this issue.
My example code is below:
public class EncryptDecrypt {
private static Cipher cipher;
private static KeyPair keyPair;
public static void main(String[] args)
{
String[] numbers = getNumbers();
String numbersStr = String.join(", ", numbers);
System.out.println("Generated: " + numbersStr + ", NumBytes: " + numbersStr.getBytes().length);
byte[] encryptedNumbers = encrypt(numbersStr);
System.out.println("Encrypted: " + encryptedNumbers.toString() + ", NumBytes: " + encryptedNumbers.length);
writeToFile(encryptedNumbers);
System.out.println("Encrypted numbers written to data.txt");
ArrayList<byte[]> encryptedData = readFromFile();
System.out.println("Encrypted numbers read from data.txt, NumSequences: " + encryptedData.size());
ArrayList<String> decryptedSequences = decrypt(encryptedData);
for (int i = 0; i < decryptedSequences.size(); i++)
{
String sequence = decryptedSequences.get(i);
System.out.println("Sequence " + i + ": " + sequence);
}
}
private static String[] getNumbers()
{
String[] numbers = new String[6];
int min = 1;
int max = 60;
for (int i = 0; i < numbers.length; i++)
{
double number = (Math.random() * (max - min) + min);
numbers[i] = number >= 10 ? Integer.toString((int) number) : "0" + Integer.toString((int) number);
}
return numbers;
}
private static byte[] encrypt(String data)
{
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
cipher.update(data.getBytes());
byte[] encrypted = cipher.doFinal();
return encrypted;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException ex) {
Logger.getLogger(EncryptDecrypt.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
private static void writeToFile(byte[] data)
{
FileOutputStream fileOut = null;
try {
File file = new File("data.txt");
file.createNewFile();
fileOut = new FileOutputStream(file, true);
fileOut.write(data);
fileOut.flush();
fileOut.close();
} catch (FileNotFoundException ex) {
Logger.getLogger(EncryptDecrypt.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(EncryptDecrypt.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
fileOut.close();
} catch (IOException ex) {
Logger.getLogger(EncryptDecrypt.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private static ArrayList<byte[]> readFromFile()
{
File file = new File("data.txt");
if (file.exists())
{
try {
ArrayList<byte[]> encryptedSequences = new ArrayList<>();
FileInputStream fileIn = new FileInputStream(file);
int blockSize = 128;
int numBlocks = fileIn.available() / blockSize;
for (int i = 0; i < numBlocks; i++)
{
byte[] encryptedSequence = new byte[blockSize];
fileIn.read(encryptedSequence);
encryptedSequences.add(encryptedSequence);
}
fileIn.close();
return encryptedSequences;
} catch (FileNotFoundException ex) {
Logger.getLogger(EncryptDecrypt.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(EncryptDecrypt.class.getName()).log(Level.SEVERE, null, ex);
}
}
return null;
}
private static ArrayList<String> decrypt(ArrayList<byte[]> data)
{
try {
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
PrivateKey privateKey = keyPair.getPrivate();
cipher.init(Cipher.DECRYPT_MODE, privateKey);
ArrayList<String> decryptedStrings = new ArrayList<>();
for (byte[] sequence : data)
{
byte[] decryptedBytes = cipher.doFinal(sequence);
String decryptedString = new String(decryptedBytes);
decryptedStrings.add(decryptedString);
}
return decryptedStrings;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException ex) {
Logger.getLogger(EncryptDecrypt.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
}
If anyone can spot what's wrong with this, I'd really appreciate it!
Thanks

Android AES encryption with python AES encryption

I have created the following python code
import base64
from django.conf import settings
from Crypto.Cipher import AES
BS = 16
def pad(s):
return s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
def unpad(s):
return s[0:-s[-1]]
class AESCipher:
def __init__(self):
self.key = settings.SECRET_KEY
def encrypt(self, raw):
raw = pad(raw)
iv = settings.SECRET_IV.encode('utf-8')
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return base64.b64encode(cipher.encrypt(raw))
def decrypt(self, enc):
enc = base64.b64decode(enc)
iv = settings.SECRET_IV.encode('utf-8')
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return unpad(cipher.decrypt(enc))
def encryptstring(self, raw):
raw = self.encrypt(raw)
raw = raw.decode('utf-8')
return raw
def decryptstring(self, raw):
raw = raw.encode('utf-8')
raw = self.decrypt(raw)
raw = raw.decode('utf-8')
return raw
This code returns a base64 encrypted string (i.e. VgRaS+J3MSmguabaf+9fJw==).
I have also created a java function, that should create the same thing, but it doesn't match.
public void login() {
SecretKeySpec keySpec;
IvParameterSpec ivSpec;
Cipher cipher;
String key = "";
String iv = "";
try {
keySpec = new SecretKeySpec(key.getBytes(), "AES");
ivSpec = new IvParameterSpec(iv.getBytes());
cipher = Cipher.getInstance("AES/CBC/NoPadding");
String test = "Test";
String newString = "";
char paddingChar = ' ';
int size = 16;
int x = test.length() % size;
int padLength = size - x;
for (int i = 0; i < padLength; i++) {
newString += paddingChar;
// test += paddingChar;
}
newString += test;
byte[] res;
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
res = cipher.doFinal(newString.getBytes());
for(int i = 0; i < res.length; i++) {
System.out.print(res[i] + ", ");
}
System.out.println("");
System.out.println(bytesToHex(res));
String base = Base64.encodeToString(res, Base64.DEFAULT);
System.out.println(base);
// TODO: VgRaS+J3MSmguabaf+9fJw==
// OyuUHNsBQ3Zuy4UGY4fUdQ==
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
}
The result I get there is 2mdRXydTCAYG+Tp0kE/NoQ==. It looks similar, but it isn't. I have checked the keys, they are the same. I have swapped them around, to make sure that wasn't the problem either. I have tried different cipher modes (AES/CBC/ISO10126Padding and AES/CBC/PKCS5Padding) both without result. I have tried to do it with plain AES, no success either. I tried to get AES_128/CBC/NoPadding and AES_256/CBC/NoPadding to work, but for some reason it tells me java.security.NoSuchAlgorithmException: No provider found for AES_256/CBC/NoPadding.
I am lost, I hope someone can help! Thanks in advance!
As JamesKPolk pointed out, the padding was wrong. I have solved this by doing the following:
Python has the following
def pad(s):
return s.ljust(16, '0')
Java has the following
char paddingChar = '0';

Encrypt string in android and decrypt in node.js is not working

I have to encrypt data in Android app and decrypt in Node.js. But in web-end decryption is not working.
I need to Implement AES256 encryption and in web-end cryptoJS encryption library is used.
This is the string to be encrypted: {"email":"jhon#gmail.com","device_type":"iOS","password":"123456","device_id":"14105DA4-CEE5-431E-96A2-2331CDA7F062","device_token":"B44777563552882EC3139A0317E401B55D6FC699D0AC3D279F392927CAF9B566"}
And result should be
"U2FsdGVkX19hTYqle0GhmGoOK2oKGPfPXDkKsH8MdANABQROPiQ7hxnSA289rRT4fds5yZG4FX7BoTKqzV40NLIpeBoRJw6Uazax8kiKyxRYDUBPNpevD7vveDLoWekRL2cLZTV3S0gkynhL\/fKMg7BUFfT3Lt9BZeiFxYKZKD++ul5DG1isRycwG06l5ZgQJDwfyNAlRz7WbJXrevvCQPMagoCqrB4i49Mgj2ANMLFzUDyImhn9gX3PGrlzlY8xgzhg2oQfHF\/5USd5pqXswTRzVhGxUoUhNahf3pFZtbscr0AyvvE8ffF6ftRo4ZLYRt72aB9Br\/vqshXxr9VYuQ=="
Currenly I am using below code:
// Encrypt where jo is input, and query is output and ENCRPYTION_KEy is key
String secret = "LSC#SD2017#ps";
byte[] input = new byte[0];
String query = null;
try {
input = cipherText1.getBytes();
byte[] cipherData = Base64.decode(cipherText1.getBytes(), Base64.NO_WRAP);
byte[] saltData = Arrays.copyOfRange(cipherData, 8, 16);
MessageDigest md5 = MessageDigest.getInstance("MD5");
final byte[][] keyAndIV = GenerateKeyAndIV(32, 16, 1, saltData, secret.getBytes("UTF-8"), md5);
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(secret.getBytes("UTF-8"));
SecretKeySpec skc = new SecretKeySpec(thedigest, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, skc);
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
ctLength += cipher.doFinal(cipherText, ctLength);
query = Base64.encodeToString(cipherText, Base64.NO_WRAP);
System.out.println("NEw code\n" + query);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (ShortBufferException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
In iOS code is perfectly working and they are using : https://github.com/etienne-martin/CryptoJS.swift
Please help me getting same output.

how to generate ValueLink merchant working keys

I am trying to generate ValueLink merchant working keys using a modified version of the apache ofbiz ValueLinkApi Class - src
I've modified it only to the extent of making it a standalone class that I can run from outside the context of the ofbiz framework.
My program runs without error but my keys are not being accepted by the api.
Have you ever implemented this? Did you use this library or something else like openSSL? If you used this library did you have to modify it at all?
heres my version:
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyAgreement;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.interfaces.DHPrivateKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.DHPrivateKeySpec;
import javax.crypto.spec.DHPublicKeySpec;
import javax.crypto.spec.IvParameterSpec;
import org.ofbiz.base.util.Debug;
import org.ofbiz.base.util.StringUtil;
public class diffHel {
public static final String module = "diffHel";
//were in props
public String publicValue = "6B0276780D3E07911D744F545833005E8C2F755E0FE59A8660527F7B7E070A45EEB853DA70C6EFE2B8BF278F0B4A334A49DF0985635745A3DAD2E85A9C0EEFAE657CC382A0B3EAE9C3F85B0A2305282612CFD2857801131EC9FE313DB9DADFB914A30EE077E8A97E5574CE5BD56661B021C39116913710947FAA38FFCB4FC045";
public String format = "ddMMyyyyHHmmss";
public String primeHex = "e516e43e5457b2f66f6ca367b335ead8319939fa4df6c1b7f86e73e922a6d19393255e419096668174e35c818a66117f799e8666c8050ee436f9801351606c55d45faba03f39e2923ba926a9cd75d4bdbca9de78b62a9b847a781c692c063eaacb43a396f01d121d042755d0b7c0b2dfa8b498a57e4d90c30ca049a7ac2b7f73";
public String genString = "05";
//were generic values
public String exchangeKey;
public String privateKey = "";
protected SecretKey kek = null;
protected SecretKey mwk = null;
protected String merchantId = null;
protected String terminalId = null;
protected Long mwkIndex = null;
protected boolean debug = false;
public StringBuffer buf = new StringBuffer();
public diffHel() {}
/**
* Output the creation of public/private keys + KEK to the console for manual database update
*/
public StringBuffer outputKeyCreation(boolean kekOnly, String kekTest) {
return this.outputKeyCreation(0, kekOnly, kekTest);
}
private StringBuffer outputKeyCreation(int loop, boolean kekOnly, String kekTest) {
//StringBuffer buf = new StringBuffer();
loop++;
if (loop > 100) {
// only loop 100 times; then throw an exception
throw new IllegalStateException("Unable to create 128 byte keys in 100 tries");
}
// place holder for the keys
DHPrivateKey privateKey = null;
DHPublicKey publicKey = null;
if (!kekOnly) {
KeyPair keyPair = null;
try {
keyPair = this.createKeys();
} catch (NoSuchAlgorithmException e) {
Debug.logError(e, module);
} catch (InvalidAlgorithmParameterException e) {
Debug.logError(e, module);
} catch (InvalidKeySpecException e) {
Debug.logError(e, module);
}
if (keyPair != null) {
publicKey = (DHPublicKey) keyPair.getPublic();
privateKey = (DHPrivateKey) keyPair.getPrivate();
buf.append("privateKeyLenth=" + privateKey.getX().toByteArray().length);
if (publicKey == null || publicKey.getY().toByteArray().length != 128) {
// run again until we get a 128 byte public key for VL
return this.outputKeyCreation(loop, kekOnly, kekTest);
}
} else {
Debug.log("Returned a null KeyPair", module);
return this.outputKeyCreation(loop, kekOnly, kekTest);
}
} else {
// use our existing private key to generate a KEK
try {
privateKey = (DHPrivateKey) this.getPrivateKey();
} catch (Exception e) {
Debug.logError(e, module);
}
}
// the KEK
byte[] kekBytes = null;
try {
kekBytes = this.generateKek(privateKey);
} catch (NoSuchAlgorithmException e) {
Debug.logError(e, module);
} catch (InvalidKeySpecException e) {
Debug.logError(e, module);
} catch (InvalidKeyException e) {
Debug.logError(e, module);
}
// the 3DES KEK value
SecretKey loadedKek = this.getDesEdeKey(kekBytes);
byte[] loadKekBytes = loadedKek.getEncoded();
// test the KEK
//Cipher cipher = this.getCipher(this.getKekKey(), Cipher.ENCRYPT_MODE);
Cipher cipher = this.getCipher(loadedKek, Cipher.ENCRYPT_MODE);
byte[] kekTestB = { 0, 0, 0, 0, 0, 0, 0, 0 };
byte[] kekTestC = new byte[0];
if (kekTest != null) {
kekTestB = StringUtil.fromHexString(kekTest);
}
// encrypt the test bytes
try {
kekTestC = cipher.doFinal(kekTestB);
} catch (Exception e) {
Debug.logError(e, module);
}
kek = loadedKek;
byte[] newMwk = generateMwk(loadedKek);
byte[] decyptedMwk = decryptViaKek(newMwk);
if (!kekOnly) {
// public key (just Y)
BigInteger y = publicKey.getY();
byte[] yBytes = y.toByteArray();
String yHex = StringUtil.toHexString(yBytes);
buf.append("======== Begin Public Key (Y # " + yBytes.length + " / " + yHex.length() + ") ========\n");
buf.append(yHex + "\n");
buf.append("======== End Public Key ========\n\n");
// private key (just X)
BigInteger x = privateKey.getX();
byte[] xBytes = x.toByteArray();
String xHex = StringUtil.toHexString(xBytes);
buf.append("======== Begin Private Key (X # " + xBytes.length + " / " + xHex.length() + ") ========\n");
buf.append(xHex + "\n");
buf.append("======== End Private Key ========\n\n");
// private key (full)
byte[] privateBytes = privateKey.getEncoded();
String privateHex = StringUtil.toHexString(privateBytes);
buf.append("======== Begin Private Key (Full # " + privateBytes.length + " / " + privateHex.length() + ") ========\n");
buf.append(privateHex + "\n");
buf.append("======== End Private Key ========\n\n");
}
if (kekBytes != null) {
buf.append("======== Begin KEK aka decrypted MWK (" + kekBytes.length + ") ========\n");
buf.append(StringUtil.toHexString(kekBytes) + "\n");
buf.append("======== End KEK ========\n\n");
buf.append("======== Begin KEK (DES) (" + loadKekBytes.length + ") ========\n");
buf.append(StringUtil.toHexString(loadKekBytes) + "\n");
buf.append("======== End KEK (DES) ========\n\n");
buf.append("======== Begin KEK Test (" + kekTestC.length + ") ========\n");
buf.append(StringUtil.toHexString(kekTestC) + "\n");
buf.append("======== End KEK Test ========\n\n");
} else {
Debug.logError("KEK came back empty", module);
}
if (newMwk != null) {
buf.append("======== Begin MWK (" + newMwk.length + ") ========\n");
buf.append(StringUtil.toHexString(newMwk) + "\n");
buf.append("======== End MWK ========\n\n");
}
if (decyptedMwk != null) {
buf.append("======== Begin Decrypted MWK (" + decyptedMwk.length + ") ========\n");
buf.append(StringUtil.toHexString(decyptedMwk) + "\n");
buf.append("======== End Decrypted MWK ========\n\n");
}
return buf;
}
/**
* Create a set of public/private keys using ValueLinks defined parameters
* #return KeyPair object containing both public and private keys
* #throws NoSuchAlgorithmException
* #throws InvalidAlgorithmParameterException
*/
public KeyPair createKeys() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeySpecException {
// initialize the parameter spec
DHPublicKey publicKey = (DHPublicKey) this.getValueLinkPublicKey();
DHParameterSpec dhParamSpec = publicKey.getParams();
//Debug.log(dhParamSpec.getP().toString() + " / " + dhParamSpec.getG().toString(), module);
// create the public/private key pair using parameters defined by valuelink
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DH");
keyGen.initialize(dhParamSpec);
KeyPair keyPair = keyGen.generateKeyPair();
return keyPair;
}
/**
* Generate a key exchange key for use in encrypting the mwk
* #param privateKey The private key for the merchant
* #return byte array containing the kek
* #throws NoSuchAlgorithmException
* #throws InvalidKeySpecException
* #throws InvalidKeyException
*/
public byte[] generateKek(PrivateKey privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
// get the ValueLink public key
PublicKey vlPublic = this.getValueLinkPublicKey();
// generate shared secret key
KeyAgreement ka = KeyAgreement.getInstance("DH");
ka.init(privateKey);
ka.doPhase(vlPublic, true);
byte[] secretKey = ka.generateSecret();
buf.append("======== Secret Key (" + secretKey.length + ") ========\n");
buf.append(StringUtil.toHexString(secretKey) + "\n");
buf.append("======== End Secret Key ========\n\n");
if (debug) {
Debug.log("Secret Key : " + StringUtil.toHexString(secretKey) + " / " + secretKey.length, module);
}
// generate 3DES from secret key using VL algorithm (KEK)
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] digest = md.digest(secretKey);
byte[] des2 = getByteRange(digest, 0, 16);
byte[] first8 = getByteRange(des2, 0, 8);
byte[] kek = copyBytes(des2, first8, 0);
if (debug) {
Debug.log("Generated KEK : " + StringUtil.toHexString(kek) + " / " + kek.length, module);
}
return kek;
}
/**
* Get a public key object for the ValueLink supplied public key
* #return PublicKey object of ValueLinks's public key
* #throws NoSuchAlgorithmException
* #throws InvalidKeySpecException
*/
public PublicKey getValueLinkPublicKey() throws NoSuchAlgorithmException, InvalidKeySpecException {
// read the valuelink public key
//String publicValue = (String) props.get("payment.valuelink.publicValue");
byte[] publicKeyBytes = StringUtil.fromHexString(publicValue);
// initialize the parameter spec
DHParameterSpec dhParamSpec = this.getDHParameterSpec();
// load the valuelink public key
KeyFactory keyFactory = KeyFactory.getInstance("DH");
BigInteger publicKeyInt = new BigInteger(publicKeyBytes);
DHPublicKeySpec dhPublicSpec = new DHPublicKeySpec(publicKeyInt, dhParamSpec.getP(), dhParamSpec.getG());
PublicKey vlPublic = keyFactory.generatePublic(dhPublicSpec);
return vlPublic;
}
/**
* Get merchant Private Key
* #return PrivateKey object for the merchant
*/
public PrivateKey getPrivateKey() throws InvalidKeySpecException, NoSuchAlgorithmException {
byte[] privateKeyBytes = this.getPrivateKeyBytes();
// initialize the parameter spec
DHParameterSpec dhParamSpec = this.getDHParameterSpec();
// load the private key
KeyFactory keyFactory = KeyFactory.getInstance("DH");
BigInteger privateKeyInt = new BigInteger(privateKeyBytes);
DHPrivateKeySpec dhPrivateSpec = new DHPrivateKeySpec(privateKeyInt, dhParamSpec.getP(), dhParamSpec.getG());
PrivateKey privateKey = keyFactory.generatePrivate(dhPrivateSpec);
return privateKey;
}
/**
* Generate a new MWK
* #return Hex String of the new encrypted MWK ready for transmission to ValueLink
*/
public byte[] generateMwk() {
KeyGenerator keyGen = null;
try {
keyGen = KeyGenerator.getInstance("DES");
} catch (NoSuchAlgorithmException e) {
Debug.logError(e, module);
}
// generate the DES key 1
SecretKey des1 = keyGen.generateKey();
SecretKey des2 = keyGen.generateKey();
if (des1 != null && des2 != null) {
byte[] desByte1 = des1.getEncoded();
byte[] desByte2 = des2.getEncoded();
byte[] desByte3 = des1.getEncoded();
// check for weak keys
try {
if (DESKeySpec.isWeak(des1.getEncoded(), 0) || DESKeySpec.isWeak(des2.getEncoded(), 0)) {
return generateMwk();
}
} catch (Exception e) {
Debug.logError(e, module);
}
byte[] des3 = copyBytes(desByte1, copyBytes(desByte2, desByte3, 0), 0);
return generateMwk(des3);
} else {
Debug.log("Null DES keys returned", module);
}
return null;
}
/**
* Generate a new MWK
* #param desBytes byte array of the DES key (24 bytes)
* #return Hex String of the new encrypted MWK ready for transmission to ValueLink
*/
public byte[] generateMwk(byte[] desBytes) {
if (debug) {
Debug.log("DES Key : " + StringUtil.toHexString(desBytes) + " / " + desBytes.length, module);
}
SecretKeyFactory skf1 = null;
SecretKey mwk = null;
try {
skf1 = SecretKeyFactory.getInstance("DESede");
} catch (NoSuchAlgorithmException e) {
Debug.logError(e, module);
}
DESedeKeySpec desedeSpec2 = null;
try {
desedeSpec2 = new DESedeKeySpec(desBytes);
} catch (InvalidKeyException e) {
Debug.logError(e, module);
}
if (skf1 != null && desedeSpec2 != null) {
try {
mwk = skf1.generateSecret(desedeSpec2);
} catch (InvalidKeySpecException e) {
Debug.logError(e, module);
}
}
if (mwk != null) {
return generateMwk(mwk);
} else {
return null;
}
}
/**
* Generate a new MWK
* #param mwkdes3 pre-generated DES3 SecretKey
* #return Hex String of the new encrypted MWK ready for transmission to ValueLink
*/
public byte[] generateMwk(SecretKey mwkdes3) {
// zeros for checksum
byte[] zeros = { 0, 0, 0, 0, 0, 0, 0, 0 };
// 8 bytes random data
byte[] random = new byte[8];
Random ran = new Random();
ran.nextBytes(random);
// open a cipher using the new mwk
Cipher cipher = this.getCipher(mwkdes3, Cipher.ENCRYPT_MODE);
// make the checksum - encrypted 8 bytes of 0's
byte[] encryptedZeros = new byte[0];
try {
encryptedZeros = cipher.doFinal(zeros);
} catch (IllegalStateException e) {
Debug.logError(e, module);
} catch (IllegalBlockSizeException e) {
Debug.logError(e, module);
} catch (BadPaddingException e) {
Debug.logError(e, module);
}
// make the 40 byte MWK - random 8 bytes + key + checksum
byte[] newMwk = copyBytes(mwkdes3.getEncoded(), encryptedZeros, 0);
newMwk = copyBytes(random, newMwk, 0);
if (debug) {
Debug.log("Random 8 byte : " + StringUtil.toHexString(random), module);
Debug.log("Encrypted 0's : " + StringUtil.toHexString(encryptedZeros), module);
Debug.log("Decrypted MWK : " + StringUtil.toHexString(mwkdes3.getEncoded()) + " / " + mwkdes3.getEncoded().length, module);
Debug.log("Encrypted MWK : " + StringUtil.toHexString(newMwk) + " / " + newMwk.length, module);
}
return newMwk;
}
/**
* Use the KEK to encrypt a value usually the MWK
* #param content byte array to encrypt
* #return encrypted byte array
*/
public byte[] encryptViaKek(byte[] content) {
return cryptoViaKek(content, Cipher.ENCRYPT_MODE);
}
/**
* Ue the KEK to decrypt a value
* #param content byte array to decrypt
* #return decrypted byte array
*/
public byte[] decryptViaKek(byte[] content) {
return cryptoViaKek(content, Cipher.DECRYPT_MODE);
}
/**
* Returns a date string formatted as directed by ValueLink
* #return ValueLink formatted date String
*/
public String getDateString() {
//String format = (String) props.get("payment.valuelink.timestamp");
SimpleDateFormat sdf = new SimpleDateFormat(format);
return sdf.format(new Date());
}
// using the prime and generator provided by valuelink; create a parameter object
protected DHParameterSpec getDHParameterSpec() {
//String primeHex = (String) props.get("payment.valuelink.prime");
//String genString = (String) props.get("payment.valuelink.generator");
// convert the p/g hex values
byte[] primeByte = StringUtil.fromHexString(this.primeHex);
BigInteger prime = new BigInteger(1, primeByte); // force positive (unsigned)
BigInteger generator = new BigInteger(this.genString);
// initialize the parameter spec
DHParameterSpec dhParamSpec = new DHParameterSpec(prime, generator, 1024);
return dhParamSpec;
}
// actual kek encryption/decryption code
protected byte[] cryptoViaKek(byte[] content, int mode) {
// open a cipher using the kek for transport
Cipher cipher = this.getCipher(this.getKekKey(), mode);
byte[] dec = new byte[0];
try {
dec = cipher.doFinal(content);
} catch (IllegalStateException e) {
Debug.logError(e, module);
} catch (IllegalBlockSizeException e) {
Debug.logError(e, module);
} catch (BadPaddingException e) {
Debug.logError(e, module);
}
return dec;
}
// return a cipher for a key - DESede/CBC/NoPadding IV = 0
protected Cipher getCipher(SecretKey key, int mode) {
byte[] zeros = { 0, 0, 0, 0, 0, 0, 0, 0 };
IvParameterSpec iv = new IvParameterSpec(zeros);
// create the Cipher - DESede/CBC/NoPadding
Cipher mwkCipher = null;
try {
mwkCipher = Cipher.getInstance("DESede/CBC/NoPadding");
} catch (NoSuchAlgorithmException e) {
Debug.logError(e, module);
return null;
} catch (NoSuchPaddingException e) {
Debug.logError(e, module);
}
try {
mwkCipher.init(mode, key, iv);
} catch (InvalidKeyException e) {
Debug.logError(e, "Invalid key", module);
} catch (InvalidAlgorithmParameterException e) {
Debug.logError(e, module);
}
return mwkCipher;
}
protected SecretKey getKekKey() {
if (kek == null) {
kek = this.getDesEdeKey(getKek());
}
if (debug) {
Debug.log("Raw KEK : " + StringUtil.toHexString(getKek()), module);
Debug.log("KEK : " + StringUtil.toHexString(kek.getEncoded()), module);
}
return kek;
}
protected SecretKey getDesEdeKey(byte[] rawKey) {
SecretKeyFactory skf = null;
try {
skf = SecretKeyFactory.getInstance("DESede");
} catch (NoSuchAlgorithmException e) {
// should never happen since DESede is a standard algorithm
Debug.logError(e, module);
return null;
}
// load the raw key
if (rawKey.length > 0) {
DESedeKeySpec desedeSpec1 = null;
try {
desedeSpec1 = new DESedeKeySpec(rawKey);
} catch (InvalidKeyException e) {
Debug.logError(e, "Not a valid DESede key", module);
return null;
}
// create the SecretKey Object
SecretKey key = null;
try {
key = skf.generateSecret(desedeSpec1);
} catch (InvalidKeySpecException e) {
Debug.logError(e, module);
}
return key;
} else {
throw new RuntimeException("No valid DESede key available");
}
}
protected byte[] getKek() {
//return StringUtil.fromHexString(this.getGenericValue().getString("exchangeKey"));
return StringUtil.fromHexString(this.exchangeKey);
}
protected byte[] getPrivateKeyBytes() {
//return StringUtil.fromHexString(this.getGenericValue().getString("privateKey"));
return StringUtil.fromHexString(this.privateKey);
}
/**
* Returns a new byte[] from the offset of the defined byte[] with a specific number of bytes
* #param bytes The byte[] to extract from
* #param offset The starting postition
* #param length The number of bytes to copy
* #return a new byte[]
*/
public static byte[] getByteRange(byte[] bytes, int offset, int length) {
byte[] newBytes = new byte[length];
for (int i = 0; i < length; i++) {
newBytes[i] = bytes[offset + i];
}
return newBytes;
}
/**
* Copies a byte[] into another byte[] starting at a specific position
* #param source byte[] to copy from
* #param target byte[] coping into
* #param position the position on target where source will be copied to
* #return a new byte[]
*/
public static byte[] copyBytes(byte[] source, byte[] target, int position) {
byte[] newBytes = new byte[target.length + source.length];
for (int i = 0, n = 0, x = 0; i < newBytes.length; i++) {
if (i < position || i > (position + source.length - 2)) {
newBytes[i] = target[n];
n++;
} else {
for (; x < source.length; x++) {
newBytes[i] = source[x];
if (source.length - 1 > x) {
i++;
}
}
}
}
return newBytes;
}
}

Categories