错误“使用填充密码解密时,输入长度必须是8的倍数”

时间:2010-06-14 20:31:14

标签: c# java encryption

我正在尝试将项目从C#转移到Java进行学习练习。我仍然是Java的新手,但我在C#中有一个TripleDES类,它加密字符串并返回加密字节数组的字符串值。这是我的C#代码:

using System;
using System.IO;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;

namespace tDocc.Classes
{
    /// <summary>
    /// Triple DES encryption class
    /// </summary>
    public static class TripleDES
    {
        private static byte[] key = { 110, 32, 73, 24, 125, 66, 75, 18, 79, 150, 211, 122, 213, 14, 156, 136, 171, 218, 119, 240, 81, 142, 23, 4 };
        private static byte[] iv = { 25, 117, 68, 23, 99, 78, 231, 219 };

        /// <summary>
        /// Encrypt a string to an encrypted byte array
        /// </summary>
        /// <param name="plainText">Text to encrypt</param>
        /// <returns>Encrypted byte array</returns>
        public static byte[] Encrypt(string plainText)
        {
            UTF8Encoding utf8encoder = new UTF8Encoding();
            byte[] inputInBytes = utf8encoder.GetBytes(plainText);
            TripleDESCryptoServiceProvider tdesProvider = new TripleDESCryptoServiceProvider();
            ICryptoTransform cryptoTransform = tdesProvider.CreateEncryptor(key, iv);
            MemoryStream encryptedStream = new MemoryStream();
            CryptoStream cryptStream = new CryptoStream(encryptedStream, cryptoTransform, CryptoStreamMode.Write);
            cryptStream.Write(inputInBytes, 0, inputInBytes.Length);
            cryptStream.FlushFinalBlock();
            encryptedStream.Position = 0;
            byte[] result = new byte[encryptedStream.Length];
            encryptedStream.Read(result, 0, (int)encryptedStream.Length);
            cryptStream.Close();
            return result;
        }

        /// <summary>
        /// Decrypt a byte array to a string
        /// </summary>
        /// <param name="inputInBytes">Encrypted byte array</param>
        /// <returns>Decrypted string</returns>
        public static string Decrypt(byte[] inputInBytes)
        {
            UTF8Encoding utf8encoder = new UTF8Encoding();
            TripleDESCryptoServiceProvider tdesProvider = new TripleDESCryptoServiceProvider();
            ICryptoTransform cryptoTransform = tdesProvider.CreateDecryptor(key, iv);
            MemoryStream decryptedStream = new MemoryStream();
            CryptoStream cryptStream = new CryptoStream(decryptedStream, cryptoTransform, CryptoStreamMode.Write);
            cryptStream.Write(inputInBytes, 0, inputInBytes.Length);
            cryptStream.FlushFinalBlock();
            decryptedStream.Position = 0;
            byte[] result = new byte[decryptedStream.Length];
            decryptedStream.Read(result, 0, (int)decryptedStream.Length);
            cryptStream.Close();
            UTF8Encoding myutf = new UTF8Encoding();
            return myutf.GetString(result);
        }

        /// <summary>
        /// Decrypt an encrypted string
        /// </summary>
        /// <param name="text">Encrypted text</param>
        /// <returns>Decrypted string</returns>
        public static string DecryptText(string text)
        {
            if (text == "")
            {
                return text;
            }
            return Decrypt(Convert.FromBase64String(text));
        }

        /// <summary>
        /// Encrypt a string
        /// </summary>
        /// <param name="text">Unencrypted text</param>
        /// <returns>Encrypted string</returns>
        public static string EncryptText(string text)
        {
            if (text == "")
            {
                return text;
            }
            return Convert.ToBase64String(Encrypt(text));
        }
    }

    /// <summary>
    /// Random number generator
    /// </summary>
    public static class RandomGenerator
    {
        /// <summary>
        /// Generate random number
        /// </summary>
        /// <param name="length">Number of randomizations</param>
        /// <returns>Random number</returns>
        public static int GenerateNumber(int length)
        {
            byte[] randomSeq = new byte[length];
            new RNGCryptoServiceProvider().GetBytes(randomSeq);
            int code = Environment.TickCount;

            foreach (byte b in randomSeq)
            {
                code += (int)b;
            }

            return code;
        }
    }

