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

サーバーの実行中に、ファイルからその文字列を変更できます (その関数を何度も実行します)。問題は、サーバーを閉じてから再度開くと、次のエラーが発生することです。

これは、サーバーが開いたときにファイルから読み取る方法です。

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(バッファ)で読み取ると、多くのスペースが追加されるようです。

このすべての変更を行いましたが、まだエラー javax.crypto.BadPaddingException: Given final block が適切に埋め込まれていません

ここで何が起こっているのか誰か知っていますか?

4

4 に答える 4

1

いくつかの問題があります。読み取りおよび復号化プロセスは、暗号化および書き込みプロセスと対称的である必要があります。しかし

  • を使用して String を byte[] に変換しますが、これは問題ありませんが、逆の操作はgetBytes("UTF8")使用しません。new String(byte[], "UTF8")
  • 潜在的な改行を含め、文字列全体をファイルに書き込みますが、行ごとに読み取り、各行を連結するため、プロセスで改行が失われます。書き込まれたすべての文字を読み取る必要があります。

また、sun.misc.Base64Encoder/Decoder のような文書化されていない、サポートされていないクラスに依存することはすべきではありません。Apache commons-codec を使用して、文書化された Base64 エンコーディングを見つけます。これは、次の JDK が出たときにも存在することが保証されており、Sun 以外の JVM を含むすべての JVM で使用できます。

于 2012-12-13T22:06:56.370 に答える
1

ここにはいくつかのことがあります。

private static String bytes2String(byte[] bytes)

危険です。このメソッドでバイトを char にキャストしているため、ここで指定されている文字エンコーディングはありません。バイトを文字に変換するには、バイト配列とエンコーディングを受け取る 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);

入力ストリームはバイトを返しています。バイトは文字ではなく、文字にキャストするだけで問題が発生します。暗号化を使用する場合、暗号化操作からの出力はバイナリ データであり、復号化操作への入力もバイナリ データです。テキストを暗号化しているという事実は、暗号化が発生する前と復号化が発生した後に対処するものです。基本的な操作は、次の行に沿って進む必要があります。

  • 暗号化するテキストを取得してバイトに変換し、String で .getBytes(String charsetName) を使用してエンコーディングを指定します。
  • これらのバイトを暗号化ルーチンに渡します
  • 結果のバイトをディスクに直接書き込む

復号化するには:

  • ファイルからバイトを読み取る
  • バイトを復号化ルーチンに渡します(バイトとして!文字列/テキストは含まれません)
  • 出力バイトを取得し、以前と同じエンコーディングを指定して new String(byte[] bytes, String charsetName) を使用して String を再構築します。

次の (テストされていませんが、動作するはずの) メソッドが役立つ場合があります。

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

これが主な問題かどうかはわかりませんが、復号化された文字列を から返すときは、次decrypt()を使用する必要があります。

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

初期化中だと思う

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

于 2012-12-13T21:59:14.500 に答える