1

我正在编写一个程序,在其中获取一个字符串,对其进行加密,然后将其写入文件中。然后,我从文件中读取字符串,解密然后修改它。这是我的 DES 加密/解密代码:

/* class for crypting and decrypting a file */
class DESEncrypter
{
private Cipher encryptionCipher;
private Cipher decryptionCipher;

public DESEncrypter (SecretKey key) throws Exception
{
encryptionCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
encryptionCipher.init(Cipher.ENCRYPT_MODE, key);
decryptionCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
decryptionCipher.init(Cipher.DECRYPT_MODE, key);
}

/* write to 'out' the encryption of the information read from 'in' */
public String encrypt(String unencryptedString)
{
    String encryptedString = "";

    try {
        byte[] unencryptedByteArray = unencryptedString.getBytes("UTF8");

        byte[] encryptedBytes = this.encryptionCipher.doFinal(unencryptedByteArray);

        encryptedString = new sun.misc.BASE64Encoder().encode(encryptedBytes);

    } catch (Exception ex) {
        Logger.getLogger(DESEncrypter.class.getName()).log(Level.SEVERE, null, ex);
    }

    return encryptedString;
}

private static String bytes2String(byte[] bytes)
{

    StringBuffer stringBuffer = new StringBuffer();
    for (int i = 0; i < bytes.length; i++) 
    {
        stringBuffer.append((char) bytes[i]);
    }

    return stringBuffer.toString();
}

/* write to 'out' the information obtained by decrypting the information read from 'in' */
public String decrypt (String encryptedString) throws UnsupportedEncodingException
{
    byte[] unencryptedByteArray = new byte[4096];

    try {
        // Encode bytes to base64 to get a string
        byte[] decodedBytes = new sun.misc.BASE64Decoder().decodeBuffer(encryptedString);

       // Decrypt
       unencryptedByteArray = this.decryptionCipher.doFinal(decodedBytes);     
    } catch (Exception ex) {
        Logger.getLogger(DESEncrypter.class.getName()).log(Level.SEVERE, null, ex);
    }

    return bytes2String(unencryptedByteArray);
}
} 

这是我在文件中写入加密字符串的函数:

public void writeToFileEncrypted(String filename, String owner, String departament)
{
try 
    {
        BufferedReader br = new BufferedReader(new FileReader(new File("files_encrypted")));
        String crypt = "";
        String aux;
        while ((aux = br.readLine()) != null)
        {
            crypt += aux;
        }
        br.close();

        String info = this.server.crypt.decrypt(crypt);
        info += filename + " " + owner + " " + departament + "\n";

        /* delete the old encryption */
        File temp = new File("files_encrypted");
        temp.delete();

        String infoCrypt = this.server.crypt.encrypt(info); 

        File newFiles = new File("files_encrypted");
        if (newFiles.createNewFile() == false) 
        {
    log.severe("Failed to re-create the 'files_encrypted' file when trying to add a new file");
    return; 
        }

        BufferedWriter bw = new BufferedWriter(new FileWriter(newFiles));
        bw.write(infoCrypt);
        bw.close();
    }
    catch (Exception e)
    {
        log.warning("An exception was caught while trying to remove '" + clientName + "' from the banned list");
        e.printStackTrace();
        return;
}
}

在服务器运行时,我可以从文件中修改该字符串(多次运行该函数)。问题是当我关闭服务器然后我再次打开它时,因为我收到错误:javax.crypto.BadPaddingException: Given final block not proper padded

这是我在服务器打开时从文件中读取的方式:

BufferedReader br = new BufferedReader(new FileReader(new File("files_encrypted")));
String crypto = new String();
String aux;
while ((aux = br.readLine()) != null)
{
    crypto += aux;
    readBytes++;
}
br.close();
System.out.println(readBytes);
info = this.crypt.decrypt(crypto); 

为什么我会收到这个错误?我做错了什么?我必须以其他方式将加密的字符串写入文件吗?

后期编辑:

我已经更改了从文件中读取字符串、解密、修改、加密然后将其写入文件的函数。

public void writeToFileEncrypted(String filename, String owner, String departament)
{
try 
    {
        File f = new File("files_encrypted");
        int nrRead = 0;
        String info = null;
        FileInputStream fis = new FileInputStream(f);
        StringBuffer sb = new StringBuffer();
        int ch;
        while ((ch = fis.read()) != -1)
        {
            sb.append((char)ch);
            nrRead++;
        }
        fis.close();

        StringBuilder sba = null;
        if (nrRead != 0)
        {
            info = this.server.crypt.decrypt(new String(sb.toString().getBytes("UTF-8"), "UTF-8"));
            sba = new StringBuilder(info);
            sba.append(filename + " " + owner + " " + departament + " ");
        }
        else
        {
            sba = new StringBuilder(filename + " " + owner + " " + departament + " ");
        }

        /* delete the old encryption */
        File temp = new File("files_encrypted");
        temp.delete();
        //System.out.println("before: " + sba.toString());
        String infoCrypt = this.server.crypt.encrypt(sba.toString()); 
        //System.out.println("after: " + infoCrypt);
        File newFiles = new File("files_encrypted");
        if (newFiles.createNewFile() == false) 
        {
    log.severe("Failed to re-create the 'files_encrypted' file when trying to add a new file");
    return; 
        }

        FileOutputStream fos = new FileOutputStream(newFiles);
        fos.write(infoCrypt.getBytes("UTF-8"));
        fos.flush();
        fos.close();
    }
    catch (Exception e)
    {
        log.warning("An exception was caught while trying to remove '" + clientName + "' from the banned list");
        e.printStackTrace();
        return;
}
}