    /// <summary>
    /// Hash generator class
    /// </summary>
    public static class Hasher
    {
        /// <summary>
        /// Hash type
        /// </summary>
        public enum eHashType
        {
            /// <summary>
            /// MD5 hash. Quick but collisions are more likely. This should not be used for anything important
            /// </summary>
            MD5 = 0,

            /// <summary>
            /// SHA1 hash. Quick and secure. This is a popular method for hashing passwords
            /// </summary>
            SHA1 = 1,

            /// <summary>
            /// SHA256 hash. Slower than SHA1, but more secure. Used for encryption keys
            /// </summary>
            SHA256 = 2,

            /// <summary>
            /// SHA348 hash. Even slower than SHA256, but offers more security
            /// </summary>
            SHA348 = 3,

            /// <summary>
            /// SHA512 hash. Slowest but most secure. Probably overkill for most applications
            /// </summary>
            SHA512 = 4,

            /// <summary>
            /// Derrived from MD5, but only returns 12 digits
            /// </summary>
            Digit12 = 5
        }

        /// <summary>
        /// Hashes text using a specific hashing method
        /// </summary>
        /// <param name="text">Input text</param>
        /// <param name="hash">Hash method</param>
        /// <returns>Hashed text</returns>
        public static string GetHash(string text, eHashType hash)
        {
            if (text == "")
            {
                return text;
            }
            if (hash == eHashType.MD5)
            {
                MD5CryptoServiceProvider hasher = new MD5CryptoServiceProvider();
                return ByteToHex(hasher.ComputeHash(Encoding.ASCII.GetBytes(text)));
            }
            else if (hash == eHashType.SHA1)
            {
                SHA1Managed hasher = new SHA1Managed();
                return ByteToHex(hasher.ComputeHash(Encoding.ASCII.GetBytes(text)));
            }
            else if (hash == eHashType.SHA256)
            {
                SHA256Managed hasher = new SHA256Managed();
                return ByteToHex(hasher.ComputeHash(Encoding.ASCII.GetBytes(text)));
            }
            else if (hash == eHashType.SHA348)
            {
                SHA384Managed hasher = new SHA384Managed();
                return ByteToHex(hasher.ComputeHash(Encoding.ASCII.GetBytes(text)));
            }
            else if (hash == eHashType.SHA512)
            {
                SHA512Managed hasher = new SHA512Managed();
                return ByteToHex(hasher.ComputeHash(Encoding.ASCII.GetBytes(text)));
            }
            else if (hash == eHashType.Digit12)
            {
                MD5CryptoServiceProvider hasher = new MD5CryptoServiceProvider();
                string newHash = ByteToHex(hasher.ComputeHash(Encoding.ASCII.GetBytes(text)));
                return newHash.Substring(0, 12);
            }
            return "";
        }

        /// <summary>
        /// Generates a hash based on a file's contents. Used for detecting changes to a file and testing for duplicate files
        /// </summary>
        /// <param name="info">FileInfo object for the file to be hashed</param>
        /// <param name="hash">Hash method</param>
        /// <returns>Hash string representing the contents of the file</returns>
        public static string GetHash(FileInfo info, eHashType hash)
        {
            FileStream hashStream = new FileStream(info.FullName, FileMode.Open, FileAccess.Read);
            string hashString = "";
            if (hash == eHashType.MD5)
            {
                MD5CryptoServiceProvider hasher = new MD5CryptoServiceProvider();
                hashString = ByteToHex(hasher.ComputeHash(hashStream));
            }
            else if (hash == eHashType.SHA1)
            {
                SHA1Managed hasher = new SHA1Managed();
                hashString = ByteToHex(hasher.ComputeHash(hashStream));
            }
            else if (hash == eHashType.SHA256)
            {
                SHA256Managed hasher = new SHA256Managed();
                hashString = ByteToHex(hasher.ComputeHash(hashStream));
            }
            else if (hash == eHashType.SHA348)
            {
                SHA384Managed hasher = new SHA384Managed();
                hashString = ByteToHex(hasher.ComputeHash(hashStream));
            }
            else if (hash == eHashType.SHA512)
            {
                SHA512Managed hasher = new SHA512Managed();
                hashString = ByteToHex(hasher.ComputeHash(hashStream));
            }
            hashStream.Close();
            hashStream.Dispose();
            hashStream = null;
            return hashString;
        }

