Java - 使用河豚加密时缺少最终字符

标签 java encryption blowfish

我正在使用一些使用 Blowfish 加密文本文件内容的 Java 代码。当我将加密文件转换回(即解密)时,字符串末尾缺少一个字符。任何想法为什么?我是 Java 的新手,已经摆弄了好几个小时都没有运气。

文件 war_and_peace.txt 只包含字符串“This is some text”。 decrypted.txt 包含“This is some tex”(末尾没有 t)。这是java代码:

public static void encrypt(String key, InputStream is, OutputStream os) throws Throwable {
    encryptOrDecrypt(key, Cipher.ENCRYPT_MODE, is, os);
}

public static void decrypt(String key, InputStream is, OutputStream os) throws Throwable {
    encryptOrDecrypt(key, Cipher.DECRYPT_MODE, is, os);
}

private static byte[] getBytes(String toGet)
{
    try
    {
        byte[] retVal = new byte[toGet.length()];
        for (int i = 0; i < toGet.length(); i++)
        {
            char anychar = toGet.charAt(i);
            retVal[i] = (byte)anychar;
        }
        return retVal;
    }catch(Exception e)
    {
        String errorMsg = "ERROR: getBytes :" + e;
        return null;
    }
}

public static void encryptOrDecrypt(String key, int mode, InputStream is, OutputStream os) throws Throwable {


   String iv = "12345678";
   byte[] IVBytes = getBytes(iv);
   IvParameterSpec IV = new IvParameterSpec(IVBytes);


    byte[] KeyData = key.getBytes(); 
    SecretKeySpec blowKey = new SecretKeySpec(KeyData, "Blowfish"); 
    //Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
    Cipher cipher = Cipher.getInstance("Blowfish/CBC/NoPadding");

    if (mode == Cipher.ENCRYPT_MODE) {
        cipher.init(Cipher.ENCRYPT_MODE, blowKey, IV);
        CipherInputStream cis = new CipherInputStream(is, cipher);
        doCopy(cis, os);
    } else if (mode == Cipher.DECRYPT_MODE) {
        cipher.init(Cipher.DECRYPT_MODE, blowKey, IV);
        CipherOutputStream cos = new CipherOutputStream(os, cipher);
        doCopy(is, cos);
    }
}

public static void doCopy(InputStream is, OutputStream os) throws IOException {
    byte[] bytes = new byte[4096];
    //byte[] bytes = new byte[64];
    int numBytes;
    while ((numBytes = is.read(bytes)) != -1) {
        os.write(bytes, 0, numBytes);
    }
    os.flush();
    os.close();
    is.close();
}   

public static void main(String[] args) {


    //Encrypt the reports
    try {
        String key = "squirrel123";

        FileInputStream fis = new FileInputStream("war_and_peace.txt");
        FileOutputStream fos = new FileOutputStream("encrypted.txt");
        encrypt(key, fis, fos);

        FileInputStream fis2 = new FileInputStream("encrypted.txt");
        FileOutputStream fos2 = new FileOutputStream("decrypted.txt");
        decrypt(key, fis2, fos2);
    } catch (Throwable e) {
        e.printStackTrace();
    }
}

`

最佳答案

这里有几件事不是最优的。

但让我们先解决您的问题。输入的最后一部分以某种方式丢失的原因是您指定的填充:无!如果不指定填充,Cipher 只能对全长 block (Blowfish 为 8 字节)进行操作。少于一个 block 长的多余输入将被静默丢弃,并且有您丢失的文本。详细说明:“This is some text”的长度为 17 个字节,因此将解密两个完整的 block ,最后的第 17 个字节“t”将被丢弃。

始终将填充与对称 block 密码结合使用,PKCS5Padding 很好。

接下来,当使用 Cipher 操作时,您不需要实现自己的 getBytes() - String#getBytes 已经在做适合你的工作。只需确保在获取字节时以及稍后从字节重建 String 时对相同的字符编码进行操作,这是常见的错误来源。

你应该看看 JCE docs ,它们将帮助您避免一些常见错误。

例如,直接使用字符串 key 对于对称密码术来说是不行的,它们不包含足够的熵,这使得暴力破解这样的 key 更容易。 JCE 为您提供了 KeyGenerator 类,您应该始终使用它,除非您确切地知道自己在做什么。它会为您生成一个大小合适的安全随 secret 钥,但除此之外,人们往往会忘记这一点,它还会确保它不会创建一个弱 key 。例如,在实际使用中应避免使用已知的 Blowfish 弱键。

最后,在进行 CBC 加密时,您不应该使用确定性 IV。最近有一些攻击可以利用这一点,导致消息完全恢复,这显然不是很酷。 IV 应始终随机选择(使用 SecureRandom)以使其不可预测。 Cipher 默认为您完成此操作,您可以在使用 Cipher#getIV 加密后简单地获取使用的 IV。

另一方面,安全相关性较低:您应该在 finally block 中关闭流,以确保不惜一切代价关闭它们 - 否则您将留下一个打开的文件句柄,以防万一一个异常(exception)。

这是你的代码的更新版本,它考虑了所有这些方面(必须使用字符串而不是 main 中的文件,但你可以简单地将它替换为你在那里的内容):

private static final String ALGORITHM = "Blowfish/CBC/PKCS5Padding";

/* now returns the IV that was used */
private static byte[] encrypt(SecretKey key, 
                              InputStream is, 
                              OutputStream os) {
    try {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, key);
        CipherInputStream cis = new CipherInputStream(is, cipher);
        doCopy(cis, os);
        return cipher.getIV();
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}

private static void decrypt(SecretKey key, 
                            byte[] iv, 
                            InputStream is, 
                            OutputStream os) 
{
    try {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
        CipherInputStream cis = new CipherInputStream(is, cipher);
        doCopy(cis, os);
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}

private static void doCopy(InputStream is, OutputStream os) 
throws IOException {
    try {
        byte[] bytes = new byte[4096];
        int numBytes;
        while ((numBytes = is.read(bytes)) != -1) {
            os.write(bytes, 0, numBytes);
        }
    } finally {
        is.close();
        os.close();
    }
}

public static void main(String[] args) {
    try {
        String plain = "I am very secret. Help!";

        KeyGenerator keyGen = KeyGenerator.getInstance("Blowfish");
        SecretKey key = keyGen.generateKey();
        byte[] iv;

        InputStream in = new ByteArrayInputStream(plain.getBytes("UTF-8"));
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        iv = encrypt(key, in, out);

        in = new ByteArrayInputStream(out.toByteArray());
        out = new ByteArrayOutputStream();
        decrypt(key, iv, in, out);

        String result = new String(out.toByteArray(), "UTF-8");
        System.out.println(result);
        System.out.println(plain.equals(result)); // => true
    } catch (Exception e) {
        e.printStackTrace();
    }
}

关于Java - 使用河豚加密时缺少最终字符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10772978/

相关文章:

c - 使用 Blowfish 加密和解密消息

php - 如何使用河豚自动为crypt方法生成盐

java - Spring - 如果服务返回 409 HTTP 代码,则重试请求

java - 如何实时查看绘制的线条。 Java Swing

c++ - 文件可以以 0x80 字符结尾吗?

c# - 需要解密 SAML 响应

java - Java 中的加密我需要一个 16 字节的初始 vector

java - 导入在类似的类中不起作用

java - 使用清晰的编辑文本的更好方法

java - 特定 Android 设备/操作系统上的增量加密问题