Textdateien mit RSA Verschlüsselung

Irgendwie stehe ich auf dem Schlauch und mir sind die Ideen ausgegangen.

Zu mein Problem:
Ich habe Textdateien mit einer Größe zwischen 1KByte und 150KByte. Nun sollen diese Textdateien mit RSA ver/entschlüsselt werden.

Bei meinen ersten (sehr naiven) Ansatz musste ich feststellen, das bei einer RSA Verschlüsselung z.B. mit 512bit die maximal verschlüsselbare Größe 52Byte sind. Danach gibt es Exceptions. Deshalb habe ich mir überlegt die eingehenden Dateien Stückchenweise zu lesen und dann die Stücken ver/entschlüsseln. Das Verschlüsseln müsste gehen, jedenfalls wird ein nicht lesbarer Zeichensalat generiert. Beim entschlüsseln bekomme ich aber Exception die ich nicht verstehe. z.B. ich soll mit einer „Zero“ also mit der Zahl Null oder dem „Wert“ NULL starten?!?

Kann mir jemand einen Tipp geben, wo mein Fehler ist? Was ich auch merkwürdig finde ist, das der verschlüsselte Output Größer als der Input ist. Soweit mir bekannt ist, müssen doch In/Output die gleiche Größe haben? In meinen Test habe ich 12Byte Input und bekomme 64Byte Output. Das Entschlüsseln dieser 64Byte geht ohne Probleme, da der Input nur 12Byte war…

Um das Ganze mal auf dem Punkt zu bringen:
Das Ver/Entschlüsseln geht bis zu einer Größe von 52Byte bei 512Bit Schlüssellänge fehlerfrei. Danach scheint nur das Verschlüsseln zu gehen und das Entschlüsseln nicht. Vielleicht geht auch noch nicht mal das Verschlüsseln, da ich es nicht prüfen kann.

Kann mir jemand einen Tipp oder ein Tutorial geben? Alle Links die ich bis jetzt gefunden habe, handeln immer nur von symmetrischer Verschlüsselung bei Dateien.

PS:
Ich habe BouncyCastle (BC) und die SUN default Implementierung ausprobiert. Bei beiden kommen die gleichen Exception, nur die Fehlerbeschreibungen(getMessage) sind andere.

Hallo,

die Chancen auf eine hilfreiche Antwort sind i.d.R. sehr viel höher wenn man Exceptions beim Namen nennt, den Stacktrace postet und den Sourcecode nicht verheimlicht.

Gruß
Heavy

Hallo,
ein Problem ist sicher, dass du etwas versuchst, was aus gutem Grund niemand so macht:
Asymmetrisches Verschlüsseln von großen Datenmengen ist nicht wirklich praktikabel. Programme wie PGP, GPG etc. lösen das so, dass sie einen zufälligen symmetrischen Schlüssel auswürfeln, und nur diesen asymmetrisch mit dem öffentlichen Schlüssel jedes Empfängers verschlüsseln, was bei bei der Länge eines AES-256-Schlüssels kein Problem gibt.

Das nächste Problem könnte werden, dass die reine RSA-Operation (Exponentiation modulo N) ein Sicherheitsrisiko ist, wenn man kein Padding-Verfahren verwendet, siehe PKCS#11.

Falls eher zu Lernzwecken gedacht: Der einfachste Weg scheint mir zu sein, eine Programmiersprache zu verwenden, die Langzahlarithmetik direkt unterstützt, z. B. Python. Da kann man direkt hinschreiben:

ciphertext = pow(plaintext, privateExponent, modulus)

wobei alles Zahlen mit 512 bit (ziemlich unsicher, 2048 wäre empfehlenswert) sind. Da hier die modulare Reduktion fest mit eingebaut ist, wird man allerdings keine Exception bekommen, wenn man zu langen Text (also eine zu große Zahl) verschlüsselt, das müsste man vorher selbst mit

if plainText \> modulus:
 raise DataError

erledigen.

Grüße, guidot