当服务器第一次打开时,我还修改了从文件中读取信息的位置:

FileInputStream fis = new FileInputStream(f);
StringBuffer sb = new StringBuffer();
int ch;
while ((ch = fis.read()) != -1)
{
    sb.append((char)ch);
    readBytes++;
}

fis.close();
if (readBytes != 0)
{
    System.out.println("on: " + sb.toString());
    info = this.crypt.decrypt(new String(sb.toString().getBytes("UTF-8"), "UTF-8"));                
    System.out.println("load: " + info);
}
} 

在带有“on:”的 System.out.println 中,我从文件中读取的内容正是我编写的加密内容,没有任何空格或换行符。如果我用 read(buffer) 读取,其中缓冲区是 byte[],似乎会增加很多空格。

尽管我已经进行了所有这些修改,但我仍然收到错误 javax.crypto.BadPaddingException: Given final block not proper padded

有人知道这里发生了什么吗?

4

4 回答 4

1

你有几个问题。读取和解密过程应该与加密和写入过程对称。但

  • 您使用 将您的 String 转换为 byte[] getBytes("UTF8"),这很好,但您不用于new String(byte[], "UTF8")执行反向操作。
  • 您将整个字符串写入文件,包括潜在的换行符,但您逐行读取并连接每一行,从而在此过程中丢失换行符。您必须阅读已写入的每个字符。

此外,不应该依赖像 sun.misc.Base64Encoder/Decoder 这样的无证、不受支持的类。使用 Apache commons-codec 查找记录在案的 Base64 编码,保证在下一个 JDK 出现时仍然存在,并且可以在每个 JVM 上使用,包括非 Sun JVM。

于 2012-12-13T22:06:56.370 回答
1

这里有几件事。

private static String bytes2String(byte[] bytes)

是狡猾的,你在这个方法中将一个字节转换为一个字符,所以这里没有指定字符编码。要将字节转换为字符,您应该只使用带有字节数组和编码的 String 构造函数。例如

    byte[] tmp = new byte[10];      
    String a = new String(tmp, "UTF-8");

小心使用 BufferedReaders + .readLine() - 这将在您读取文件时从文件中删除任何换行符,除非您将它们添加回缓冲区。虽然我不认为这是你的问题。

但我认为简化代码的最佳方法是通过 OutputStream 将编码字节直接写入文件。除非您需要通过不喜欢二进制数据的传输方式发送文件的内容,否则不需要 base64 编码。只需使用 Input/OutputStreams 将加密字节直接写入磁盘。

对稍后编辑的回应:

您仍在混淆对二进制数据(字节)和字符数据(字符串/字符)的使用。你不能做这样的事情:

    int ch;
    while ((ch = fis.read()) != -1)
    {
        sb.append((char)ch);

输入流正在重新调整字节,一个字节不是一个字符,只是将其转换为一个会导致问题。使用加密时,加密操作的输出是二进制数据,解密操作的输入也是二进制数据。您正在加密文本的事实是您在加密发生之前和解密发生之后处理的事情。您的基本操作应遵循以下原则。

  • 获取要加密的文本并将其转换为字节,使用字符串上的 .getBytes(String charsetName) 指定编码。
  • 将这些字节传递到您的加密例程中
  • 将结果字节直接写入磁盘

解密:

  • 从文件中读取字节
  • 将字节传递给您的解密例程(作为字节!不涉及字符串/文本)
  • 取出输出字节并使用 new String(byte[] bytes, String charsetName) 重新构造您的字符串,指定与以前相同的编码。

您可能会发现以下(未经测试,但应该可以使用)方法很有用:

public byte[] readBinaryFile(File f) throws IOException
{       
    byte[] contents = new byte[(int)f.length()];
    BufferedInputStream bis = null;
    try
    {
        bis = new BufferedInputStream(new FileInputStream(f));
        DataInputStream dis = new DataInputStream(bis);
        dis.readFully(contents);
    }
    finally
    {
        if(bis != null)
        {
            bis.close();
        }
    }           
    return contents;            
}

public void writeBinaryFile(byte[] contents, File f) throws IOException
{
    BufferedOutputStream bos = null;
    try
    {
        bos = new BufferedOutputStream(new FileOutputStream(f));
        bos.write(contents);
    }
    finally
    {
        if(bos != null)
        {
            bos.close();
        }
    }           
}

因此,您还需要更改接口以及加密和解密方法的内部结构,以便它们获取和返回字节数组,并放弃 base64 编码。

于 2012-12-13T22:07:53.513 回答
0

我认为它在初始化中

SecureRandom sr = new SecureRandom();
cipher.init(Cipher.DECRYPT_MODE,desKey,sr);

于 2012-12-13T21:59:14.500 回答
0

不确定这是主要问题,但是当您从 中返回解密的字符串时decrypt(),您应该使用:

return new String(unencryptedByteArray, "UTF-8");
于 2012-12-13T22:02:36.097 回答