I am trying to encrypt an ISO-0 pinblock using the codenameone BouncyCastle lib.
The methods I use to achieve this is as follows:
private static byte[] performEncrypt(byte[] key, String plainText, boolean padding) {
byte[] ptBytes = plainText.getBytes();
BufferedBlockCipher cipher;
if (padding) {
cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new DESedeEngine()));
} else {
cipher = new BufferedBlockCipher(new CBCBlockCipher(new DESedeEngine()));
}
cipher.init(true, new KeyParameter(key));
byte[] rv = new byte[cipher.getOutputSize(ptBytes.length)];
int oLen = cipher.processBytes(ptBytes, 0, ptBytes.length, rv, 0);
try {
cipher.doFinal(rv, oLen);
} catch (CryptoException ce) {
LoggingUtil.error(TAG, ce, "Unexpected Exception");
}
return rv;
}
private static String createIso0PinBlock(String pin, String number) {
...
}
private static String getPaddedData(String data, byte padCharacter) {
String paddedData = ByteUtil.pad(data, (char) padCharacter, 8).toString();
return paddedData;
}
public static String createPinBlockAndEncrypt(String pin, String number) {
LoggingUtil.debug("SecurityUtil", "CREAT PIN BLOCK AND ENCRYPT.. PIN: " + pin + " NUMBER: " + number);
String pb = createIso0PinBlock(pin, number.substring(0, number.length() - 1));
LoggingUtil.debug("SecurityUtil", "PINBLOCK: " + pb);
String padded = getPaddedData(pb, (byte) 0x00);
LoggingUtil.debug("SecurityUtil", "PADDED: " + padded);
byte[] encrypted = performEncrypt(Hex.decode(KEY.getBytes()), new String(ByteUtil.hex2byte(padded)), false);
return ByteUtil.byte2hex(encrypted);
}
In ByteUtil:
public static StringBuilder pad(String data, char padCharacter, int multiplier) {
StringBuilder text = new StringBuilder();
text.append(data);
while (text.length() % multiplier != 0) {
text.append(padCharacter);
}
return text;
}
To give example Log outputs:
[SecurityUtil] CREAT PIN BLOCK AND ENCRYPT.. PIN: 2255 NUMBER: 6284734104205417486
[SecurityUtil] PINBLOCK: 042214FBDFABE8B7
[SecurityUtil] PADDED: 042214FBDFABE8B7
When I run this through a public static void main method, it works as expected, however, when I build this for Android via Codenameone, I get the following error in logcat:
org.bouncycastle.crypto.DataLengthException: data not block size aligned
org.bouncycastle.crypto.BufferedBlockCipher.doFinal(BufferedBlockCipher.java:275)
Despite the padded pinblock being 16 in length (a multiple of 8).
Any help regarding this issue would be appreciated.
Encryption works on binary data, and your pinblock is binary, so keep it that way.
When calling performEncrypt(..) you convert your hex encoded pinblock to a string with new String(ByteUtil.hex2byte(padded)), and inside performEncrypt(...) you convert it to a byte array with byte[] ptBytes = plainText.getBytes();. The problem with this is that not all byte sequences can be mapped correctly back and forth through a string, and you might end up with different data and even different length etc. take a look here
Change your signature of you performEncrypt(..) to:
private static byte[] performEncrypt(byte[] key, byte[] plainText, boolean padding) {
and avoid converting through a string altogether.
Related
I need to implement 256 bit AES encryption for cash flow i have c# answer but the answer is not the same,for a newbie, I am not sure if my direction is correct.
this is my code
public static void main(String[] args) {
String key = "12345678901234567890123456789012";
String hashIv = "1234567890123456";
String value = "MerchantID=3430112RespondType=JSONTimeStamp=1485232229Version=1.4MerchantOrderNo=S_1485232229Amt=40ItemDesc=UnitTest";
String result = encrypt(key, hashIv, value);
System.out.println(result);
System.out.println();
String sha256 = encrySha256("HashKey=" + key + "&" + result + "&HashIV=" + hashIv);
System.out.println(sha256.trim());
}
public static String encrypt(String hashKey, String hashIv, String text) {
try {
SecretKeySpec skeySpec = new SecretKeySpec(hashKey.getBytes("UTF-8"), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(hashIv.getBytes("UTF-8"));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal((text.getBytes("UTF-8")));
String test = bytesToHex(encrypted);
return test.toLowerCase();
} catch (Exception e) {
System.out.println(e.getMessage());
}
return null;
}
public static String bytesToHex(byte[] bytes) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
public static String encrySha256(String value) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(value.getBytes());
byte byteBuffer[] = messageDigest.digest();
StringBuffer strHexString = new StringBuffer();
for (int i = 0; i < byteBuffer.length; i++) {
String hex = Integer.toHexString(0xff & byteBuffer[i]);
if (hex.length() == 1) {
strHexString.append('0');
}
strHexString.append(hex);
}
return strHexString.toString().toUpperCase();
} catch (Exception e) {
}
return null;
}
sample encrypt answer :
ff91c8aa01379e4de621a44e5f11f72e4d25bdb1a18242db6cef9ef07d80b0165e476fd1d
9acaa53170272c82d122961e1a0700a7427cfa1cf90db7f6d6593bbc93102a4d4b9b66d9
974c13c31a7ab4bba1d4e0790f0cbbbd7ad64c6d3c8012a601ceaa808bff70f94a8efa5a4f
984b9d41304ffd879612177c622f75f4214fa
encryptSha256 answer : EA0A6CC37F40C1EA5692E7CBB8AE097653DF3E91365E6A9CD7E91312413C7BB8
this is c# code and this is sample data
[MerchantID] => 3430112 [RespondType] => JSON [TimeStamp] => 1485232229
[Version] => 1.4 [MerchantOrderNo] => S_1485232229 [Amt] => 40 [ItemDesc] =>
UnitTest
public string EncryptAES256(string source)//加密
{
string sSecretKey = "12345678901234567890123456789012";
string iv = "1234567890123456";
byte[] sourceBytes =
AddPKCS7Padding(Encoding.UTF8.GetBytes(source), 32);
var aes = new RijndaelManaged();
aes.Key = Encoding.UTF8.GetBytes(sSecretKey);
aes.IV = Encoding.UTF8.GetBytes(iv);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.None;
ICryptoTransform transform = aes.CreateEncryptor();
return ByteArrayToHex(transform.TransformFinalBlock(sourceBytes, 0,
sourceBytes.Length)).ToLower();
}
private static byte[] AddPKCS7Padding(byte[] data, int iBlockSize)
{
int iLength = data.Length;
byte cPadding = (byte)(iBlockSize - (iLength % iBlockSize));
var output = new byte[iLength + cPadding];
Buffer.BlockCopy(data, 0, output, 0, iLength);
for (var i = iLength; i < output.Length; i++)
output[i] = (byte)cPadding;
return output;
}
private static string ByteArrayToHex(byte[] barray)
{
char[] c = new char[barray.Length * 2];
byte b;
for (int i = 0; i < barray.Length; ++i)
{
b = ((byte)(barray[i] >> 4));
c[i * 2] = (char)(b > 9 ? b + 0x37 : b + 0x30);
b = ((byte)(barray[i] & 0xF));
c[i * 2 + 1] = (char)(b > 9 ? b + 0x37 : b + 0x30);
}
return new string(c);
}
The reason for the different encrypted data is that you compare different plain texts. In your Java code you encrypt the plain text
MerchantID=3430112RespondType=JSONTimeStamp=1485232229Version=1.4MerchantOrderNo=S_1485232229Amt=40ItemDesc=UnitTest
and you compare the encrypted data with your reference data
ff91c8aa01379e4de621a44e5f11f72e4d25bdb1a18242db6cef9ef07d80b0165e476fd1d9acaa53170272c82d122961e1a0700a7427cfa1cf90db7f6d6593bbc93102a4d4b9b66d9974c13c31a7ab4bba1d4e0790f0cbbbd7ad64c6d3c8012a601ceaa808bff70f94a8efa5a4f984b9d41304ffd879612177c622f75f4214fa
However, these reference data correspond to a different plain text. The latter you can easily derive by decrypting the reference data with the C# DecryptAES256-method which provides
MerchantID=3430112&RespondType=JSON&TimeStamp=1485232229&Version=1.4&MerchantOrderNo=S_1485232229&Amt=40&ItemDesc=UnitTest
Here, in contrast to the plain text in your Java code, a &-delimiter is used.
If you use the same plain text the Java encrypt- and the C# EncryptAES256-method provide the same encrypted data (same key, IV and padding supposed; for the latter see EDIT-section).
In the following testcase the plain text from your Java code is used:
encrypt("12345678901234567890123456789012", "1234567890123456", "MerchantID=3430112RespondType=JSONTimeStamp=1485232229Version=1.4MerchantOrderNo=S_1485232229Amt=40ItemDesc=UnitTest")
and
EncryptAES256("MerchantID=3430112RespondType=JSONTimeStamp=1485232229Version=1.4MerchantOrderNo=S_1485232229Amt=40ItemDesc=UnitTest")
both provide the encrypted data:
ff91c8aa01379e4de621a44e5f11f72ef45b7b9f9663d386da51af13f7f3b8f2b1ed4a3b7ac6b7783402193ea1d766e3046b6acf612d62568ccdbc475e5a14d114273735b069464dcc8281f4e5bf8486eb97d31602c3fe79cfe7140d2848413edad9d96fabf54d103f3d7a9b401c83fa5e4f17b10a280df10b3d61f23e69bbb8
which (as expected) differs from your reference data (with the exception of the first block).
EDIT
There is a second issue concerning the padding: Your C# EncryptAES256-method uses a custom padding provided by the C# AddPKCS7Padding-method which pads to a multiple of 32 bytes.
In contrast, your Java encrypt-method uses PKCS5Padding which pads to a multiple of 16 bytes.
Thus, the encrypted data of the Java encrypt- and the C# EncryptAES256-method differ if the length of the plain text is between 16 * n byte and 16 * (n + 1) - 1 byte with even n (0,2,4,...).
For odd n (1,3,5,...) the encrypted data are identical. In the example above the byte array of the plain text has 116 bytes that is n = 7 (112 <= 116 <= 127) and therefore the encrypted data are the same.
If the Java encrypt-method should use the same padding as the C# EncryptAES256-method you additionally have to implement an analogous Java-method e.g.:
private static byte[] addPKCS7Padding(byte[] data, int iBlockSize)
{
int iLength = data.length;
byte cPadding = (byte)(iBlockSize - (iLength % iBlockSize));
byte[] output = new byte[iLength + cPadding];
System.arraycopy(data, 0, output, 0, iLength);
for (int i = iLength; i < output.length; i++)
output[i] = (byte)cPadding;
return output;
}
and in the Java encrypt-method you have to replace:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
with
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
and also
byte[] encrypted = cipher.doFinal(text.getBytes("UTF-8"));
with
byte[] encrypted = cipher.doFinal(addPKCS7Padding(text.getBytes("UTF-8"), 32));
before:
MerchantID=3430112RespondType=JSONTimeStamp=1485232229Version=1.4MerchantOrderNo=S_1485232229Amt=40ItemDesc=UnitTest
After:
MerchantID=3430112&RespondType=JSON&TimeStamp=1485232229&Version=1.4&MerchantOrderNo=S_1485232229&Amt=40&ItemDesc=UnitTest
I only add "&" with each parameter, it's work!!!!
try it!!!
(我只加了&就成功拉,你的代碼沒問題,只是參數要加&而已)
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()).
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;
}
I wanted to know how to encrypt a file or a photo which will eventually uploaded into the dropbox.
As i have research online and only manage to found this code (pasted at the bottom) which only encrypt the password, but I wanted to know how to encrypt a file or a photo which will eventually uploaded into the dropbox instead.
So is there any reference or help or guide on how to write a java programming ( will be using on Eclipse software ) on encrypting a file using triple DES? Thank you so much.
package com.kushal.utils;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class DESEncryption {
private static final String UNICODE_FORMAT = "UTF8";
public static final String DES_ENCRYPTION_SCHEME = "DES";
private KeySpec myKeySpec;
private SecretKeyFactory mySecretKeyFactory;
private Cipher cipher;
byte[] keyAsBytes;
private String myEncryptionKey;
private String myEncryptionScheme;
SecretKey key;
public DESEncryption() throws Exception
{
myEncryptionKey = "ThisIsSecretEncryptionKey";
myEncryptionScheme = DES_ENCRYPTION_SCHEME;
keyAsBytes = myEncryptionKey.getBytes(UNICODE_FORMAT);
myKeySpec = new DESKeySpec(keyAsBytes);
mySecretKeyFactory = SecretKeyFactory.getInstance(myEncryptionScheme);
cipher = Cipher.getInstance(myEncryptionScheme);
key = mySecretKeyFactory.generateSecret(myKeySpec);
}
/**
* Method To Encrypt The String
*/
public String encrypt(String unencryptedString) {
String encryptedString = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] plainText = unencryptedString.getBytes(UNICODE_FORMAT);
byte[] encryptedText = cipher.doFinal(plainText);
BASE64Encoder base64encoder = new BASE64Encoder();
encryptedString = base64encoder.encode(encryptedText);
} catch (Exception e) {
e.printStackTrace();
}
return encryptedString;
}
/**
* Method To Decrypt An Ecrypted String
*/
public String decrypt(String encryptedString) {
String decryptedText=null;
try {
cipher.init(Cipher.DECRYPT_MODE, key);
BASE64Decoder base64decoder = new BASE64Decoder();
byte[] encryptedText = base64decoder.decodeBuffer(encryptedString);
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText= bytes2String(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
/**
* Returns String From An Array Of Bytes
*/
private static String bytes2String(byte[] bytes) {
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
stringBuffer.append((char) bytes[i]);
}
return stringBuffer.toString();
}
/**
* Testing the DES Encryption And Decryption Technique
*/
public static void main(String args []) throws Exception
{
DESEncryption myEncryptor= new DESEncryption();
String stringToEncrypt="Sanjaal.com";
String encrypted=myEncryptor.encrypt(stringToEncrypt);
String decrypted=myEncryptor.decrypt(encrypted);
System.out.println("String To Encrypt: "+stringToEncrypt);
System.out.println("Encrypted Value :" + encrypted);
System.out.println("Decrypted Value :"+decrypted);
}
}
File reading is taken from here
public static byte[] encryptFile(String pFilePath, byte[] pKey) throws GeneralSecurityException, IOException
{
File file = new File(pFilePath);
long length = file.length();
InputStream is = new FileInputStream(file);
// You cannot create an array using a long type.
// It needs to be an int type.
// Before converting to an int type, check
// to ensure that file is not larger than Integer.MAX_VALUE.
if (length > Integer.MAX_VALUE) {
// File is too large
}
// Create the byte array to hold the data
byte[] bytes = new byte[(int)length];
// Read in the bytes
int offset = 0;
int numRead = 0;
while (offset < bytes.length
&& (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
offset += numRead;
}
// Close the input stream and return bytes
is.close();
// Ensure all the bytes have been read in
if (offset < bytes.length) {
throw new IOException("Could not completely read file "+file.getName());
}
SecretKeyFactory lDESedeKeyFactory = SecretKeyFactory.getInstance("DESede");
SecretKey kA = lDESedeKeyFactory.generateSecret(new DESedeKeySpec(pKey));
IvParameterSpec lIVSpec = new IvParameterSpec(new byte[8]);
Cipher desedeCBCCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
desedeCBCCipher.init(Cipher.ENCRYPT_MODE, kA, lIVSpec);
byte[] encrypted = desedeCBCCipher.doFinal(bytes);
return encrypted;
}
A file can probably be read as a string of binary digits (lots of 1's and 0's). You could read the file, then encrypt the string using DES the same way you would a password, outputting the result to a new file.
Decryption would work the same way, reading the encrypted file, decrypting, and outputting to an unencrypted file.
In the decrypt class, I cant get it to correctly decrypt. I've verified in the decrypt class that my salt and my ciphertext still carries the same values before I convert them back to bytes.
The program itself doesn't given an error however when I compile, I'll encrypt, save it to the string encryptPhrase and then decrypt it in the decrypt method and I cant seem to get it to decrypt properly. I've marked in comments where the issue is and gives a badpadding exception, however there is no padding?
The odd thing is... if i take out everything in the decrypt method and return just encryptPhrase, it actually returns the correct plain text.
Any help would be appreciated. Thanks! :)
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.util.*;
public class PasswordEncryption {
private static int ITERATIONS = 1000;
private static String saltString;
private static String ciphertextString;
private static String encryptPhrase;
private static void usage(){
System.err.println("Usage: java PBE -e|-d password text");
System.exit(1);
}
public static void main(String[] args) throws Exception{
scan = new Scanner(System.in);
System.out.println("Please enter plain text: ");
String text = scan.nextLine();
System.out.println("Please enter password to encrypt plain text: ");
char [] pw = scan.nextLine().toCharArray();
int option=3;
while (option!=0){
System.out.println("Are we encrypting(1) or decrypting(2) or Exit(0)? " );
option = scan.nextInt();
String output="exiting program";
if (option == 1)
output = encrypt(pw, text);
else if (option ==2)
output = decrypt(pw,text);
System.out.println("Output: " +output);
}
}
private static Scanner scan;
private static String encrypt(char[] password, String plaintext) throws Exception {
//create random salt
byte[] salt = new byte[8];
SecureRandom random = new SecureRandom();
random.nextBytes(salt);
//create key based on password
int iterationCount = ITERATIONS;
PBEKeySpec pbeSpec = new PBEKeySpec(password, salt, iterationCount);
SecretKeyFactory keyFact = SecretKeyFactory.getInstance("PBEWithSHAAnd3KeyTripleDES");
//create a cipher
Cipher myCipher = Cipher.getInstance("PBEWithSHAAnd3KeyTripleDES");
Key encryptKey = keyFact.generateSecret(pbeSpec);
myCipher.init(Cipher.ENCRYPT_MODE, encryptKey);
byte[] cipherText = myCipher.doFinal();
System.out.println("Encrypted Text: " +toString(cipherText));
//produce salt to string
saltString = salt.toString();
System.out.println("SALT: " +saltString);
//produce cipher text to string
ciphertextString = toString(cipherText);
//stores salt and cipher string in encryptPhrase
encryptPhrase = saltString+ciphertextString;
return saltString+ciphertextString;
}
public static String decrypt(char[] password, String encryptPhrase) throws Exception{
//split the encryption data into salt and ciphertext
//System.out.println("encrypt Phrase: " +encryptPhrase);
//convert salt into bytearray
byte[] bsalt = toByteArray(saltString);
//convert ciphertext into bytearray
byte[] bciphert= toByteArray(ciphertextString);
//produce cipher
/////////////////////////////////////////////////////////////////////
int iterationCount = ITERATIONS;
PBEKeySpec pbeSpec = new PBEKeySpec(password, bsalt, iterationCount);
//use SHA and 3DES
//create the key
SecretKeyFactory keyFact = SecretKeyFactory.getInstance("PBEWithSHAAnd3KeyTripleDES");
Cipher cDec = Cipher.getInstance("PBEWithSHAAnd3KeyTripleDES");
Key sKey = keyFact.generateSecret(pbeSpec);
//perform decryption
cDec.init(cDec.DECRYPT_MODE,sKey);
byte [] plainTextb = cDec.doFinal(bciphert); //gives me an error here. Says BadPaddingException: pad block corrupted?
String plainText = toString(plainTextb);
return plainText;
//return encryptPhrase;
}
/**
* Convert a byte array of 8 bit characters into a String.
*
*#param bytes the array containing the characters
* #param length the number of bytes to process
* #return a String representation of bytes
*/
public static String toString(byte[] bytes, int length)
{
char[] chars = new char[length];
for (int i = 0; i != chars.length; i++)
{
chars[i] = (char)(bytes[i] & 0xff);
}
return new String(chars);
}
/**
* Convert a byte array of 8 bit characters into a String.
*
* #param bytes the array containing the characters
* #return a String representation of bytes
*/
public static String toString( byte[] bytes)
{
return toString(bytes, bytes.length);
}
/**
* Convert the passed in String to a byte array by
* taking the bottom 8 bits of each character it contains.
*
* #param string the string to be converted
* #return a byte array representation
*/
public static byte[] toByteArray(String string)
{
byte[] bytes = new byte[string.length()];
char[] chars = string.toCharArray();
for (int i = 0; i != chars.length; i++)
{
bytes[i] = (byte)chars[i];
}
return bytes;
}
private static String digits = "0123456789abcdef";
public static String toHex(byte[] data, int length)
{
StringBuffer buf = new StringBuffer();
for (int i=0; i!= length; i++)
{
int v = data[i] & 0xff;
buf.append(digits.charAt(v >>4));
buf.append(digits.charAt(v & 0xf));
}
return buf.toString();
}
/**
* Return the passed in byte array as a hex string.
*
* #param data the bytes to be converted.
* #return a hex representation of data.
*/
public static String toHex(byte[] data)
{
return toHex(data, data.length);
}
}
Yet another confusion between bytes and characters. Could have guessed. Please look up the base 64 encoding and see why it exists. Then note the implementation of Object.toString() and lookup character encoding (don't use new String(byte[]) on randomized bytes).