Java - 使用blowfish加密时缺少最终字符 [英] Java - Missing final characters when encrypting using blowfish

查看:116
本文介绍了Java - 使用blowfish加密时缺少最终字符的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用一些使用Blowfish加密文本文件内容的j​​ava代码。当我将加密文件转换(即解密)时,字符串从最后丢失一个字符。任何想法为什么?我对Java非常新鲜,没有运气几个小时。



文件war_and_peace.txt只包含字符串这是一些文本。 decryptpted.txt包含这是一些tex(没有在结尾)。这是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()]; (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;
返回null;
}
}

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


String iv =12345678;
byte [] IVBytes = getBytes(iv);
IvParameterSpec IV =新的IvParameterSpec(IVBytes);


byte [] KeyData = key.getBytes();
SecretKeySpec blowKey = new SecretKeySpec(KeyData,Blowfish);
//密码密码= Cipher.getInstance(Blowfish / CBC / PKCS5Padding);
密码密码= 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){


//加密报表
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(decryptpted.txt);
decrypt(key,fis2,fos2);
} catch(Throwable e){
e.printStackTrace();
}
}

`

解决方案

这里有一些不是最佳的东西。



但是让我们先来解决你的问题。你输入的最后一部分缺少的原因是你指定的填充:none!没有指定填充,密码只能在全长块(Blowfish为8个字节)上运行。少于一个块长的输入将被默认丢弃,并且您的文本丢失。详细说:这是一些文本是17个字节长,所以两个完整的块将被解密,最后的第17个字节t将被丢弃。



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



接下来,当使用密码 ,你不需要实现你自己的 getBytes() - 有$ code> String#getBytes 已经在为你做这个工作。确保在获取字节时使用相同的字符编码,并在稍后从字节重建 String 时,它是常见的错误源。



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



例如,直接使用String键是不要去对于对称加密,它们不包含足够的熵,这将使得更容易强制这样的密钥。 JCE为您提供 KeyGenerator 类,您应该始终使用它,除非您确切了解您正在做什么。它为您生成适当大小的安全随机密钥,但另外,这是人们往往忘记的东西,它也将确保它不会产生弱键。例如,在实际使用中应该避免使用Blowfish的弱键。



最后,在进行CBC加密时,不要使用确定性IV。有一些最近的攻击使得有可能利用这一点,导致消息的完全恢复,这显然不是很酷。应该随机选择IV(使用 SecureRandom ),以使其不可预测。 Cipher 默认情况下,您可以使用密码#getIV 加密后,直接获取使用的IV。



另一方面,与安全性相关的安全性较低:您应该关闭最终块中的流,以确保它们关闭所有费用 - 否则,如果发生异常,您将被保留一个打开的文件句柄。



这是一个更新版本的代码,将所有这些方面纳入考虑(不得不使用字符串而不是 main 中的文件,但您可以简单地将其替换为您所在的位置):

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

/ *现在返回使用的IV * /
private static byte [] encrypt(SecretKey key,
InputStream is,
OutputStream os){
try {
密码密码= 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.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 =新字节[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 =我很秘密,帮助! ;

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


I am using some java code that encrypts the contents of a text file using Blowfish. When I convert the encrypted file back (i.e. decrypt it) the string is missing a character from the end. Any ideas why? I am very new to Java and have been fiddling with this for hours with no luck.

The file war_and_peace.txt just contains the string "This is some text". decrypted.txt contains "This is some tex" (with no t on the end). Here is the java code:

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

`

解决方案

There is a couple of things not optimal here.

But let's first solve your problem. The reason why the last portion of your input is somehow missing is the padding you specify: none! Without specifying a padding, the Cipher can just operate on full-length blocks (8 bytes for Blowfish). Excess input that is less than a block long will be silently discarded, and there's your missing text. In detail: "This is some text" is 17 bytes long, so two full blocks will be decrypted, and the final 17th byte, "t", will be discarded.

Always use a padding in combination with symmetric block ciphers, PKCS5Padding is fine.

Next, when operating with Cipher, you don't need to implement your own getBytes() - there's String#getBytes already doing the job for you. Just be sure to operate on the same character encoding when getting the bytes and when reconstructing a String from bytes later on, it's a common source of errors.

You should have a look at the JCE docs, they will help you avoiding some of the common mistakes.

For example, using String keys directly is a no-go for symmetric cryptography, they do not contain enough entropy, which would make it easier to brute-force such a key. The JCE gives you theKeyGenerator class and you should always use it unless you know exactly what you are doing. It generates a securely random key of the appropriate size for you, but in addition, and that is something people tend to forget, it will also ensure that it doesn't create a weak key. For example, there are known weak keys for Blowfish that should be avoided in practical use.

Finally, you shouldn't use a deterministic IV when doing CBC encryption. There are some recent attacks that make it possible to exploit this, resulting in total recovery of the message, and that's obviously not cool. The IV should always be chosen at random (using a SecureRandom) in order to make it unpredictable. Cipher does this for you by default, you can simply obtain the used IV after encryption with Cipher#getIV.

On another note, less security-relevant: you should close streams in a finally block to ensure they're closed at all cost - otherwise you will be left with an open file handle in case of an exception.

Here's an updated version of your code that takes all these aspects into account (had to use Strings instead of files in main, but you can simply replace it with what you had there):

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 - 使用blowfish加密时缺少最终字符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