こんにちは、Habr! Javaの暗号化の基礎を学びたい初心者向けの一連の記事のうち 、Jakob Jenkovによる2番目の記事「Java Cipher」の翻訳を紹介します。
目次:
Java Cipher(暗号)
Java Cipherクラス( javax.crypto.Cipher )は暗号化アルゴリズムです。 「暗号」という用語は、暗号化の世界における暗号化アルゴリズムの標準用語です。 これが、JavaクラスがEncryptor / DecryptorなどではなくCipherと呼ばれる理由です。 Cipherインスタンスを使用して、Javaでデータを暗号化および復号化できます。 この章では、Cipherクラスの仕組みについて説明します。
暗号作成
暗号を使用する前に、使用する暗号化アルゴリズムのタイプを示すパラメーターを指定してgetInstance()メソッドを呼び出して、 Cipherクラスのインスタンスを作成する必要があります。 Java Cipherインスタンスを作成する例を次に示します。
Cipher cipher = Cipher.getInstance("AES");
この例では、AES暗号化アルゴリズムを使用してCipherのインスタンスを作成します。
暗号化モード
一部の暗号化アルゴリズムは異なるモードで機能する場合があります。 暗号化モードは、データの暗号化方法の詳細を決定します。 したがって、暗号化モードは暗号化アルゴリズムに部分的に影響します。 暗号化モードは、メインの暗号化アルゴリズムに追加される方法として、いくつかの異なる暗号化アルゴリズムで使用される場合があります。 そのため、モードは暗号化アルゴリズム自体とは別に、暗号化アルゴリズムへの「追加」と見なされます。 以下は、最もよく知られている暗号化モードの一部です。
- EBC-電子コードブック(電子コードブックモード )
- CBC-暗号ブロック連鎖
- CFB-暗号フィードバック(暗号フィードバックモード )
- OFB-出力フィードバック
- CTR-カウンター(カウンターモード )
暗号のインスタンスを作成するときに、暗号化アルゴリズムの名前にモードを追加できます。 ブロックカップリングモード-Cipher Block Chaining(CBC)を使用してAES Cipherのインスタンスを作成するには、次のようにします。
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
暗号化ブロックの結合モードも「パディング方式」を必要とするため、パディング方式( PKCS5Padding )が暗号化アルゴリズム名の文字列の最後に追加されます。
すべての暗号化アルゴリズムとモードがJava SDK暗号化プロバイダーによってデフォルトでサポートされているわけではないことを知っておくことが重要です。 必要なモードと塗りつぶしパターンで必要な暗号インスタンスを作成するには、Bouncy Castleなどのサードパーティプロバイダーをインストールする必要がある場合があります。
暗号の初期化
Cipherインスタンスを使用する前に、初期化する必要があります。 暗号は、 init()メソッドを呼び出すことにより初期化されます。 init()メソッドは2つのパラメーターを取ります。
- モード
- キー
暗号化モードでCipherインスタンスを初期化する例:
Key key = ... // / cipher.init(Cipher.ENCRYPT_MODE, key);
そして、これはすでに復号化モードで暗号インスタンスを初期化する例です:
Key key = ... /// cipher.init(Cipher.DECRYPT_MODE, key);
データの暗号化と復号化
Cipherインスタンスを使用してデータを暗号化または復号化するには、次の2つの方法のいずれかを呼び出します。
-
update()
-
doFinal()
さまざまなパラメーターを取るupdate()およびdoFinal()メソッドのオーバーライドされたバージョンがいくつかあります。 ここで最も一般的に使用されるものを検討してください。 単一のデータブロックを暗号化または復号化する必要がある場合は、暗号化または復号化するデータを指定してdoFinal()を呼び出すだけです。 例:
byte[] plainText = "abcdefghijklmnopqrstuvwxyz".getBytes("UTF-8"); byte[] cipherText = cipher.doFinal(plainText);
実際、コードはデータの復号化の場合とほぼ同じに見えます。 Cipherインスタンスは復号化モードで初期化する必要があることに注意してください。 暗号文の1ブロックの復号化は次のようになります。
byte[] plainText = cipher.doFinal(cipherText);
複数のブロックに分割された大きなファイルを暗号化または復号化する必要がある場合、各データブロックに対してupdate()が1回呼び出され、最後のデータブロックでdoFinal()メソッドの呼び出しで終了します。 複数のデータブロックを暗号化する例を次に示します。
byte[] data1 = "abcdefghijklmnopqrstuvwxyz".getBytes("UTF-8"); byte[] data2 = "zyxwvutsrqponmlkjihgfedcba".getBytes("UTF-8"); byte[] data3 = "01234567890123456789012345".getBytes("UTF-8"); byte[] cipherText1 = cipher.update(data1); byte[] cipherText2 = cipher.update(data2); byte[] cipherText3 = cipher.doFinal(data3);
最後のデータブロックにdoFinal()呼び出しが必要な理由は、特定の暗号ブロックサイズ(たとえば、8バイト境界)に適合するために一部の暗号化アルゴリズムがデータを補完する必要があるためです。 中間の暗号化されたデータを補足する必要はありません。 したがって、中間データブロックに対してupdate()メソッドが呼び出され、最後のデータブロックに対してdoFinal()呼び出しが呼び出されます。
複数のデータブロックを復号化するときは、中間データブロックに対してupdate()メソッドを呼び出し、最後のブロックに対してdoFinal()メソッドも呼び出します。 複数のデータブロックの復号化の例:
byte[] plainText1 = cipher.update(cipherText1); byte[] plainText2 = cipher.update(cipherText2); byte[] plainText3 = cipher.doFinal(cipherText3);
この例でも、暗号インスタンスは復号化モードで初期化する必要があります。
バイト配列の一部の暗号化/復号化
Cipherクラスの暗号化および復号化メソッドは、バイト配列に格納されたデータの一部を暗号化または復号化できます。 update()および/またはdoFinal()メソッドは、オフセットと長さを渡す必要があります。
int offset = 10; int length = 24; byte[] cipherText = cipher.doFinal(data, offset, length);
この例では、インデックス10以降の24バイトは暗号化されます(暗号の初期化に応じて復号化されます)。
既存のバイト配列に対する暗号化/復号化
この章の暗号化および復号化の例はすべて、暗号化または復号化されたデータを新しいバイト配列で返します。 ただし、既存のバイト配列にデータを暗号化または復号化することもできます。 これは、作成されるバイト配列の数を減らすのに役立ちます。 これを行うには、ターゲットバイト配列をパラメーターとしてupdate()および/またはdoFinal()メソッドに渡します。
int offset = 10; int length = 24; byte[] dest = new byte[1024]; cipher.doFinal(data, offset, length, dest);
この例では、データは10インデックス24バイトから0バイトのオフセットでdestバイト配列に暗号化されます。destバイト配列に異なるオフセットを設定する場合、追加のオフセットパラメータを受け入れるupdate()およびdoFinal()バージョンがあります。 dest配列にオフセットを指定してdoFinal()メソッドを呼び出す例:
int offset = 10; int length = 24; byte[] dest = new byte[1024]; int destOffset = 12 cipher.doFinal(data, offset, length, dest, destOffset);
暗号のインスタンスを再利用する
Cipherインスタンスの初期化は高価な操作であり、Cipherインスタンスを再利用することは良い考えです。 さいわい、Cipherクラスは再利用性を考慮して設計されています。 CipherインスタンスでdoFinal()メソッドを呼び出すと、初期化直後の状態に戻ります。 その後、Cipherインスタンスを使用して、より多くのデータを暗号化または復号化できます。
Java Cipherインスタンスを再利用する例:
Cipher cipher = Cipher.getInstance("AES"); Key key = ... /// cipher.init(Cipher.ENCRYPT_MODE, key); byte[] data1 = "abcdefghijklmnopqrstuvwxyz".getBytes("UTF-8"); byte[] data2 = "zyxwvutsrqponmlkjihgfedcba".getBytes("UTF-8"); byte[] cipherText1 = cipher.update(data1); byte[] cipherText2 = cipher.doFinal(data2); byte[] data3 = "01234567890123456789012345".getBytes("UTF-8"); byte[] cipherText3 = cipher.doFinal(data3);
最初に、Cipherインスタンスが作成および初期化され、その後、一貫性のあるデータの2つのブロックの暗号化に使用されます。 これらの2つのデータブロックに対してupdate()を呼び出してからdoFinal()を呼び出すことに注意してください。 その後、Cipherインスタンスを再度使用してデータを暗号化できます。 これは、3番目のデータブロックでdoFinal()を呼び出すことによって行われます。 このdoFinal()の呼び出し後、同じJava Cipherインスタンスで別のデータブロックを暗号化できます。