Каждый метод, который я пишу для кодирования строки в Java с использованием 3DES, нельзя расшифровать до исходной строки. Есть ли у кого-нибудь простой фрагмент кода, который может просто кодировать, а затем декодировать строку обратно в исходную строку?
Я знаю, что где-то в этом коде совершаю очень глупую ошибку. Вот с чем я работал до сих пор:
** обратите внимание, я не возвращаю текст BASE64 из метода шифрования, и я не использую кодировку base64 в методе дешифрования, потому что я пытался увидеть, не сделал ли я ошибку в части головоломки BASE64.
public class TripleDESTest {
public static void main(String[] args) {
String text = "kyle boon";
byte[] codedtext = new TripleDESTest().encrypt(text);
String decodedtext = new TripleDESTest().decrypt(codedtext);
System.out.println(codedtext);
System.out.println(decodedtext);
}
public byte[] encrypt(String message) {
try {
final MessageDigest md = MessageDigest.getInstance("md5");
final byte[] digestOfPassword = md.digest("HG58YZ3CR9".getBytes("utf-8"));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;)
{
keyBytes[k++] = keyBytes[j++];
}
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
final byte[] plainTextBytes = message.getBytes("utf-8");
final byte[] cipherText = cipher.doFinal(plainTextBytes);
final String encodedCipherText = new sun.misc.BASE64Encoder().encode(cipherText);
return cipherText;
}
catch (java.security.InvalidAlgorithmParameterException e) { System.out.println("Invalid Algorithm"); }
catch (javax.crypto.NoSuchPaddingException e) { System.out.println("No Such Padding"); }
catch (java.security.NoSuchAlgorithmException e) { System.out.println("No Such Algorithm"); }
catch (java.security.InvalidKeyException e) { System.out.println("Invalid Key"); }
catch (BadPaddingException e) { System.out.println("Invalid Key");}
catch (IllegalBlockSizeException e) { System.out.println("Invalid Key");}
catch (UnsupportedEncodingException e) { System.out.println("Invalid Key");}
return null;
}
public String decrypt(byte[] message) {
try
{
final MessageDigest md = MessageDigest.getInstance("md5");
final byte[] digestOfPassword = md.digest("HG58YZ3CR9".getBytes("utf-8"));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;)
{
keyBytes[k++] = keyBytes[j++];
}
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
final Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
decipher.init(Cipher.DECRYPT_MODE, key, iv);
//final byte[] encData = new sun.misc.BASE64Decoder().decodeBuffer(message);
final byte[] plainText = decipher.doFinal(message);
return plainText.toString();
}
catch (java.security.InvalidAlgorithmParameterException e) { System.out.println("Invalid Algorithm"); }
catch (javax.crypto.NoSuchPaddingException e) { System.out.println("No Such Padding"); }
catch (java.security.NoSuchAlgorithmException e) { System.out.println("No Such Algorithm"); }
catch (java.security.InvalidKeyException e) { System.out.println("Invalid Key"); }
catch (BadPaddingException e) { System.out.println("Invalid Key");}
catch (IllegalBlockSizeException e) { System.out.println("Invalid Key");}
catch (UnsupportedEncodingException e) { System.out.println("Invalid Key");}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}




