2017-10-27 10 views
-3

暗号化復号化アプリケーションを作成しようとしています。私は2つのクラスを持っています - 一つはキーを生成する関数、もう一つは暗号化と復号化、もう一つはJavaFX GUIです。 GUIクラスでは、私は4つのテキストエリアを持っています:1番目は暗号化するテキストを書き、2番目は暗号化されたテキスト、3番目はキー()、4番目は復号化されたテキストです。文字列からSecretKeyを正しく再作成する方法

問題は、私はテキストを復号化できません。ここに私は[email protected]と私はjavax.crypto.IllegalBlockSizeException: Input length must be multiple of 8 when decrypting with padded cipher

を取得しています:私はこのような重要なルックス暗号化すると

String encodedKey = textAreaKey.getText();     
byte[] decodedKey = Base64.getDecoder().decode(encodedKey); 
SecretKey klucz = new SecretKeySpec(decodedKey, "DESede"); 

[email protected]を、私はそれを再作成しようとすると、私はこのようなのSecretKeyを再作成しようとしていますファーストクラス:

public class Encryption { 

    public static SecretKey generateKey() throws NoSuchAlgorithmException { 
     Security.addProvider(new com.sun.crypto.provider.SunJCE()); 
     KeyGenerator keygen = KeyGenerator.getInstance("DESede"); 
     keygen.init(168); 
     SecretKey klucz = keygen.generateKey(); 

     return klucz; 
    } 

    static byte[] encrypt(byte[] plainTextByte, SecretKey klucz) 
     throws Exception { 
     Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding"); 
     cipher.init(Cipher.ENCRYPT_MODE, klucz); 
     byte[] encryptedBytes = cipher.doFinal(plainTextByte); 
     return encryptedBytes; 
    } 

    static byte[] decrypt(byte[] encryptedBytes, SecretKey klucz) 
     throws Exception { 
     Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding"); 
     cipher.init(Cipher.DECRYPT_MODE, klucz); 
     byte[] decryptedBytes = cipher.doFinal(encryptedBytes); 
     return decryptedBytes; 
    } 
} 

編集

