Как использовать шифрование / дешифрование 3DES в Java?

Каждый метод, который я пишу для кодирования строки в 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;
    }
}

person Kyle Boon    schedule 21.08.2008    source источник


Ответы (5)


Ваш код был в порядке, за исключением бита кодирования 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");
    }
}
person Boris Terzic    schedule 21.08.2008
comment
Большое спасибо. Но на самом деле этот метод использует только два ключа для шифрования сообщения: HG58YZ3CR9 и IvParameterSpec iv = new IvParameterSpec (new byte [8]); . Но самый надежный вариант тройного дезинформации - использовать три разных ключа для шифрования сообщения. Итак, как это сделать? Я нашел метод в Cipher, который использует SecureRandom в качестве другого параметра. Так это правильный путь? Большое спасибо - person Marshal Chen; 04.07.2013
comment
3DES использует 3,8-байтовые ключи (в этом примере хранятся как 24 байта). Обычно 1-й и 3-й ключи совпадают (то есть, взяв 16-байтовый ключ двойной длины, вы повторно используете первый компонент в качестве 3-го компонента). Чтобы использовать ключ тройной длины, просто пропустите бит выше, где 1-й компонент (байты 0-7) копируется в пространство для 3-го (байты 16-23). - person Adrian Hope-Bailie; 15.11.2013
comment
@ AdrianHope-Bailie Этот совет неверен, поскольку MD5 выводит только 16 байт, поэтому остальные будут заполнены нулями. Просто ноль никогда не будет хорошим ключом. - person Maarten Bodewes; 17.07.2014
comment
Этот код действительно делает многие вещи правильно, в том числе использует допустимые поля и режим, кодировку символов и т. Д. Он, однако, должен использовать PBKDF2 для преобразования паролей в ключи или использовать действительно случайные ключи. Добавление вычисления HMAC к зашифрованному тексту также может значительно повысить безопасность. - person Maarten Bodewes; 17.07.2014
comment
@owlstead Вы правы, я упустил тот факт, что входные данные всего 16 байт. Удобно маскируется с помощью Array.copyOf (digestOfPassword, 24). В ответ на конкретный вопрос я указал, что вместо использования тех же 8 байтов в первых и последних 8 байтах 24-байтового ключа (как это обычно бывает при выполнении 3DES с ключевыми компонентами 2 * 8 байтов) можно использовать 3 уникальные 8-байтовые ключевые компоненты. - person Adrian Hope-Bailie; 30.07.2014
comment
Проблема с этим кодом в том, что он использует статические нулевые байты IV. Гораздо лучше генерировать случайный IV для каждого шифрования и хранить его в начале зашифрованного текста. Это не обязательно должно быть секретом, но оно должно быть непредсказуемым. - person Artjom B.; 23.09.2015
comment
как преобразовать cipherText в String? - person Panadol Chong; 27.09.2018

Вот решение, использующее библиотеку 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
person oneiros    schedule 28.11.2012
comment
Этот ответ решает вопрос, но код вводит все ошибки кодировки, режима и т. Д., Которых следует тщательно избегать в коде вопроса. - person Maarten Bodewes; 17.07.2014
comment
Спасибо за ответ. Возникает вопрос, а если ключ короче 24 байтов, как правильно решить проблему? - person Salvo; 01.06.2017
comment
key = skf.generateSecret(ks); здесь мы генерируем ключ для шифрования, верно? так что же такое myEncryptionKey? - person artur47wien; 07.10.2017
comment
это, похоже, не работает для меня, когда я заменяю зашифрованную строку выше своей строкой и заменяю ключ своим ключом. Я получаю, что длина ввода не является фактором 8, исключение - person GSUgambit; 20.03.2018

Мне самому было трудно понять это, и этот пост помог мне найти правильный ответ для моего случая. При работе с финансовыми сообщениями в формате 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);
       }
    }

person jlmontesdeoca    schedule 15.04.2012
comment
Просто предоставление любого кода 3DES не кажется мне ответом. Кроме того, явное использование ECB и NoPadding, а также использование BC там, где это действительно не нужно, на мой взгляд, дисквалифицируют этот ответ. - person Maarten Bodewes; 17.07.2014
comment
Большое спасибо @jlmontesdeoca. Ваш ответ сэкономил мне время. Я тоже застрял с PKCS7Padding. - person Tahir Mehmood; 14.02.2019

Вот очень простой статический класс шифрования / дешифрования, основанный на примере 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);
        }
    }

person siliconsmiley    schedule 06.07.2012
comment
Но он по-прежнему использует ЕЦБ, что является большой ошибкой. - person Maarten Bodewes; 17.07.2014

Этот пример сработал для меня. И шифрование, и дешифрование работают без проблем.

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;

    }

}
person Saranga kapilarathna    schedule 11.02.2021