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

каждый метод, который я пишу для кодирования строки в Java с помощью 3DES, не может быть дешифрован обратно в исходную строку. У кого-нибудь есть простой фрагмент кода, который может просто кодировать, а затем декодировать строку обратно в исходную строку?

Я знаю, что делаю очень глупую ошибку где-то в этом коде. Вот с чем я работал до сих пор:

** примечание, Я не возвращаю текст BASE64 из метода encrypt, и я не base64 un-encoding в методе decrypt потому что я пытался понять, не ошибаюсь ли я в части головоломки 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;
    }
}

5 ответов


ваш код был нормально, за исключением кодировки base 64 бит (который вы упомянули, был тест), причина выхода, так что вы показывали сырым массивом байт (делать toString() на массив байтов возвращает свою внутреннюю ссылку Java, а не строковое представление содержание). Вот версия, которая немного очищена и которая печатает "Кайл Бун" в качестве декодированной строки:

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");
    }
}

вот решение, использующее 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

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


вот очень просто статический класс шифрования / дешифрования, смещенный на примере надувного замка без заполнения Хосе Луисом Монтесом де Ока. Этот использует "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);
        }
    }


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;

    }

}