web-dev-qa-db-ja.com

IllegalBlockSizeExceptionの取得:rsaを使用する場合、データは256バイト以下でなければなりません

私はサーバーに送信する長い文字列を暗号化するためにRSAキーを使用しています(サーバーの公開鍵と秘密鍵で暗号化します)が、javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytesのような例外をスローしますこれまでのrsaの適切な機能(組み込みライブラリを使用することが原因です)。
この例外がスローされる理由を説明してください。長い文字列を暗号化して送信することはまったく不可能ですか?

40
Ashwin

RSAアルゴリズムでは、ビット単位のRSAキー長の最大バイト長を8マイナス11パディングバイトで割ったデータのみを暗号化できます。つまり、最大バイト数=ビット単位のキー長/ 8-11です。

したがって、基本的に、キーの長さを8〜11で除算します(パディングがある場合)。たとえば、2048ビットキーがある場合は、2048/8 = 256バイト(パディングがある場合は11バイト)を暗号化できます。そのため、より大きなキーを使用するか、対称キーでデータを暗号化し、そのキーをrsaで暗号化します(推奨されるアプローチです)。

それには次のことが必要になります。

  1. 対称鍵を生成します
  2. 対称キーでデータを暗号化する
  3. RSAを使用して対称キーを暗号化する
  4. 暗号化されたキーとデータを送信する
  5. 暗号化された対称キーをrsaで復号化する
  6. 対称キーを使用してデータを復号化する
  7. 完了:)
66
John Snow

@John Snowの回答に基づいて、私は例を行いました

  1. 対称キーの生成(128ビットのAES)

    KeyGenerator generator = KeyGenerator.getInstance("AES");
    generator.init(128); // The AES key size in number of bits
    SecretKey secKey = generator.generateKey();
    
  2. AESを使用してプレーンテキストを暗号化する

    String plainText = "Please encrypt me urgently..."
    Cipher aesCipher = Cipher.getInstance("AES");
    aesCipher.init(Cipher.ENCRYPT_MODE, secKey);
    byte[] byteCipherText = aesCipher.doFinal(plainText.getBytes());
    
  3. RSA公開キーを使用してキーを暗号化する

    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
    kpg.initialize(2048);
    KeyPair keyPair = kpg.generateKeyPair();
    
    PublicKey puKey = keyPair.getPublic();
    PrivateKey prKey = keyPair.getPrivate();
    
    Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    cipher.init(Cipher.PUBLIC_KEY, puKey);
    byte[] encryptedKey = cipher.doFinal(secKey.getEncoded()/*Seceret Key From Step 1*/);
    
  4. 暗号化されたデータ(byteCipherText)+暗号化されたAESキー(encryptedKey)を送信します

  5. クライアント側で、RSA秘密鍵を使用して対称鍵を復号化します

    cipher.init(Cipher.PRIVATE_KEY, prKey);
    byte[] decryptedKey = cipher.doFinal(encryptedKey);
    
  6. 復号化された対称キーを使用して暗号を復号化します

    //Convert bytes to AES SecertKey
    SecretKey originalKey = new SecretKeySpec(decryptedKey , 0, decryptedKey .length, "AES");
    Cipher aesCipher = Cipher.getInstance("AES");
    aesCipher.init(Cipher.DECRYPT_MODE, originalKey);
    byte[] bytePlainText = aesCipher.doFinal(byteCipherText);
    String plainText = new String(bytePlainText);`
    
31

秘密データにRSAを直接使用しないでください。セッションキーやメッセージ認証コードなどのpseudo-randomまたはcompletely randomデータでのみRSAを使用する必要があります。

256バイトで問題が発生したのは、おそらく2048ビットキーで作業しているためです。キーは、0から2^2048 - 1の範囲の整数を同じ範囲に暗号化できます。つまり、データは256バイト以下でなければなりません。

これ以上暗号化する場合は、1つのRSA暗号化を使用して対称アルゴリズムのセッションキーを暗号化し、thatを使用してデータを暗号化してください。

11
sarnold

上記のJohn Snowの回答をフォローするために、秘密鍵を使用して任意の長さのデータを単純に暗号化するために使用できる単純なランダム対称暗号ライブラリを作成しました。

ライブラリは GitHub-random-symmetric-crypto にあります。

 final RandomSymmetricCipher cipher = new RandomSymmetricCipher();

 // Encrypt the data and the random symmetric key.
 final CryptoPacket cryptoPacket = cipher.encrypt(inputData, PRIVATE_KEY_BASE64);

 // Convert the CryptoPacket into a Base64 String that can be readily reconstituted at the other end.
 final CryptoPacketConverter cryptoPacketConverter = new CryptoPacketConverter();
 final String base64EncryptedData = cryptoPacketConverter.convert(cryptoPacket);
 System.out.println("Base64EncryptedData=" + base64EncryptedData);

 // Decrypt the Base64 encoded (and encrypted) String.
 final byte[] outputData = cipher.decrypt(base64EncryptedData, PUBLIC_KEY_BASE64);
3
William