import java.math.*;
import java.util.*;
// I/O Funktionen
import java.io.*;

public class MyRSA{
   private static Vector schluesselbund;
    
    //     modulus = Moduluszahl, öffentlicher und publicr Schlüssel
    
    public static BigInteger pubKey, privKey ;
    
    // Konstruktor
    public MyRSA(){
      schluesselbund = new Vector(); 
    }
    
    // p,q,phi,modulus
    public  static BigInteger phi, p, q, modulus;
    //     bitLength= Bitlänge der erzeugten Prime p,q
    public static int bitLength = 48;
    public static BigInteger zwischenwert, chiffre, entschluesselt;


    public static StringBuffer returnAusgabe = new StringBuffer("");
    public static StringBuffer returnAusgabe2 = new StringBuffer("");
    public static char einzelzeichen;


    //  _   _ _ _  __     _                        _
    // | | | (_) |/ _|___| |__   ___ _ __ ___  ___| |__  _ __  _   _ _ __   __ _
    // | |_| | | | |_/ __| '_ \ / _ \ '__/ _ \/ __| '_ \| '_ \| | | | '_ \ / _` |
    // |  _  | | |  _\__ \ |_) |  __/ | |  __/ (__| | | | | | | |_| | | | | (_| |
    // |_| |_|_|_|_| |___/_.__/ \___|_|  \___|\___|_| |_|_| |_|\__,_|_| |_|\__, |
    //                                                                     |___/

    public static BigInteger erzeugeHilfszahlen(){

        // große Zufallsprime zur Schluesselgenerierung, müssen geheim bleiben
        // Erzeugung in gewünschter Bitlänge
        // p
        p = BigInteger.probablePrime(bitLength, new Random());
        // q
        q = BigInteger.probablePrime(bitLength, new Random());

        // wenn p=q q nochmal berechnen, da sonst Fehler bei dechiffrieren
        if (p == q )
            q = BigInteger.probablePrime(bitLength, new Random());

        // zur Ver-/Entschluesselung noetig
        // modulus = p*q
        modulus = p.multiply(q);
        // phi = (p-1)*(q-1)
        phi = (p.subtract(new BigInteger("1"))).multiply(q.subtract(new BigInteger("1")));
        return phi;
    }

    //              __  __            _   _ _      _
    //   ___   ___ / _|/ _| ___ _ __ | |_| (_) ___| |__   ___ _ __      ___
    //  / _ \ / _ \ |_| |_ / _ \ '_ \| __| | |/ __| '_ \ / _ \ '__|    / _ \
    // | (_) |  __/  _|  _|  __/ | | | |_| | | (__| | | |  __/ |      |  __/
    //  \___/ \___|_| |_|  \___|_| |_|\__|_|_|\___|_| |_|\___|_|       \___|

    public static BigInteger oeffentlicherSchluessel(){
        // erzeugt Zufallsprim als öffentlichen Schlüssel
        pubKey = BigInteger.probablePrime(bitLength, new Random());
        return pubKey;
    }

    //             _            _                  _
    //  _ __  _ __(_)_   ____ _| |_ ___ _ __    __| |
    // | '_ \| '__| \ \ / / _` | __/ _ \ '__|  / _` |
    // | |_) | |  | |\ V / (_| | ||  __/ |    | (_| |
    // | .__/|_|  |_| \_/ \__,_|\__\___|_|     \__,_|
    // |_|

    public static BigInteger privatSchluessel(){
        // public Schlüssel ist das Moduloinverse, also die
        // Lösung von: (e * d) mod phi=1
        erzeugeHilfszahlen();
        pubKey = BigInteger.probablePrime(bitLength, new Random());
        privKey = pubKey.modInverse(phi);
        return privKey;
    }


        //Schluesselbund
        public void anSchluesselbund(BigInteger pubKey, BigInteger privKey, BigInteger modulus){
        schluesselbund.add(privKey);
        schluesselbund.add(pubKey);
        schluesselbund.add(modulus);
        return;
        }


    //                     _    _ _  _            _
    // __ _____ _ _ ___ __| |_ | (_)(_)______ ___| |_ _
    // \ V / -_) '_(_-</ _| ' \| | || (_-<_-</ -_) | ' \
    //  \_/\___|_| /__/\__|_||_|_|\_,_/__/__/\___|_|_||_|