Hallo,
ein Problem ist sicher, dass du etwas versuchst, was aus gutem
Grund niemand so macht:
Asymmetrisches Verschlüsseln von großen Datenmengen ist nicht
wirklich praktikabel. Programme wie PGP, GPG etc. lösen das
so, dass sie einen zufälligen symmetrischen Schlüssel
auswürfeln, und nur diesen asymmetrisch mit dem öffentlichen
Schlüssel jedes Empfängers verschlüsseln, was bei bei der
Länge eines AES-256-Schlüssels kein Problem gibt.

Ich denke auch das ist die einzige sinnvolle Möglichkeit. Denn genau für diesen Anwendungsfall gibt es schon bequeme/vorgefertigte Methoden in der Java Cryptographic Architecture (JCA)…

Zum Thema RSA Sicherheit es wurde schon ein RSA 768bit Schlüssel geknackt… Also ist alles kleiner gleich 786bit nicht sicher!!!
hier kucks du: http://www.rsa.com/rsalabs/node.asp?id=3723

Aber rein aus Interesse möchte ich es trotzdem wissen, wo mein Fehler ist…
ich habe die Buffergröße für den Iterator fest verdrahtet, da ich bei meinen Schlüssel weiß, das beim verschlüsseln 52 rein => 128 raus und logischer weise beim entschlüsseln 128 rein => 52 raus. Das ist nur zu Testzwecken, wird später entfernt…

hier mein Code:

/\*\*
 \* Test Implementierung fuer die auf Zertifikate basierende Verschluesselung
 \* 
 \* @author Daniel
 \* @version 1.0
 \* @since 06.07.2010
 \*/
public class CertificateBasedRSA implements AsymmetricCryptoTool
{
 /\*\*
 \* Das Chipher Objekt welche mit DES initialisiert wird
 \*/
 private Cipher cipherCert;