        /// <summary>
        /// Converts a byte array to a hex string
        /// </summary>
        /// <param name="data">Byte array</param>
        /// <returns>Hex string</returns>
        public static string ByteToHex(byte[] data)
        {
            StringBuilder builder = new StringBuilder();
            foreach (byte hashByte in data)
            {
                builder.Append(string.Format("{0:X1}", hashByte));
            }
            return builder.ToString();
        }

        /// <summary>
        /// Converts a hex string to a byte array
        /// </summary>
        /// <param name="hexString">Hex string</param>
        /// <returns>Byte array</returns>
        public static byte[] HexToByte(string hexString)
        {
            byte[] returnBytes = new byte[hexString.Length / 2];
            for (int i = 0; i <= returnBytes.Length - 1; i++)
            {
                returnBytes[i] = byte.Parse(hexString.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber);
            }
            return returnBytes;
        }
    }
}

到目前为止,她是我已经获得的Java代码,但是当我对此进行测试时,我收到错误“使用填充密码解密时输入长度必须是8的倍数”:

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import com.tdocc.utils.Base64;

public class TripleDES {
    private static byte[] keyBytes = { 110, 32, 73, 24, 125, 66, 75, 18, 79, (byte)150, (byte)211, 122, (byte)213, 14, (byte)156, (byte)136, (byte)171, (byte)218, 119, (byte)240, 81, (byte)142, 23, 4 };
    private static byte[] ivBytes = { 25, 117, 68, 23, 99, 78, (byte)231, (byte)219 };

    public static String encryptText(String plainText) {
        try {
            if (plainText.isEmpty()) return plainText;
            return Base64.decode(TripleDES.encrypt(plainText)).toString();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    public static byte[] encrypt(String plainText) throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchPaddingException {
        try {
            final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
            final IvParameterSpec iv = new IvParameterSpec(ivBytes);
            final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, key, iv);

            final byte[] plainTextBytes = plainText.getBytes("utf-8");
            final byte[] cipherText = cipher.doFinal(plainTextBytes);

            return cipherText;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    public static String decryptText(String message) {
        try {
            if (message.isEmpty()) return message;
            else return TripleDES.decrypt(message.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    public static String decrypt(byte[] message) {
        try {
            final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
            final IvParameterSpec iv = new IvParameterSpec(ivBytes);
            final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, key, iv);

            final byte[] plainText = cipher.doFinal(message);

            return plainText.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

}

3 个答案:

答案 0 :(得分:1)

您在Java代码中没有正确地进行base64 en / decoding。您的C#代码对数据进行加密,然后base64对其进行编码。因此,您的Java decryptText方法必须首先将base64字符串解码基于字节数组,然后使用decrypt方法解密字节数组。您的加密方法看起来同样错误,但我不熟悉那个特定的base64实用程序。

答案 1 :(得分:0)

This问题似乎与您的问题类似。希望能帮助到你。

答案 2 :(得分:0)

您的密码指定PKCS#5填充,这要求密文是块大小的倍数(对于Triple DES,为8字节)。

根本原因是密文已损坏。可能的原因是,

  1. 密文是二进制文件,可以编码为数据库中的文本(如Base64或hex)。您需要将其解码为二进制文件才能解密。

  2. 密文被视为文本,字符编码将其混淆。例如,如果您将二进制文件读为UTF-8,则可能会出现大量问号。

  3. 此Java代码也无法满足您的需求,

           return plainText.toString();
    

    应该是

           return new String(plainText, "UTF-8");