btnEncrypt.setOnAction((ActionEvent event) -> { 
     try { 
      String plainText = textAreaToEncrypt.getText(); 
      SecretKey klucz = Encryption.generateKey();     
      byte[] plainTextByte = plainText.getBytes();     
      byte[] encryptedBytes = Encryption.encrypt(plainTextByte, klucz);     
      String encryptedText = Base64.getEncoder().encodeToString(encryptedBytes); 
      textAreaEncryptedText.setText(encryptedText);     
      byte[] byteKey = klucz.getEncoded(); 
      String stringKey = Base64.getEncoder().encodeToString(byteKey); 
      textAreaKey.setTextstringKey 
     } catch (Exception ex) { 
      ex.printStackTrace(); 
     } 
    }); 

    btnDecrypt.setOnAction((ActionEvent event) -> { 
     try { 
      String stringKey = textAreaKey.getText();     
      byte[] decodedKey = Base64.getDecoder().decode(encodedKey); 
      SecretKey klucz2 = new SecretKeySpec(decodedKey, "DESede");     
      String encryptedText = textAreaEncryptedText.getText(); 
      byte[] encryptedBytes = Base64.getDecoder().decode(encryptedText.getBytes());     
      byte[] decryptedBytes = Encryption.decrypt(encryptedBytes, klucz2; 
      String decryptedText = Base64.getEncoder().encodeToString(decryptedBytes); 

      textAreaDecryptedText.setText(decryptedText); 

     } catch (Exception ex) { 
      ex.printStackTrace(); 
     } 
    }); 
+1

あなたが投稿したコードを逐語的に使用しましたが、再現できませんでした。だから問題はあなたが投稿していないコードになければならない。 https://gist.github.com/jnizet/5ce2971a94fb6f88a746607243613ca7 –

+0

1. 3DESが必要な新しいプロジェクト、学校プロジェクトには使用すべきではない3DES(DESede)を使用する理由は何ですか? 2. 3DESキーは168ビットのキーで24バイト、 "[email protected]"は3DESキーではありません。 3DESキーは24バイトですが、一部の実装では短いキーを受け入れ、24バイトのキーを作成する部分を複製します。 3.なぜ、Base64エンコードの3番目のフィールドは、テキストではありませんか? 4.パディングは言及されていませんが、すべてのパラメータを明示的に指定することをお勧めします。 5. 'Cipher.ENCRYPT_MODE'の値は何ですか? – zaph

+0

1.はい、それは学校のプロジェクト です。2.正しい3DESキーを提供する方法を理解することに問題があったため、その部分がインターネットのどこかに見つかりました。私はこれの代わりに何を使うべきですか? 3.最初に、暗号化によって、私はSecretKeyをStringに変換するので、 'textAreaKey'にそれを表示することができます。次に、解読することによって、 'textAreaKey'のテキストをSecretKey(質問に追加されたコード)に変換しています。 4.ここで何を意味するのか分かりません。私はむしろ初心者です。 5.「System.out.println( "Value of Cipher.ENCRYPT_MODE:" + Cipher.ENCRYPT_MODE); "これはちょうど' 1'を返します。 – zygmunt

答えて

2

あなたの問題の一つはここにある:一般

String encryptedText = new String(encryptedBytes, "UTF8"); 

、暗号文の多くのバイトシーケンスは、有効なUTF-8 –エンコードされた文字ではありません。 Stringを作成しようとすると、この不正なシーケンスが「置換文字」に置き換えられ、暗号テキストからの情報が回復不能に失われます。 Stringをバイトに変換して復号化しようとすると、破損した暗号テキストによってエラーが発生します。

暗号テキストを文字列として表す必要がある場合は、キーと同様に、ベース64エンコーディングを使用します。

もう1つの主な問題は、完全な変換を指定していないことです。 「DESede/ECB/PKCS5Padding」のように、暗号の「モード」と「パディング」を明示的に指定する必要があります。

正しいモードは、割り当てによって異なります。 ECBは一般的に安全ではありませんが、より安全なモードは少し複雑になり、割り当ての範囲外になる可能性があります。あなたの指示を勉強し、必要に応じて教師との要件を明確にします。

+0

だから、私はそれを 'String encryptedText = Base64.getEncoder()。encodeToString(encryptedBytes);'に置き換えました。しかし、この "DESede/ECB/PKCS5Padding"はどうですか?私はすべての "DESEDE"をそれに置き換えるべきですか?私が試したところ、 'java.security.NoSuchAlgorithmException:DESede/ECB/PKCS5Padding KeyGeneratorが利用できませんでした。 '彼は本当に気にしていない、私はすでにこの課題のマークを持っていたが、私はアプリを再起動しなかった場合にのみ解読していた、私は自分自身のためにこれを改善し、スイングの代わりにJavaFXでそれをやろうとした。 – zygmunt

+0

Hmm今、 'generateKey()'と 'DESede/ECB/PKCS5Padding'の' encrypt() 'と' decrypt() 'に' DESede'を設定したとき、 'NoSuchAlgorithmException'を得ていないとき、しかし、私はまだ解読でエラーが出ます: 'java.security.InvalidKeyException:間違ったアルゴリズム:DESedeまたはTripleDESが必要です' – zygmunt

+1

@zygmuntあなたの現在のコードを見なければなりません。解読のためにあなたの 'SecretKeySpec'を作成するときに" DESede "を指定していますか? – erickson

2

は、次の2つの主要な問題があります:あなたはキーとしてユーザが入力したパスワードを使用しないでください

  1. (両者の差があります)。キーは、暗号に応じて特定のサイズを持つ必要があります(3desの場合は16または24バイト)

  2. Direct 3DES(DESede)は8バイトを一度に暗号化するブロック暗号です。複数のブロックを暗号化するには、それを正しく行う方法がいくつか定義されています。それはBlock cipher modeです。パスワードからキーを作成するいくつかのより多くの事

    の世話をする必要があり、適切な暗号化のための

はあなたがDESEDE(3DES)を使用したいと仮定しましょう。 キーは固定サイズ(16バイトまたは24バイト)でなければなりません。パスワードから鍵を正しく生成するには、PBKDFを使用する必要があります。「使用する必要があります」と敏感な人もいますが、このステップを無視すると、主にユーザーが入力したパスワードを使用して暗号化のセキュリティが侵害されます。あなたが使用することができ3DESについては

 int keySize = 16*8; 
     int iterations = 800000; 
     char[] password = "password".toCharArray(); 
     SecureRandom random = new SecureRandom(); 
     byte[] salt = random.generateSeed(8); 

     SecretKeyFactory secKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512"); 
     KeySpec spec = new PBEKeySpec(password, salt, iterations, keySize); 
     SecretKey pbeSecretKey = secKeyFactory.generateSecret(spec); 
     SecretKey desSecret = new SecretKeySpec(pbeSecretKey.getEncoded(), "DESede"); 

     // iv needs to have block size 
     // we will use the salt for simplification 
     IvParameterSpec ivParam = new IvParameterSpec(salt); 

     Cipher cipher = Cipher.getInstance("DESEde/CBC/PKCS5Padding"); 
     cipher.init(Cipher.ENCRYPT_MODE, desSecret, ivParam); 

     System.out.println("salt: "+Base64.getEncoder().encodeToString(salt)); 
     System.out.println(cipher.getIV().length+" iv: "+Base64.getEncoder().encodeToString(cipher.getIV())); 
     byte[] ciphertext = cipher.doFinal("plaintext input".getBytes()); 
     System.out.println("encrypted: "+Base64.getEncoder().encodeToString(ciphertext)); 

を、あなたのパスワードが良いエントロピーを持っていることを確認することができた場合(長いと十分にランダムである)あなたは、単純なハッシュ

 MessageDigest dgst = MessageDigest.getInstance("sha-1"); 
     byte[] hash = dgst.digest("some long, complex and random password".getBytes()); 
     byte[] keyBytes = new byte[keySize/8]; 
     System.arraycopy(hash, 0, keyBytes, 0, keySize/8); 
     SecretKey desSecret = new SecretKeySpec(keyBytes, "DESede"); 

と良いかもしれは出力をランダム化するために使用されるため、使用する必要があります。

暗号化の出力は、salt | cipthertext | tagである必要があります(必ずしもこの順ではありませんが、適切な暗号化のためにこれらのすべてが必要です)。

出力を復号化するには、出力をsalt、暗号文およびタグに分割する必要があります。

StackOverflowの例では非常に頻繁にゼロベクトル(static saltまたはiv)が表示されますが、多くの場合、キーまたは平文の壊れた暗号が破損する可能性があります。

初期化ベクトルIVは、(単一ブロックよりも長い入力を暗号化)ブロック連鎖モードのために必要とされるのと同じ大きさを有する場合、我々は、我々の場合には(8バイト ならびにキーから塩を使用することができ)。本当に安全なソリューションのためには、パスワードの塩はもっと長くする必要があります。

タグは認証タグであり、誰も暗号文を操作していないことを保証します。平文または暗号文のHMACを使用できます。 HMACでは暗号化とは異なるキーを使用することが重要です。しかし、あなたの場合、あなたの宿題はhmacタグなしでもOKになると信じています。

+0

明日はそれを詳しく見ていきますが、 – zygmunt

+1

@zygmunt暗号が正しく機能するのは難しいです。人々は細部を無視して、実装が壊れてしまうでしょう。セキュリティ製品を作成するときは、何をしているのかを理解してください:/ – gusto2

+0

@zygmunt )[ブログ投稿](https://gusto77.wordpress.com/2017/10/30/encryption-reference-project/)では、「パスワードの対称暗号化」をチェックすることができます。パスワードから暗号化キーを作成する – gusto2

関連する問題