Ваш код был в порядке, за исключением бита кодирования Base 64 (который, как вы упомянули, был тестом), причина, по которой вывод, возможно, не имел смысла, заключается в том, что вы отображали необработанный массив байтов (выполнение toString () в массиве байтов возвращает его внутренний Ссылка на Java, а не на строковое представление содержание). Вот версия, которая немного очищена и выводит «kyle boon» в качестве декодированной строки:
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class TripleDESTest {
public static void main(String[] args) throws Exception {
String text = "kyle boon";
byte[] codedtext = new TripleDESTest().encrypt(text);
String decodedtext = new TripleDESTest().decrypt(codedtext);
System.out.println(codedtext); // this is a byte array, you'll just see a reference to an array
System.out.println(decodedtext); // This correctly shows "kyle boon"
}
public byte[] encrypt(String message) throws Exception {
final MessageDigest md = MessageDigest.getInstance("md5");
final byte[] digestOfPassword = md.digest("HG58YZ3CR9"
.getBytes("utf-8"));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;) {
keyBytes[k++] = keyBytes[j++];
}
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
final byte[] plainTextBytes = message.getBytes("utf-8");
final byte[] cipherText = cipher.doFinal(plainTextBytes);
// final String encodedCipherText = new sun.misc.BASE64Encoder()
// .encode(cipherText);
return cipherText;
}
public String decrypt(byte[] message) throws Exception {
final MessageDigest md = MessageDigest.getInstance("md5");
final byte[] digestOfPassword = md.digest("HG58YZ3CR9"
.getBytes("utf-8"));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;) {
keyBytes[k++] = keyBytes[j++];
}
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
final Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
decipher.init(Cipher.DECRYPT_MODE, key, iv);
// final byte[] encData = new
// sun.misc.BASE64Decoder().decodeBuffer(message);
final byte[] plainText = decipher.doFinal(message);
return new String(plainText, "UTF-8");
}
}
3DES использует 3,8-байтовые ключи (в этом примере хранятся как 24 байта). Обычно 1-й и 3-й ключи совпадают (то есть, взяв 16-байтовый ключ двойной длины, вы повторно используете первый компонент в качестве 3-го компонента). Чтобы использовать ключ тройной длины, просто пропустите бит выше, где 1-й компонент (байты 0-7) копируется в пространство для 3-го (байты 16-23).
@ AdrianHope-Bailie Этот совет неверен, поскольку MD5 выводит только 16 байт, поэтому остальные будут заполнены нулями. Просто ноль никогда не будет хорошим ключом.
Этот код действительно делает многие вещи правильно, включая использование допустимого заполнения и режима, кодировки символов и т. д. Он, однако, должен использовать PBKDF2 для преобразования паролей в ключи или использовать действительно случайные ключи. Добавление вычисления HMAC к зашифрованному тексту также может значительно повысить безопасность.
@owlstead Вы правы, я упустил тот факт, что входные данные всего 16 байт. Удобно маскируется с помощью Array.copyOf (digestOfPassword, 24). В ответ на конкретный вопрос я указал, что вместо использования тех же 8 байтов в первых и последних 8 байтах 24-байтового ключа (как это обычно бывает при выполнении 3DES с ключевыми компонентами 2 * 8 байтов) можно использовать 3 уникальные 8-байтовые ключевые компоненты.
Проблема с этим кодом в том, что он использует статические нулевые байты IV. Гораздо лучше генерировать случайный IV для каждого шифрования и хранить его в начале зашифрованного текста. Это не обязательно должно быть секретом, но оно должно быть непредсказуемым.
как преобразовать cipherText в String?
Мне самому было трудно понять это, и этот пост помог мне найти правильный ответ для моего случая. При работе с финансовыми сообщениями в формате ISO-8583 требования к 3DES довольно специфичны, поэтому для моего особого случая комбинации «DESede / CBC / PKCS5Padding» не решали проблему. После некоторого сравнительного тестирования моих результатов с некоторыми калькуляторами 3DES, разработанными для финансового мира, я обнаружил, что значение «DESede / ECB / Nopadding» больше подходит для конкретной задачи.
Вот демонстрационная реализация моего класса TripleDes (с использованием поставщика Bouncy Castle)
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
/**
*
* @author Jose Luis Montes de Oca
*/
public class TripleDesCipher {
private static String TRIPLE_DES_TRANSFORMATION = "DESede/ECB/Nopadding";
private static String ALGORITHM = "DESede";
private static String BOUNCY_CASTLE_PROVIDER = "BC";
private Cipher encrypter;
private Cipher decrypter;
public TripleDesCipher(byte[] key) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
InvalidKeyException {
Security.addProvider(new BouncyCastleProvider());
SecretKey keySpec = new SecretKeySpec(key, ALGORITHM);
encrypter = Cipher.getInstance(TRIPLE_DES_TRANSFORMATION, BOUNCY_CASTLE_PROVIDER);
encrypter.init(Cipher.ENCRYPT_MODE, keySpec);
decrypter = Cipher.getInstance(TRIPLE_DES_TRANSFORMATION, BOUNCY_CASTLE_PROVIDER);
decrypter.init(Cipher.DECRYPT_MODE, keySpec);
}
public byte[] encode(byte[] input) throws IllegalBlockSizeException, BadPaddingException {
return encrypter.doFinal(input);
}
public byte[] decode(byte[] input) throws IllegalBlockSizeException, BadPaddingException {
return decrypter.doFinal(input);
}
}
Просто предоставление любого кода 3DES не кажется мне ответом. Кроме того, явное использование ECB и NoPadding, а также использование BC там, где это действительно не нужно, на мой взгляд, дисквалифицируют этот ответ.
Большое спасибо @jlmontesdeoca. Ваш ответ сэкономил мне время. Я тоже застрял с PKCS7Padding.
Вот очень простой статический класс шифрования / дешифрования, основанный на примере Bouncy Castle без заполнения, созданном Хосе Луисом Монтесом де Ока. Здесь используется «DESede / ECB / PKCS7Padding», поэтому мне не нужно беспокоиться о ручном заполнении.
package com.zenimax.encryption;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
/**
*
* @author Matthew H. Wagner
*/
public class TripleDesBouncyCastle {
private static String TRIPLE_DES_TRANSFORMATION = "DESede/ECB/PKCS7Padding";
private static String ALGORITHM = "DESede";
private static String BOUNCY_CASTLE_PROVIDER = "BC";
private static void init()
{
Security.addProvider(new BouncyCastleProvider());
}
public static byte[] encode(byte[] input, byte[] key)
throws IllegalBlockSizeException, BadPaddingException,
NoSuchAlgorithmException, NoSuchProviderException,
NoSuchPaddingException, InvalidKeyException {
init();
SecretKey keySpec = new SecretKeySpec(key, ALGORITHM);
Cipher encrypter = Cipher.getInstance(TRIPLE_DES_TRANSFORMATION,
BOUNCY_CASTLE_PROVIDER);
encrypter.init(Cipher.ENCRYPT_MODE, keySpec);
return encrypter.doFinal(input);
}
public static byte[] decode(byte[] input, byte[] key)
throws IllegalBlockSizeException, BadPaddingException,
NoSuchAlgorithmException, NoSuchProviderException,
NoSuchPaddingException, InvalidKeyException {
init();
SecretKey keySpec = new SecretKeySpec(key, ALGORITHM);
Cipher decrypter = Cipher.getInstance(TRIPLE_DES_TRANSFORMATION,
BOUNCY_CASTLE_PROVIDER);
decrypter.init(Cipher.DECRYPT_MODE, keySpec);
return decrypter.doFinal(input);
}
}
Но он по-прежнему использует ЕЦБ, что является большой ошибкой.
Вот решение, использующее библиотеку javax.crypto и библиотеку кодеков apache commons для кодирования и декодирования в Base64:
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import org.apache.commons.codec.binary.Base64;
public class TrippleDes {
private static final String UNICODE_FORMAT = "UTF8";
public static final String DESEDE_ENCRYPTION_SCHEME = "DESede";
private KeySpec ks;
private SecretKeyFactory skf;
private Cipher cipher;
byte[] arrayBytes;
private String myEncryptionKey;
private String myEncryptionScheme;
SecretKey key;
public TrippleDes() throws Exception {
myEncryptionKey = "ThisIsSpartaThisIsSparta";
myEncryptionScheme = DESEDE_ENCRYPTION_SCHEME;
arrayBytes = myEncryptionKey.getBytes(UNICODE_FORMAT);
ks = new DESedeKeySpec(arrayBytes);
skf = SecretKeyFactory.getInstance(myEncryptionScheme);
cipher = Cipher.getInstance(myEncryptionScheme);
key = skf.generateSecret(ks);
}
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);
encryptedString = new String(Base64.encodeBase64(encryptedText));
} catch (Exception e) {
e.printStackTrace();
}
return encryptedString;
}
public String decrypt(String encryptedString) {
String decryptedText=null;
try {
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] encryptedText = Base64.decodeBase64(encryptedString);
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText= new String(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
public static void main(String args []) throws Exception
{
TrippleDes td= new TrippleDes();
String target = "imparator";
String encrypted=td.encrypt(target);
String decrypted=td.decrypt(encrypted);
System.out.println("String To Encrypt: "+ target);
System.out.println("Encrypted String:" + encrypted);
System.out.println("Decrypted String:" + decrypted);
}
}
Выполнение вышеуказанной программы приводит к следующему результату:
String To Encrypt: imparator
Encrypted String:FdBNaYWfjpWN9eYghMpbRA==
Decrypted String:imparator
Этот ответ решает вопрос, но код вводит все ошибки кодировки, режима и т. д., Которых следует тщательно избегать в коде вопроса.
Спасибо за ответ. Возникает вопрос, а если ключ короче 24 байтов, как правильно решить проблему?
key = skf.generateSecret(ks); здесь мы генерируем ключ для шифрования, верно? так что же такое myEncryptionKey?
это, похоже, не работает для меня, когда я заменяю зашифрованную строку выше своей строкой и заменяю ключ своим ключом. Я получаю, что длина ввода не является фактором 8, исключение
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.util.Base64;
import java.util.Base64.Encoder;
/**
*
* @author shivshankar pal
*
* this code is working properly. doing proper encription and decription
note:- it will work only with jdk8
*
*
*/
public class TDes {
private static byte[] key = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02 };
private static byte[] keyiv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00 };
public static String encode(String args) {
System.out.println("plain data==> " + args);
byte[] encoding;
try {
encoding = Base64.getEncoder().encode(args.getBytes("UTF-8"));
System.out.println("Base64.encodeBase64==>" + new String(encoding));
byte[] str5 = des3EncodeCBC(key, keyiv, encoding);
System.out.println("des3EncodeCBC==> " + new String(str5));
byte[] encoding1 = Base64.getEncoder().encode(str5);
System.out.println("Base64.encodeBase64==> " + new String(encoding1));
return new String(encoding1);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
public static String decode(String args) {
try {
System.out.println("encrypted data==>" + new String(args.getBytes("UTF-8")));
byte[] decode = Base64.getDecoder().decode(args.getBytes("UTF-8"));
System.out.println("Base64.decodeBase64(main encription)==>" + new String(decode));
byte[] str6 = des3DecodeCBC(key, keyiv, decode);
System.out.println("des3DecodeCBC==>" + new String(str6));
String data=new String(str6);
byte[] decode1 = Base64.getDecoder().decode(data.trim().getBytes("UTF-8"));
System.out.println("plaintext==> " + new String(decode1));
return new String(decode1);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "u mistaken in try block";
}
private static byte[] des3EncodeCBC(byte[] key, byte[] keyiv, byte[] data) {
try {
Key deskey = null;
DESedeKeySpec spec = new DESedeKeySpec(key);
SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("desede");
deskey = keyfactory.generateSecret(spec);
Cipher cipher = Cipher.getInstance("desede/ CBC/PKCS5Padding");
IvParameterSpec ips = new IvParameterSpec(keyiv);
cipher.init(Cipher.ENCRYPT_MODE, deskey, ips);
byte[] bout = cipher.doFinal(data);
return bout;
} catch (Exception e) {
System.out.println("methods qualified name" + e);
}
return null;
}
private static byte[] des3DecodeCBC(byte[] key, byte[] keyiv, byte[] data) {
try {
Key deskey = null;
DESedeKeySpec spec = new DESedeKeySpec(key);
SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("desede");
deskey = keyfactory.generateSecret(spec);
Cipher cipher = Cipher.getInstance("desede/ CBC/NoPadding");//PKCS5Padding NoPadding
IvParameterSpec ips = new IvParameterSpec(keyiv);
cipher.init(Cipher.DECRYPT_MODE, deskey, ips);
byte[] bout = cipher.doFinal(data);
return bout;
} catch (Exception e) {
System.out.println("methods qualified name" + e);
}
return null;
}
}
Этот пример сработал для меня. И шифрование, и дешифрование работают без проблем.
package com.test.encodedecode;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class ThreeDesHandler {
public static void main(String[] args) {
String encodetext = null;
String decodetext = null;
ThreeDesHandler handler = new ThreeDesHandler();
String key = "secret key";//Need to change with your value
String plaintxt = "String for encode";//Need to change with your value
encodetext = handler.encode3Des(key, plaintxt);
System.out.println(encodetext);
decodetext = handler.decode3Des(key, encodetext);
System.out.println(decodetext);
}
public String encode3Des(String key, String plaintxt) {
try {
byte[] seed_key = (new String(key)).getBytes();
SecretKeySpec keySpec = new SecretKeySpec(seed_key, "TripleDES");
Cipher nCipher = Cipher.getInstance("TripleDES");
nCipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] cipherbyte = nCipher.doFinal(plaintxt.getBytes());
String encodeTxt = new String(Base64.encodeBase64(cipherbyte));
return encodeTxt;
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
public String decode3Des(String key, String desStr) {
try {
Base64 base64 = new Base64();
byte[] seed_key = (new String(key)).getBytes();
SecretKeySpec keySpec = new SecretKeySpec(seed_key, "TripleDES");
Cipher nCipher = Cipher.getInstance("TripleDES");
nCipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] src = base64.decode(desStr);
String returnstring = new String(nCipher.doFinal(src));
return returnstring;
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
Большое спасибо. Но на самом деле этот метод использует только два ключа для шифрования сообщения: «HG58YZ3CR9» и «IvParameterSpec iv = new IvParameterSpec (new byte [8]);» . Но самый надежный вариант тройного десерта - использовать три разных ключа для шифрования сообщения. Итак, как это сделать? Я нашел метод в Cipher, который использует "SecureRandom" в качестве другого параметра. Так это правильный путь? Большое спасибо