I have an API documentation that requires encrypting a key to authenticate,
I managed to build and compile their sample code, but the results on windows are different than linux.
When I run and test from Windows, all seems to be correct and works with the API.
That same test on Linux outputs a different result. I need it working on Linux since that's the main server.
I am using & running the same jar file on both environments.
This is the key I am trying to encrypt (it's a dynamic key):
2136230$486B91E1BEA5D082BA3601CD803585CE$20140409$20140409$$ABCDEFGH$Reserved$CTC
This is the correct output on Windows (it's obviously quite longer):
F7BE2B7E0CEAD9D09135FCF2A8AEB11E2937D26B33CCBC9B8132A29A3534040C9737B2A8E3F271A9DF6454696CF890F7886223AE9C86F81EF58E41AEAA3D34A80F7089154E64F4FD36E75C25A7C2DA7FF03D21F57DA950F5
This is the wrong output from Linux:
F66D4CE1238B30EE54ABC74966D7AC3064FEA3ADFB9D37548E41509CE4FED9CB1D146651B491F2433169999A85F73DAF9ACD07A090DF3D85477BE4201ADC9E1A0181EA7CB763050A
What is causing this and how to correct it ?
This is the source code of the program to use as we received from the API company:
public class DESUtil
{
private static final String Algorithm = "DESede/ECB/PKCS5Padding";// DESede/ECB/PKCS5Padding;DESede
private static final String DESede = "DESede";
public static byte[] encrypt(byte[] keybyte, byte[] src)
throws NoSuchAlgorithmException, NoSuchPaddingException, Exception
{
SecretKey deskey = new SecretKeySpec(keybyte, DESede);
Cipher c1 = Cipher.getInstance(Algorithm);
c1.init(Cipher.ENCRYPT_MODE, deskey);
return c1.doFinal(src);
}
public static byte[] decrypt(byte[] keybyte, byte[] src)
throws NoSuchAlgorithmException, NoSuchPaddingException, Exception
{
SecretKey deskey = new SecretKeySpec(keybyte, DESede);
Cipher c1 = Cipher.getInstance(Algorithm);
c1.init(Cipher.DECRYPT_MODE, deskey);
return c1.doFinal(src);
}
public static String byte2hex(byte[] b)
{
StringBuffer hs = new StringBuffer();
String stmp = "";
for (int n = 0; n <b.length; n++)
{
stmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
if (stmp.length() == 1)
hs.append("0").append(stmp);
else
hs.append(stmp);
}
return hs.toString().toUpperCase(Locale.getDefault());
}
public static byte[] hex2byte(String hexStr)
{
if (hexStr.length() % 2 != 0)
{
AppLogger.error("hex2bytes's hexStr length is not even.");
return null;
}
byte[] toBytes = new byte[hexStr.length() / 2];
for (int i = 0, j = 0; i <hexStr.length(); j++, i = i + 2)
{
int tmpa = Integer.decode(
"0X" + hexStr.charAt(i) + hexStr.charAt(i + 1)).intValue();
toBytes[j] = (byte) (tmpa & 0XFF);
}
return toBytes;
}
public static void main(String[] args)
{
Security.addProvider(new com.sun.crypto.provider.SunJCE());
final byte[] rawKey = "db90e7eb".getBytes();
final byte[] keyBytes = new byte[24];
for (int i = 0; i <rawKey.length; i++)
{
keyBytes[i] = rawKey[i];
}
for (int i = rawKey.length; i <keyBytes.length; i++)
{
keyBytes[i] = (byte)0;
}
String szSrc = "20926330$AD75B1697FB5EB6345B2D412124030D2$10086$10086$10.164.111$ABCDEFGH$Reserved$CTC";
System.out.println("string before encrypt:" + szSrc);
byte[] encoded = null;
try
{
encoded = encrypt(keyBytes, szSrc.getBytes());
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println("string after encrypt::" + byte2hex(encoded));
byte[] srcBytes = null;
try
{
srcBytes = decrypt(keyBytes, encoded);
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println("string before decode: :" + (new String(srcBytes)));
}
}
Almost certainly your use of szSrc.getBytes() which uses the platform's default character encoding.
Try szSrc.getBytes("ISO-8859-1") as a starter if it's working on Windows, but if this string comes from an external service you should determine the encoding scheme dynamically (eg. if it comes through a Servlet use httpRequest.getCharacterEncoding()).
Related
I have an android application that uses BouncyCastle for encryption and decryption. It has a database that there are some string in it that encrypted with BouncyCastle, I need to decompile that and get some decryption from that, I'm doing this with apktool and it gives me success decompile, I'm changing some codes and add log.d to it to use to decrypt it and then after compile that, it successfully compiled, Now while I'm using it and I wanna try to get log from it to know what is that string, it says mac check in GCM failed. what should I do?
below the codes of that application:
the text to decrypt:
IBbdPw==
for encryption:
public static int KeyBitSize = 16;
public static int MacBitSize = 32;
public static int NonceBitSize = 16;
public static String encrypt(String str, byte[] bArr, byte[] bArr2) {
String str2 = "";
try {
byte[] bytes = str.getBytes("UTF-8");
GCMBlockCipher gCMBlockCipher = new GCMBlockCipher(new AESFastEngine());
gCMBlockCipher.init(true, new AEADParameters(new KeyParameter(bArr), MacBitSize, bArr2, null));
byte[] bArr3 = new byte[gCMBlockCipher.getOutputSize(bytes.length)];
gCMBlockCipher.doFinal(bArr3, gCMBlockCipher.processBytes(bytes, 0, bytes.length, bArr3, 0));
return Base64.encodeToString(bArr3, 0);
} catch (UnsupportedEncodingException | IllegalArgumentException | IllegalStateException | DataLengthException | InvalidCipherTextException e) {
System.out.println(e.getMessage());
return str2;
}
}
for decryption:
public static String decrypt(String str, byte[] bArr, byte[] bArr2) {
String str2 = "";
try {
byte[] decode = Base64.decode(str, 0);
GCMBlockCipher gCMBlockCipher = new GCMBlockCipher(new AESFastEngine());
gCMBlockCipher.init(false, new AEADParameters(new KeyParameter(bArr), MacBitSize, bArr2, null));
byte[] bArr3 = new byte[gCMBlockCipher.getOutputSize(decode.length)];
gCMBlockCipher.doFinal(bArr3, gCMBlockCipher.processBytes(decode, 0, decode.length, bArr3, 0));
return new String(bArr3, Charset.forName("UTF-8"));
} catch (IllegalArgumentException | IllegalStateException | DataLengthException | InvalidCipherTextException e) {
System.out.println(e.getMessage());
return str2;
}
}
Updated:
it is using the code like below to decrypt:
byte[] HexToByte(String str) {
int length = str.length();
byte[] bArr = new byte[(length / 2)];
for (int i = 0; i < length; i += 2) {
bArr[i / 2] = (byte) ((Character.digit(str.charAt(i), 16) << 4) + Character.digit(str.charAt(i + 1), 16));
}
return bArr;
}
String arg_1="IBbdPw==";
String arg_2="C6704D668D64DD012A86858C36F35D46F3";
String arg_3="58EB2DB3E1063FC93A";
decrypt(arg_1, HexToByte(arg_2.substring(0, 32)), HexToByte(arg_3.substring(0, 16)));
while arg_2 is changing while application run by its sign key
I am using the below configuration to add data source to my WAR in a WildFly swarm:
swarm:
datasources:
data-sources:
MyDS:
driver-name: oracle
connection-url: <my-connection-string>
user-name: <my-user-name>
password: <my-password-in-clear-text>
How can this be changed so that the password is encrypted and not in clear text?
Here is my working example for Oracle12 with SecureIdentityLoginModule:
swarm:
datasources:
data-sources:
<your-datasoure-name>:
driver-name: oracle
connection-url: jdbc:oracle:thin:#<your-oracle-ip>:<your-oracle-port>:<your-oracle-sid>
security-domain: myEncryptedDs
security:
security-domains:
myEncryptedDs:
classic-authentication:
login-modules:
myLoginModule:
code: org.picketbox.datasource.security.SecureIdentityLoginModule
flag: required
module-options:
username: <your-oracle-username>
password: <your-encrypted-oracle-password>
With the following command you can encrypt your password (the two jar-libraries can be found in your created wildfly-swarm-war-File):
java -cp <your-path-to-wildfly-jars>\picketbox-4.9.6.Final.jar;<your-path-to-wildfly-jars>\logging-2017.11.0.jar:$CLASSPATH org.picketbox.datasource.security.SecureIdentityLoginModule <your-password>
You need to implements org.wildfly.swarm.spi.api.ConfigurationFilter.
Your class will be called for each property of your file. You can change the value at the same time. Here an example how to decrypt your value. You must provide a key (jvm startup) to decrypt your value.
public class DecryptPropertyFilter implements ConfigurationFilter {
private EncryptionHelper encryptionHelper;
{
try {
encryptionHelper = new EncryptionHelper(System.getProperty("encryption.key"));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
#Override
#SuppressWarnings("unchecked")
public <T> T filter(String key, T value) {
if (value instanceof String) {
String str = value.toString();
if (str.startsWith("ENC(") && str.endsWith(")")) {
try {
value = (T) encryptionHelper.decrypt(str.substring(4, str.length() - 1));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
return value;
}
}
For thorntail.
thorntail:
datasources:
data-sources:
myDS:
use-java-context: true
statistics-enabled: true
driver-name: mysql
connection-url: ${db.url}
security-domain: mydbSecure
jndi-name: java:/myDS
check-valid-connection-sql: select 1
valid-connection-checker-class-name: org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker
validate-on-match: false
background-validation: true
background-validation-millis: 10000
use-fast-fail: true
min-pool-size: 5
max-pool-size: 10
prefill: true
flush-strategy: FailingConnectionOnly
exception-sorter-class-name: org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter
security:
security-domains:
mydbSecure:
classic-authentication:
login-modules:
default:
module-options:
username: ${ds.uname}
password: ${ds.pass}
flag: required
code: org.picketbox.datasource.security.SecureIdentityLoginModule
cache-type: default
This is how you would encode the password
public class EncodePassword {
public static void main(String[] args) throws Exception
{
String password = "password";
String encode = encode(password);
System.out.println("Encoded password: "+encode);
}
private static String encode(String secret) throws NoSuchPaddingException, NoSuchAlgorithmException,
InvalidKeyException, BadPaddingException, IllegalBlockSizeException
{
byte[] kbytes = "jaas is the way".getBytes();
SecretKeySpec key = new SecretKeySpec(kbytes, "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encoding = cipher.doFinal(secret.getBytes());
BigInteger n = new BigInteger(encoding);
return n.toString(16);
}
}
The below is how you would decode the password.
public class DecodePassword {
public static void main(String[] args) throws Exception {
String value = "5dfc52b51bd35553df8592078de921bc";
try {
System.out.println(decode(value));
} catch (Exception io) {
io.printStackTrace();
}
}
public static char[] decode(String secret)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
byte[] kbytes = "jaas is the way".getBytes();
SecretKeySpec key = new SecretKeySpec(kbytes, "Blowfish");
BigInteger n = new BigInteger(secret, 16);
byte[] encoding = n.toByteArray();
//SECURITY-344: fix leading zeros
if (encoding.length % 8 != 0) {
int length = encoding.length;
int newLength = ((length / 8) + 1) * 8;
int pad = newLength - length; //number of leading zeros
byte[] old = encoding;
encoding = new byte[newLength];
for (int i = old.length - 1; i >= 0; i--) {
encoding[i + pad] = old[i];
}
//SECURITY-563: handle negative numbers
if (n.signum() == -1) {
for (int i = 0; i < newLength - length; i++) {
encoding[i] = (byte) -1;
}
}
}
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decode = cipher.doFinal(encoding);
return new String(decode).toCharArray();
}
}
for more information regarding picketBox.
https://source.jboss.org/browse/PicketBox/trunk/security-jboss-sx/jbosssx/src/main/java/org/picketbox/datasource/security/SecureIdentityLoginModule.java?r=276
Since yesterday when I posted the frist time I learned a lot. So what I am trying to do now is to implement user console input instead of the given values, written in bold, encrypt, save them in the file and then keep verifying next user input with the encrypted values saved in the file until correct or up to 10 times.
String openPwd = "my password is datasecurity";
String openUser = "a user is ME";
The first question is: can I implement second user input and verification in the same main method of the class?
Moreover I encrypt values with two way encryption AES (Now I know that is not the safest way to encrypt) and one way encryption with hash and salt would be the safest option due to the number of reasons. Also I save password and key in the system file, since setting up database would be too time consuming for the task.
The second question is: can I use PBKDF2 and salt instead of encryption with AES if I do not save password and user name in dbms, but in system file instead? How will verification process in encryption case and PBKDF2 with salt differ?
public class PasswordEncryption {
public static final String AES = "AES";
public static String encrypt(String value, File keyFile)
throws GeneralSecurityException, IOException {
if (!keyFile.exists()) {
KeyGenerator keyGen = KeyGenerator
.getInstance(PasswordEncryption.AES);
keyGen.init(128);
SecretKey sk = keyGen.generateKey();
FileWriter fw = new FileWriter(keyFile);
fw.write(byteArrayToHexString(sk.getEncoded()));
fw.flush();
fw.close();
}
SecretKeySpec sks = getSecretKeySpec(keyFile);
Cipher cipher = Cipher.getInstance(PasswordEncryption.AES);
cipher.init(Cipher.ENCRYPT_MODE, sks, cipher.getParameters());
byte[] encrypted = cipher.doFinal(value.getBytes());
return byteArrayToHexString(encrypted);
}
public static String decrypt(String message, File keyFile)
throws GeneralSecurityException, IOException {
SecretKeySpec sks = getSecretKeySpec(keyFile);
Cipher cipher = Cipher.getInstance(PasswordEncryption.AES);
cipher.init(Cipher.DECRYPT_MODE, sks);
byte[] decrypted = cipher.doFinal(hexStringToByteArray(message));
return new String(decrypted);
}
private static SecretKeySpec getSecretKeySpec(File keyFile)
throws NoSuchAlgorithmException, IOException {
byte[] key = readKeyFile(keyFile);
SecretKeySpec sks = new SecretKeySpec(key, PasswordEncryption.AES);
return sks;
}
private static byte[] readKeyFile(File keyFile)
throws FileNotFoundException {
#SuppressWarnings("resource")
Scanner scanner = new Scanner(keyFile).useDelimiter("\\Z");
String keyValue = scanner.next();
scanner.close();
return hexStringToByteArray(keyValue);
}
private static String byteArrayToHexString(byte[] b) {
StringBuffer sb = new StringBuffer(b.length * 2);
for (int i = 0; i < b.length; i++) {
int v = b[i] & 0xff;
if (v < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(v));
}
return sb.toString().toUpperCase();
}
private static byte[] hexStringToByteArray(String s) {
byte[] b = new byte[s.length() / 2];
for (int i = 0; i < b.length; i++) {
int index = i * 2;
int v = Integer.parseInt(s.substring(index, index + 2), 16);
b[i] = (byte) v;
}
return b;
}
public static void main(String[] args) throws Exception {
final String KEY_FILE = "/Users/xxx/key";
final String PASSWORD_FILE = "/Users/xxx/properties";
String openPwd = "my password is datasecurity";
String openUser = "a user is ME";
Properties p1 = new Properties();
String encryptedPwd = PasswordEncryption.encrypt(openPwd, new File(
KEY_FILE));
String encryptedUser = PasswordEncryption.encrypt(openUser, new File(
KEY_FILE));
p1.put("password",encryptedPwd);
p1.put("user",encryptedUser);
p1.store(new FileWriter(PASSWORD_FILE),"");
// ==================
Properties p2 = new Properties();
p2.load(new FileReader(PASSWORD_FILE));
encryptedPwd = p2.getProperty("password");
encryptedUser = p2.getProperty("user");
System.out.println(encryptedPwd);
System.out.println(encryptedUser);
System.out.println(PasswordEncryption.decrypt(encryptedPwd, new File(
KEY_FILE)));
System.out.println(PasswordEncryption.decrypt(encryptedUser, new File(
KEY_FILE)));
}
}
There are two simple ways of doing this. The first would be to create a static class level variable in your first class that contains the value of the encryptedPwd. To make a class level variable you would define it like this:
public class FirstClass {
public static String encryptedPwd = "";
public static void main(String[] args){
...
FirstClass.encryptedPwd = encryptedPwd; //Set the class level variable equal to the encrypted password
...
}
}
Then you could access it from your Authenticate class using:
if (FirstClass.encryptedPwd.equals(inputHash)) //Getting the password variable in Authenticate class
or you could create a static method to access it as well which would allow you to keep the variable private.
public static String getPassword(){ //Static Method inside your first class that returns the encrypted password
return encryptedPwd;
}
if(FirstClass.getPassword().equals(inputHash)) //Method call in Authenticate class
There are other options too, but it depends on what your doing and the design you want for your project
Can someone figure out why the output of these (php and java) snippets of code don't return the same SHA512 for the same input?
$password = 'whateverpassword';
$salt = 'ieerskzcjy20ec8wkgsk4cc8kuwgs8g';
$salted = $password.'{'.$salt.'}';
$digest = hash('sha512', $salted, true);
echo "digest: ".base64_encode($digest);
for ($i = 1; $i < 5000; $i++) {
$digest = hash('sha512', $digest.$salted, true);
}
$encoded_pass = base64_encode($digest);
echo $encoded_pass;
This is the code on the android application:
public String processSHA512(String pw, String salt, int rounds)
{
try {
md = MessageDigest.getInstance("SHA-512");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("No Such Algorithm");
}
String result = hashPw(pw, salt, rounds);
System.out.println(result);
return result;
}
private static String hashPw(String pw, String salt, int rounds) {
byte[] bSalt;
byte[] bPw;
String appendedSalt = new StringBuilder().append('{').append(salt).append('}').toString();
try {
bSalt = appendedSalt.getBytes("ISO-8859-1");
bPw = pw.getBytes("ISO-8859-1");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Unsupported Encoding", e);
}
byte[] digest = run(bPw, bSalt);
Log.d(LCAT, "first hash: " + Base64.encodeBytes(digest));
for (int i = 1; i < rounds; i++) {
digest = run(digest, bSalt);
}
return Base64.encodeBytes(digest);
}
private static byte[] run(byte[] input, byte[] salt) {
md.update(input);
return md.digest(salt);
}
The library for base64 encoding is this: base64lib
This java code is actually some modified code I found around another question in StackOverflow.
Although the Android code is running fine it doesn't match with the output from the php script. It doesn't even match the first hash!
Note 1: On php hash('sha512',$input, $raw_output) returns raw binary output
Note 2: On java I tried to change the charset (UTF-8, ASCII) but it also didn't work.
Note 3: The code from the server can not be changed, so I would appreciate any answer regarding how to change my android code.
The first hash should be the same on the server and in Java. But then in the loop what gets appended to the digest is password{salt} in the PHP code, but only {salt} in the Java code.
For the lazy ones, one example better than a thousand words ;). I finally understood what was happening. The method update appends bytes to the digest, so when you append $password.{$salt} is the same as doing mda.update(password bytes) and the mda.digest("{$salt}" bytes. I do that answer because I was going crazy finding why it was not working and it was all in this answer.
Thanks guys.
This is the example that works in a Java Server:
public static String hashPassword(String password, String salt) throws Exception {
String result = password;
String appendedSalt = new StringBuilder().append('{').append(salt).append('}').toString();
String appendedSalt2 = new StringBuilder().append(password).append('{').append(salt).append('}').toString();
if(password != null) {
//Security.addProvider(new BouncyCastleProvider());
MessageDigest mda = MessageDigest.getInstance("SHA-512");
byte[] pwdBytes = password.getBytes("UTF-8");
byte[] saltBytes = appendedSalt.getBytes("UTF-8");
byte[] saltBytes2 = appendedSalt2.getBytes("UTF-8");
byte[] digesta = encode(mda, pwdBytes, saltBytes);
//result = new String(digesta);
System.out.println("first hash: " + new String(Base64.encode(digesta),"UTF-8"));
for (int i = 1; i < ROUNDS; i++) {
digesta = encode(mda, digesta, saltBytes2);
}
System.out.println("last hash: " + new String(Base64.encode(digesta),"UTF-8"));
result = new String(Base64.encode(digesta));
}
return result;
}
private static byte[] encode(MessageDigest mda, byte[] pwdBytes,
byte[] saltBytes) {
mda.update(pwdBytes);
byte [] digesta = mda.digest(saltBytes);
return digesta;
}
all files in ~/Cipher/nsdl/crypto can be found here
java files compiled with gcj, see compile.sh
nmint#nqmk-mint ~/Cipher/nsdl/crypto $ echo test | ./cryptTest encrypt deadbeefdeadbeefdeadbeefdeadbeef deadbeef Blowfish CBC > test
null
Exception in thread "main" java.lang.IllegalStateException: cipher is not for encrypting or decrypting
at javax.crypto.Cipher.update(libgcj.so.81)
at javax.crypto.CipherOutputStream.write(libgcj.so.81)
at nsdl.crypto.BlockCrypt.encrypt(cryptTest)
at nsdl.crypto.cryptTest.main(cryptTest)
BlockCrypt.java:
package nsdl.crypto;
import java.io.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class BlockCrypt {
Cipher ecipher;
Cipher dcipher;
byte[] keyBytes;
byte[] ivBytes;
SecretKey key;
AlgorithmParameterSpec iv;
byte[] buf = new byte[1024];
BlockCrypt(String keyStr, String ivStr, String algorithm, String mode) {
try {
ecipher = Cipher.getInstance(algorithm + "/" + mode + "/PKCS5Padding");
dcipher = Cipher.getInstance(algorithm + "/" + mode + "/PKCS5Padding");
keyBytes = hexStringToByteArray(keyStr);
ivBytes = hexStringToByteArray(ivStr);
key = new SecretKeySpec(keyBytes, algorithm);
iv = new IvParameterSpec(ivBytes);
ecipher.init(Cipher.ENCRYPT_MODE, key, iv);
dcipher.init(Cipher.DECRYPT_MODE, key, iv);
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
public void encrypt(InputStream in, OutputStream out) {
try {
// out: where the plaintext goes to become encrypted
out = new CipherOutputStream(out, ecipher);
// in: where the plaintext comes from
int numRead = 0;
while ((numRead = in.read(buf)) >= 0) {
out.write(buf, 0, numRead);
}
out.close();
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
public void decrypt(InputStream in, OutputStream out) {
try {
// in: where the plaintext come from, decrypted on-the-fly
in = new CipherInputStream(in, dcipher);
// out: where the plaintext goes
int numRead = 0;
while ((numRead = in.read(buf)) >= 0) {
out.write(buf, 0, numRead);
}
out.flush();
out.close();
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
}
cryptTest.java:
package nsdl.crypto;
import nsdl.crypto.BlockCrypt;
public class cryptTest {
public static void main (String args[]) {
if (args.length != 5) {
System.err.println("Usage: cryptTest (encrypt|decrypt) key iv algorithm mode");
System.err.println("Takes input from STDIN. Output goes to STDOUT.");
} else {
String operation = args[0];
String key = args[1];
String iv = args[2];
String algorithm = args[3];
String mode = args[4];
BlockCrypt blockCrypt = new BlockCrypt(key, iv, algorithm, mode);
if (operation.equalsIgnoreCase("encrypt")) {
blockCrypt.encrypt(System.in, System.out);
} else if (operation.equalsIgnoreCase("decrypt")) {
blockCrypt.decrypt(System.in, System.out);
} else {
System.err.println("Invalid operation. Use (encrypt|decrypt).");
}
}
}
}
The Cipher, ecipher, is not initialized, and it throws an IllegalStateException when you try to use it as if it were initialized in ENCRYPT_MODE.
Note your catch block in the constructor of BlockCrypt. It is catching an exception with no message, and printing "null" to System.err. Rather than aborting execution—perhaps by throwing an exception from the constructor—you keep sailing.
Replacing System.err.println(e.getMessage()) with e.printStackTrace() or at least System.err.println(e) should give you more detail. My guess is that ecipher.init() is throwing an exception because you're providing a 32-bit IV instead of 64 bits.
Perhaps looking at the source for javax.crypto.Cipher helps this make sense? I couldn't really figure it out, even finding the error message in the source. Good luck!