 /\*\*
 \* das Encoding fuer die String Repraesentation (siehe
 \* {@link Charset#defaultCharset()})
 \*/
 private String encoding;

 /\*\*
 \* das Key-Paar (oeffentliche und privater Schluessel)
 \*/
 private String keystorePassword;

 /\*\*
 \* das Zertifikat fuer die Ver/Entschluesselung
 \*/
 private Certificate cert;

 /\*\*
 \* das Schluesselpaar (oeffenlicher und privater Schluessel)
 \*/
 private KeyPair keypair;

 /\*\*
 \* der Standard Konstruktor
 \*/
 @SuppressWarnings("nls")
 public CertificateBasedRSA()
 {
 this("", Charset.defaultCharset().name());
 }

 /\*\*
 \* der Konstruktor zum definieren des Passwortes fuer den KeyStore
 \* 
 \* @param keyStorePasswd
 \* das KeyStore Passwort
 \*/
 public CertificateBasedRSA(String keyStorePasswd)
 {
 this(keyStorePasswd, Charset.defaultCharset().name());
 }

 /\*\*
 \* der Konstruktor zum definieren des Passwortes fuer den KeyStore und das
 \* Encoding (siehe {@link Charset#name()})
 \* 
 \* @param keyStorePasswd
 \* das KeyStore Passwort
 \* @param encoding
 \* das Encoding (siehe {@link Charset#name()})
 \*/
 public CertificateBasedRSA(String keyStorePasswd, String encoding)
 {
 this.keystorePassword = keyStorePasswd;
 this.encoding = encoding;
 }

 /\*\*
 \* initialisiere des das Chipher Objekt mit dem DES Algorithmus
 \* 
 \* @param mode
 \* true =\> encrytion, false =\> decrytion
 \* @throws NoSuchAlgorithmException
 \* Wenn der Algorithmus nicht implementiert ist
 \* @throws NoSuchPaddingException
 \* wenn ein Fehler beim Padding auftritt
 \* @throws InvalidKeyException
 \* wenn der Key einen fehlerhaft ist
 \* @throws IOException
 \* Wenn ein Fehler beim Zugriff auf den KeyStore auftritt
 \* @throws CertificateException
 \* Wenn ein Fehler bei der Verarbeitung des Zertifikats auftritt
 \* @throws KeyStoreException
 \* Wenn ein Fehler bei der Initialisierung des KeyStores auftritt
 \* @throws UnrecoverableKeyException
 \* Wenn ein Fehler beim initialisieren des Zertifaktes/Schluessels
 \* auftritt
 \*/
 @SuppressWarnings("nls")
 private void initCipher(boolean mode) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
 IOException, CertificateException, KeyStoreException, UnrecoverableKeyException
 {
 // init keystore
 KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
 char[] password = this.keystorePassword.toCharArray();

 // read keystore data
 FileInputStream inputStream = null;
 try
 {
 inputStream = new FileInputStream(new File("C:/Program Files/Java/Tomcat 6.0/conf/keystore/ssl1"));
 keystore.load(inputStream, password);

 }
 finally
 {
 if (inputStream != null)
 {
 inputStream.close();
 }
 }

 // generate cert, private key
 this.cert = keystore.getCertificate("tomcat");
 PrivateKey privateKey = (PrivateKey) keystore.getKey("tomcat", password);

 if (this.cert == null)
 {
 throw new IOException("kein Zertifikat gefunden");
 }

 // init keypair
 this.keypair = new KeyPair(this.cert.getPublicKey(), privateKey);

 if(this.cipherCert == null)
 {
 this.cipherCert = Cipher.getInstance("RSA");
 }

 if (mode)
 {
 this.cipherCert.init(Cipher.ENCRYPT\_MODE, this.keypair.getPublic());
 }
 else
 {
 this.cipherCert.init(Cipher.DECRYPT\_MODE, this.keypair.getPrivate());
 }

 }

 /\*\*
 \* entschluessel einen Content
 \* 
 \* @param content
 \* der zu entschluesselnde String
 \* @return der entschluesselte String
 \* @throws DecodeException
 \* wenn ein Fehler bei der Entschluesselung auftritt
 \*/
 public String decode(String content) throws DecodeException
 {
 try
 {
 this.initCipher(false);
 byte[] result = new byte[1];

 String base64DecryptedContent = Base64.encodeBase64String(content.getBytes());

 Iterator it = new ContentIterator(base64DecryptedContent.getBytes(this.encoding), 128);
 while(it.hasNext())
 {
 byte[] plain = it.next();
 byte[] cryptedContent = this.cipherCert.doFinal(plain);
 byte[] tempResult = new byte[result.length+cryptedContent.length];

 //copy result to temp
 System.arraycopy(result, 0, tempResult, 0, result.length);
 //copy crypted to temp
 System.arraycopy(cryptedContent, 0, tempResult, result.length, cryptedContent.length);
 //init result with new values
 result = new byte[tempResult.length];
 System.arraycopy(tempResult, 0, result, 0, tempResult.length);

 this.initCipher(true);
 }

 //workaround for delete the first NULL element
 byte[] temp = new byte[result.length-1];
 System.arraycopy(result, 1, temp, 0, result.length-1);

 return new String(temp, this.encoding);
 }
 catch (InvalidKeyException error)
 {
 throw new DecodeException(error);
 }
 catch (NoSuchAlgorithmException error)
 {
 throw new DecodeException(error);
 }
 catch (NoSuchPaddingException error)
 {
 throw new DecodeException(error);
 }
 catch (UnsupportedEncodingException error)
 {
 throw new DecodeException(error);
 }
 catch (IllegalBlockSizeException error)
 {
 throw new DecodeException(error);
 }
 catch (BadPaddingException error)
 {
 throw new DecodeException(error);
 }
 catch (CertificateException error)
 {
 throw new DecodeException(error);
 }
 catch (IOException error)
 {
 throw new DecodeException(error);
 }
 catch (UnrecoverableKeyException error)
 {
 throw new DecodeException(error);
 }
 catch (KeyStoreException error)
 {
 throw new DecodeException(error);
 }
 }

 /\*\*
 \* verschluessel eines String Inhaltes
 \* 
 \* @param content
 \* der zu verschluesselnde String
 \* @return der verschluesselte String
 \* @throws EncodeException
 \* wenn ein Fehler bei der Verschluesselung auftritt
 \*/
 public String encode(String content) throws EncodeException
 {
 try
 {
 this.initCipher(true);
 byte[] result = new byte[1];

 Iterator it = new ContentIterator(content.getBytes(this.encoding), 52);
 while(it.hasNext())
 {
 byte[] plain = it.next();
 byte[] cryptedContent = this.cipherCert.doFinal(plain);
 byte[] tempResult = new byte[result.length+cryptedContent.length];

 //copy result to temp
 System.arraycopy(result, 0, tempResult, 0, result.length);
 //copy crypted to temp
 System.arraycopy(cryptedContent, 0, tempResult, result.length, cryptedContent.length);
 //init result with new values
 result = new byte[tempResult.length];
 System.arraycopy(tempResult, 0, result, 0, tempResult.length);

 this.initCipher(true);
 }

 //workaround for delete the first NULL element
 byte[] temp = new byte[result.length-1];
 System.arraycopy(result, 1, temp, 0, result.length-1);

 return Base64.encodeBase64String(temp);
 }
 catch (InvalidKeyException error)
 {
 throw new EncodeException(error);
 }
 catch (NoSuchAlgorithmException error)
 {
 throw new EncodeException(error);
 }
 catch (NoSuchPaddingException error)
 {
 throw new EncodeException(error);
 }
 catch (UnsupportedEncodingException error)
 {
 throw new EncodeException(error);
 }
 catch (IllegalBlockSizeException error)
 {
 throw new EncodeException(error);
 }
 catch (BadPaddingException error)
 {
 throw new EncodeException(error);
 }
 catch (CertificateException error)
 {
 throw new EncodeException(error);
 }
 catch (IOException error)
 {
 throw new EncodeException(error);
 }
 catch (UnrecoverableKeyException error)
 {
 throw new EncodeException(error);
 }
 catch (KeyStoreException error)
 {
 throw new EncodeException(error);
 }
 }

 /\*\*
 \* hole das derzeit verwendete Encoding fuer die Strings (siehe
 \* {@link Charset#defaultCharset()})
 \* 
 \* @return das derzeit verwendete Encoding fuer die Strings (siehe
 \* {@link Charset#defaultCharset()})
 \*/
 public String getEncoding()
 {
 return this.encoding;
 }

 /\*\*
 \* setze das derzeit verwendete Encoding fuer die Strings (siehe
 \* {@link Charset#defaultCharset()})
 \* 
 \* @param encoding
 \* das derzeit verwendete Encoding fuer die Strings (siehe
 \* {@link Charset#name()})
 \* @throws GeneralCryptoException
 \* wenn das Encoding nicht von der JVM unterstuetzt wird
 \*/
 @SuppressWarnings("nls")
 public void setEncoding(String encoding) throws GeneralCryptoException
 {
 if (!Charset.isSupported(encoding))
 {
 throw new GeneralCryptoException(new Throwable("Dieser Zeichensatz wird nicht von der JVM unterstützt"));
 }
 this.encoding = encoding;
 }

 /\*\*
 \* hole das Key-Paar (oeffentlicher und privater Schluessel)
 \* 
 \* @return das Key-Paar (oeffentlicher und privater Schluessel)
 \*/
 public KeyPair getKeyPair()
 {
 return this.keypair;
 }

 /\*\*
 \* setze das Key-Paar (oeffentlicher und privater Schluessel)
 \* 
 \* @param keyPair
 \* das Key-Paar (oeffentlicher und privater Schluessel)
 \* @throws GeneralCryptoException
 \* wenn das Schluesselpaar keine RSA Schluessel sind
 \*/
 @SuppressWarnings("nls")
 public void setKeyPair(KeyPair keyPair) throws GeneralCryptoException
 {
 if (!keyPair.getPublic().getAlgorithm().toLowerCase().contains("rsa")
 || !keyPair.getPrivate().getAlgorithm().toLowerCase().contains("rsa"))
 {
 throw new GeneralCryptoException(new Throwable(
 "Das Schlüsselpaar enthält keine RSA Schlüssel (private key has \"" + keyPair.getPrivate().getAlgorithm()
 + "\" algorithmn, public key has \"" + keyPair.getPublic().getAlgorithm() + "\" algorithmn"));
 }

 this.keypair = keyPair;
 }

 /\*\*
 \* Der Iterator um durch die zu ver/entschlüsselnde Menge zu iterieren
 \* 
 \* @author Daniel
 \* @version 1.0
 \* @since 07.07.2010
 \*/
 private class ContentIterator implements Iterator
 {
 /\*\*
 \* die aktuelle Position in der Menge
 \*/
 private int index;

 /\*\*
 \* die Laenge eines jeden "Schrittes"
 \*/
 private int stepLength;

 /\*\*
 \* die Menge durch welche iteriert wird
 \*/
 private byte[] content;

 /\*\*
 \* der InputStream zum Lesen des Contents
 \*/
 private ByteArrayInputStream inputStream;

 /\*\*
 \* der Konstruktor mit der Definition der Menge
 \* @param content die Menge durch die iteriert wird
 \* @param stepLength die Laenge eines jeden "Schrittes"
 \*/
 public ContentIterator(byte[] content, int stepLength)
 {
 this.content = content;
 this.index = -1;
 this.stepLength = stepLength;
 this.inputStream = new ByteArrayInputStream(this.content);
 }

 /\*\*
 \* gibt es ein weiteres Element?
 \* 
 \* @return gibt es ein weiteres Element?
 \*/
 public boolean hasNext()
 {
 if(this.index == -1) this.index = 0;
 if (this.index = this.content.length)
 {
 throw new NoSuchElementException("iteration has no more elements");
 }
 try
 { 
 byte[] buffer = new byte[this.stepLength];
 int read = this.inputStream.read(buffer, 0, this.stepLength);

 if(read != -1)
 {
 this.index += read;

 //if buffer complete?
 if(read == buffer.length)
 {
 return buffer;
 }

 //partial copy
 byte[] tmp = new byte[read];
 System.arraycopy(buffer, 0, tmp, 0, read);
 return tmp;
 }

 this.inputStream.close();
 throw new NoSuchElementException("iteration has no more elements");
 }
 catch(IOException error)
 {
 throw new NoSuchElementException("iteration has no more elements or an internal error is occured");
 }
 }

 /\*\*
 \* enferne ein Element aus der Menge

 \* **diese Methode ist nicht von diesem Iterator implementiert**
 \* 
 \* @exception IllegalStateException
 \* Wenn diese Methode aufgerufen wird bevor das erste mal next
 \* aufgerufen wird
 \* @exception UnsupportedOperationException
 \* diese Methode ist nicht von diesem Iterator implementiert
 \*/
 @SuppressWarnings("nls")
 public void remove()
 {
 if (this.index == -1)
 {
 throw new IllegalStateException("if the next() method has not yet been called");
 }

 throw new UnsupportedOperationException("This is not supported by this iterator");
 }
 }
}

hier ist der Stacktrace:

Exception in thread "main" de.buntklicken.util.crypto.contract.exception.DecodeException: javax.crypto.BadPaddingException: Data must start with zero
 at de.buntklicken.util.crypto.CertificateBasedRSA.decode(CertificateBasedRSA.java:240)
 at de.buntklicken.util.crypto.CertTest.main(CertTest.java:321)
Caused by: javax.crypto.BadPaddingException: Data must start with zero
 at sun.security.rsa.RSAPadding.unpadV15(Unknown Source)
 at sun.security.rsa.RSAPadding.unpad(Unknown Source)
 at com.sun.crypto.provider.RSACipher.a(DashoA13\*..)
 at com.sun.crypto.provider.RSACipher.engineDoFinal(DashoA13\*..)
 at javax.crypto.Cipher.doFinal(DashoA13\*..)
 at de.quick.meldecenter.util.crypto.CertificateBasedRSA.decode(CertificateBasedRSA.java:198)
 ... 1 more
2 Like