    // (Klartext ^ Oeffentlicher Schlüssel) mod modulzahl
    // zerlegt String in ASCII-Werte
    static StringBuffer verschluessele(String eingabe){
        String blank=" ";
        char ausgabe[]= new char [eingabe.length()];
        BigInteger zeichenkette[] = new BigInteger [eingabe.length()];

        for (int c=0; c<eingabe.length(); c++){
            // casting der Zeichen in ASCII
            int zeichen = eingabe.charAt(c);
            // casted zeichen (int) in bigZeichen (BigInteger)
            BigInteger bigZeichen = BigInteger.valueOf(zeichen);
            // potenzierung & mod := (zeichen ^ schlüssel) % modulzahl
            //    zwischenschritt = bigZeichen.modPow(pubKey, modulus);
            zwischenwert = bigZeichen.modPow(pubKey,  modulus );
            // String zusammensetzen
            returnAusgabe.append(zwischenwert.toString());
            returnAusgabe.append(blank);

        } //for
        return returnAusgabe;
    }// verschluessele()


    //             _            _     _ _   _              _
    //   ___ _ __ | |_ ___  ___| |__ | (_) (_)___ ___  ___| |_ __
    //  / _ \ '_ \| __/ __|/ __| '_ \| | | | / __/ __|/ _ \ | '_ \
    // |  __/ | | | |_\__ \ (__| | | | | |_| \__ \__ \  __/ | | | |
    //  \___|_| |_|\__|___/\___|_| |_|_|\__,_|___/___/\___|_|_| |_|
    /*
        public static StringBuffer entschluessele(String verschluesselt){
            char ausgabe[]= new char [verschluesselt.length()];
            char ausgabeString[] = new  char [verschluesselt.length()];
            returnAusgabe=null;
            // String zerlegen
            StringTokenizer stok = new StringTokenizer(verschluesselt);
            int c=0;
            while (stok.hasMoreTokens()) {
                 // nach BigInteger casten
                BigInteger bigZeichen = new BigInteger (stok.nextToken()); 
    //             BigInteger(stok.nextToken(),2);
                // entschlüsselungsfunktion
                entschluesselt=bigZeichen.modPow(privKey, modulus);
                // casting von BigInteger in int und char
                int ascii = entschluesselt.intValue();
                char asciichar = (char) ascii;
                ausgabeString[c] = asciichar;
                // zusammensetzen in Array
                c++;
            }//while
            returnAusgabe.append(ausgabeString.toString());
            return returnAusgabe;
        }// entschluessele

    */
    public static StringBuffer entschluessele(String verschluesselt){
        char ausgabeArray[] = new char[verschluesselt.length()];
        int c=0;
        // zerlegt String
        StringTokenizer stok = new StringTokenizer(verschluesselt);
        // für jedes token
        while (stok.hasMoreTokens()){
            // String -> int

            int zeichen = Integer.parseInt(stok.nextToken());
            BigInteger bigZeichen = BigInteger.valueOf(zeichen);

            // entschlüsselungsfunktion
            entschluesselt=bigZeichen.modPow(privKey, modulus);

            // casting von BigInteger in int und char
            int ascii = entschluesselt.intValue();
            char asciichar = (char) ascii;
            //             ausgabeArray[c]= asciichar;
            //             returnAusgabe2.append(ausgabeArray.toString());
            returnAusgabe2.append(asciichar);
        }
        return returnAusgabe2;
    }// entschluessele







    //  ____        _       _       _      _
    // |  _ \  __ _| |_ ___(_)  ___(_) ___| |__   ___ _ __ _ __
    // | | | |/ _` | __/ _ \ | / __| |/ __| '_ \ / _ \ '__| '_ \
    // | |_| | (_| | ||  __/ | \__ \ | (__| | | |  __/ |  | | | |
    // |____/ \__,_|\__\___|_| |___/_|\___|_| |_|\___|_|  |_| |_|

    public static void speichereDatei(BigInteger zwischenschritt, BigInteger zwischenschritt2, BigInteger zwischenschritt3) throws IOException{

        // Dateiname
        File ausgabeDatei;
        // OutputStream für Datei-I/O
        FileOutputStream dateizugriff;
        String name="rsa-schluessel.txt";
        // initialisiert Datei als File Objekt
        ausgabeDatei = new File(name);
        // wenn datei schon da: löschen
        if(ausgabeDatei.exists())try{ausgabeDatei.delete();}
        // Fängt Rechteverstöße ab
            catch(SecurityException e){};
        // Öffnet Stream zum schreiben
        try{dateizugriff = new FileOutputStream(name);}
        // Datei nicht gefunden
        catch(FileNotFoundException e){return;};
        // jetzt kann geschrieben werden:
        // macht String aus Schlüsseln + Mod
        String  zwischenstring=zwischenschritt.toString()+" "+zwischenschritt2.toString()+" "+zwischenschritt3.toString();
        // zerlege String in byte
        byte bytekette[] = new byte[zwischenstring.length()];
        for(int c = 0; c < zwischenstring.length(); c++)
            bytekette[c]=(byte)zwischenstring.charAt(c);
        //schreibe bytes
        dateizugriff.write(bytekette);
        // Stream schließen, schreiben beendet
        dateizugriff.close();
    }// speichereDatei()
} //class
