2016-04-11 4 views
1

Ich benutze RSA verschlüsseln Text und Text entschlüsseln. Der öffentliche Schlüssel und der private Schlüssel werden mit dem OpenSSL-Tool generiert. Ich habe eine "java.lang.ArrayIndexOutOfBoundsException: zu viele Daten für RSA Block" Ausnahme beim Entschlüsseln von Daten festgestellt.java.lang.ArrayIndexOutOfBoundsException: zu viele Daten für RSA-Block

ist die RSA util Klasse:

package studio.uphie.app; 

import android.util.Base64; 

import java.security.KeyFactory; 
import java.security.NoSuchAlgorithmException; 
import java.security.PrivateKey; 
import java.security.PublicKey; 
import java.security.spec.InvalidKeySpecException; 
import java.security.spec.PKCS8EncodedKeySpec; 
import java.security.spec.X509EncodedKeySpec; 

import javax.crypto.Cipher; 

/** 
* Created by Uphie on 2016/4/11. 
*/ 
public class RSA { 

    private static String RSA = "RSA"; 

    /** 
    * 
    * @param text text to be encrypted 
    * @param pub_key rsa public key 
    * @return encrypted data in byte-array form 
    */ 
    public static byte[] encryptData(String text, String pub_key) { 
     try { 
      byte[] data = text.getBytes(); 
      PublicKey publicKey = getPublicKey(Base64.decode(pub_key.getBytes(), Base64.DEFAULT)); 

      Cipher cipher = Cipher.getInstance(RSA); 
      cipher.init(Cipher.ENCRYPT_MODE, publicKey); 
      return cipher.doFinal(data); 
     } catch (Exception e) { 
      e.printStackTrace(); 
      return null; 
     } 
    } 

    /** 
    * 
    * @param text text to be decrypted 
    * @param pri_key rsa private key 
    * @return 
    */ 
    public static byte[] decryptData(String text, String pri_key) { 
     try { 
      byte[] data = text.getBytes(); 
      PrivateKey privateKey = getPrivateKey(Base64.decode(pri_key.getBytes(),Base64.DEFAULT)); 

      Cipher cipher = Cipher.getInstance(RSA); 
      cipher.init(Cipher.DECRYPT_MODE, privateKey); 
      return cipher.doFinal(data); 
     } catch (Exception e) { 
      //"java.lang.ArrayIndexOutOfBoundsException: too much data for RSA block" exception occurs here. 
      return null; 
     } 
    } 

    /** 
    * 
    * @param keyBytes 
    * @return 
    * @throws NoSuchAlgorithmException 
    * @throws InvalidKeySpecException 
    */ 
    public static PublicKey getPublicKey(byte[] keyBytes) throws NoSuchAlgorithmException, InvalidKeySpecException { 
     X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); 
     KeyFactory keyFactory = KeyFactory.getInstance(RSA); 
     return keyFactory.generatePublic(keySpec); 
    } 

    /** 
    * 
    * @param keyBytes 
    * @return 
    * @throws NoSuchAlgorithmException 
    * @throws InvalidKeySpecException 
    */ 
    public static PrivateKey getPrivateKey(byte[] keyBytes) throws NoSuchAlgorithmException, 
      InvalidKeySpecException { 
     PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); 
     KeyFactory keyFactory = KeyFactory.getInstance(RSA); 
     return keyFactory.generatePrivate(keySpec); 
    } 
} 

Und das Snippet, das und entschlüsselt Daten verschlüsselt:

//encrypt 
byte[] e = RSA.encryptData(text, PUBLIC_KEY); 
String result = Base64.encodeToString(e, Base64.DEFAULT); 
tv_encrypted.setText(result); 

//decrypt 
byte[] d = RSA.decryptData(text, PRIVATE_KEY); 
String result = Base64.encodeToString(d, Base64.DEFAULT); 
tv_decrypted.setText("Decrypted result:\n" + result); 

ich der Grund, dass der Text zu lang entschlüsselt werden kann ist, aber ich verschlüssele einfach "abc" und entschlüssele dann das verschlüsselte "abc". Und wie geht man mit der Verschlüsselung von Langtext um, wenn der zu verschlüsselnde oder entschlüsselte Text um 11 Bytes kleiner sein sollte als der private Schlüssel rsa? Wie kann ich tun, um es zu lösen? Ich bin neu in RSA.

Vielen Dank im Voraus!

Antwort

5

Ihnen fehlen einige Schritte in Ihrem Code, die es unmöglich machen, zu überprüfen. Es gibt jedoch einige Hinweise, die auf ein Problem hinweisen. Ihre decryptData Methode nimmt ein String-Argument und ruft dann String.getBytes() auf, um die Daten zu erhalten, die dann entschlüsselt werden. Das Ergebnis der Verschlüsselung ist jedoch eine Folge von Bytes, bei der es sich nicht um eine gültige Zeichenfolge handelt. Vielleicht wollten Sie base64 die Eingabe dekodieren anstatt getBytes() anzurufen. Um die Entschlüsselung und Decodierung durchzuführen, müssen Sie die Schritte, die Sie während der Verschlüsselung und Codierung durchgeführt haben, in umgekehrter Reihenfolge durchführen. Also, wenn der Klartext ist ein byte [] dann die Schritte sind:

byte [] → Encrypt → byte [] → Base64 kodieren → String.

dann in der Entschlüsselungs Richtung beginnen Sie mit einem Base64-String, müssen Sie in der Reihenfolge:

String → Base64 dekodieren → byte [] → entschlüsseln → byte []

Auch eine andere Frage, die ist eine schlechte Praxis und eine Quelle für viele Portabilität Bugs ist die Verwendung von Standardeinstellungen. Sie verwenden Standardeinstellungen an zwei Orten und sie sind beide problematisch. Zuerst verwenden Sie die Standardmethode no-args String.getBytes() und stimmen diese vermutlich mit dem Konstruktor one-arg String (byte []) überein. Verwenden Sie den Standardzeichensatz der Plattform, dies kann jedoch auf verschiedenen Plattformen unterschiedlich sein. Geben Sie daher immer einen Zeichensatz an. Für die meisten Anwendungen ist UTF-8 die ideale Wahl. Zweitens rufen Sie Cipher.getInstance('RSA') ohne Angabe von Auffüllen. Oracle's Java und Android's Java werden Ihnen eine andere Auffüllung geben und somit wird Ihr Code nicht zwischen den Plattformen portierbar sein. Geben Sie immer die vollständige Füllzeichenfolge an.Hier ist die Auswahl etwas schwieriger, wenn Sie eine Portabilität für ältere Java-Implementierungen benötigen. OAEP Padding sollte Ihre erste Wahl sein, also Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); ist wahrscheinlich die richtige Wahl. Weitere Informationen finden Sie unter this.

Informationen zum Verschlüsseln längerer Texte finden Sie unter answer from Henry.

+0

Danke für deine Hilfe! Vielleicht habe ich viel von dieser Frage. Openssl erzeugt unlesbare Byte-Sequenzen: public key und private key und konvertiert sie in base64-kodierten Text, der lesbar ist. Beim Verschlüsseln oder Entschlüsseln benötigt die Chiffre den Bytecode des Textes und des Schlüssels. Ich nahm Ihren Rat und änderte meine RSA-Util-Klasse und es funktioniert gut. – Uphie

0

Normalerweise generieren Sie einen zufälligen geheimen Schlüssel für eine symmetrische Chiffre (wie AES) und verwenden dies, um Ihre Payload zu verschlüsseln.

RSA wird dann nur verwendet, um diesen zufälligen Schlüssel zu verschlüsseln. Dies gilt nicht nur die Länge Problem zu lösen, hat aber einige andere Vorteile auch:

  • Symmetric Chiffren sind in der Regel viel schneller
  • Wenn die Nachricht an mehrere Empfänger verschickt gesendet wird, hat nur der verschlüsselte Schlüssel hinzugefügt werden, die speziell für jeder Empfänger kann der Hauptinhalt gleich sein.
+0

Ja, ist es. RSA kann langen Text nicht verschlüsseln, was schade ist, und wird normalerweise mit der sysmmetrischen Chiffre AES oder DES verwendet, um den secrect-Schlüssel der sysmmetrischen Chiffre zu verschlüsseln. Und die sysmmetrische Chiffre verschlüsselt jeden Text. Vielen Dank! – Uphie

1

Fianlly modifizierte ich meine Codes so, und sie funktionieren gut:

public static String encryptData(String text, String pub_key) { 
     try { 
      byte[] data = text.getBytes("utf-8"); 
      PublicKey publicKey = getPublicKey(Base64.decode(pub_key.getBytes("utf-8"), Base64.DEFAULT)); 
      Cipher cipher = Cipher.getInstance(RSA); 
      cipher.init(Cipher.ENCRYPT_MODE, publicKey); 
      return Base64.encodeToString(cipher.doFinal(data),Base64.DEFAULT); 
     } catch (Exception e) { 
      e.printStackTrace(); 
      return null; 
     } 
    } 
    public static String decryptData(String text, String pri_key) { 
     try { 
      byte[] data =Base64.decode(text,Base64.DEFAULT); 
      PrivateKey privateKey = getPrivateKey(Base64.decode(pri_key.getBytes("utf-8"),Base64.DEFAULT)); 

      Cipher cipher = Cipher.getInstance(RSA); 
      cipher.init(Cipher.DECRYPT_MODE, privateKey); 
      return new String(cipher.doFinal(data),"utf-8"); 
     } catch (Exception e) { 
      return null; 
     } 
    } 

Wenn etwas noch falsch scheint, dass Sie mich daran erinnern kann. Danke für die Antwort von James und Henry.