1
$\begingroup$

By using RSA encryption, it is possible to "encrypt" sign with sender's private key and then with receiver's public key, and decrypt with receiver's private key and then with sender's public key?

Like this:

  • Sender keys
    • private: $(n_s,d_s)$
    • public: $(n,s,e_s)$
  • Receiver keys
    • private: $(n_r,d_r)$
    • public: $(n_r,e_r)$
  • (Signature then) Encryption of message $m$
    • $c_1\gets m^{d_s}\bmod n_s$
    • $c_2\gets {c_1}^{e_r}\bmod n_r$
  • and then decryption with
    • $\widetilde{c_1}\gets {c_2}^{d_r}\bmod n_r$
    • $\widetilde{c_2}\gets {\widetilde{c_1}}^{e_s}\bmod n_s$

Mathematically speaking it should work, right?

EDIT: code with the methods to generate keys, encrypt and decrypt.

    import java.io.*;
    import java.math.BigInteger;
    import java.util.*;         


public class ChatRSA {

        //inizializzazione di un oggetto BigInteger con valore intero randomico
    public static BigInteger Rand(){

            Random ra=new Random();

            int bits=1024;
            BigInteger bigran;
            do{
                //Numero che è probabilmente primo(la possibilità che non lo sia è di 1 ogni 100 miliardi di anni approssimativamente)
               bigran=BigInteger.probablePrime(bits, ra);
               //Eseguo un doppio controllo per aumentare la possibilità che sia primo
            }while(!bigran.isProbablePrime(100));// Più è grande il parametro, più aumenta la possibilità che sia primo


            return bigran;
        }
        //Calcolo primo numero delle 2 chiavi
        public static BigInteger N(BigInteger p, BigInteger q){

            BigInteger n=p.multiply(q);
            return n;
        }
        //Funzione Toziente(Conta in base a un numero n i numeri coprimi rispetto a n)
        public static BigInteger V(BigInteger p, BigInteger q){
            BigInteger v=p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE));
            return v;
        }
        //Secondo numero della chiave pubblica, che deve essere coprimo di V
        public static BigInteger Npub(BigInteger v){

            BigInteger npub;

            do{
               npub=ChatRSA.Rand();
               // gcd, il massimo comune divisore deve essere 1
            }while(!npub.gcd(v).equals(BigInteger.ONE)&&npub.compareTo(v)==-1);

            return npub;
        }
        //Secondo numero della chiave privata
        public static BigInteger Npri(BigInteger v, BigInteger npub){

            BigInteger npri;

            do{
                //(Npri * Npub)mod(v)Deve risultare = 1
                //quindi questo metodo opera npri^-1 mod(v)
                npri=npub.modInverse(v);


                //System.out.println((npri.multiply(npub)).mod(v).toString());
            }while(!(npri.multiply(npub)).mod(v).equals(BigInteger.ONE));

            return npri;
        }
        //Metodo per convertire la Stringa del messaggio in cifre
        public static BigInteger StringToCipher(String msg){

            //Array di byte, tipo di dato intero più piccolo. Viene usato e gestito in array
            //Recupera in array di bytes la stringa e la assegna
            byte [] bar=msg.getBytes();
            //Il valore dell'array di bytes convertito nel corrispondete grande intero
            BigInteger cmsg=new BigInteger(bar);
            return cmsg;
        }
        //Metodo per convertire il messaggio cifrato in stringa
        public static String CipherToString(BigInteger msg){

            byte [] bar=msg.toByteArray();
            String dcmsg=new String(bar);
            return dcmsg;
        }
        //Metodi per criptare e decriptare il messaggio
        public static BigInteger Encryption(BigInteger cmsg, BigInteger npub, BigInteger n){

            //Il metodo modPow utilizza la formula per criptare il messaggio nell RSA: cmsg^npub mod(n)
            BigInteger c=cmsg.modPow(npub, n);
            return c;
        }
        public static BigInteger Decryption(BigInteger c, BigInteger npri, BigInteger n){

            BigInteger m=c.modPow(npri, n);
            return m;

        }

}
$\endgroup$
5
  • 2
    $\begingroup$ Welcome to Cryptography.SE. Yes, but why? What is the origin of this question? Why not to use hybrid encryption? $\endgroup$
    – kelalaka
    Commented May 19, 2021 at 19:28
  • 2
    $\begingroup$ What problem are you trying to solve? $\endgroup$
    – poncho
    Commented May 19, 2021 at 19:55
  • $\begingroup$ The problem is that with some specific string in a specific session between my client/server chat it doesn't work. I don't know why it can't let me insert the java code in the question. $\endgroup$ Commented May 20, 2021 at 7:13
  • 1
    $\begingroup$ 'Encrypting with the private key' is not actually encryption because it does not provide confidentiality; x^d mod n can be used for signing, which for historical reasons is often misdescribed that way, and we have many Qs on this. Your unpadded RSA, aka 'textbook' or 'naive', only works if nr > ns, which is hard to ensure in general, and impossible for bidirectional traffic. More importantly, unpadded RSA is not secure, and properly (differently) padded encryption and signing do not compose at all. Whether to sign before encrypting, or after, or both, is complicated; see other Qs. $\endgroup$ Commented May 20, 2021 at 9:26
  • $\begingroup$ I uploaded the class with the methods used to generte keys, encrypt and decrypt. Can someone tell me why if i encrypting and decrypt with both private and public key of both sender and receiver ONLY some string can't be encrypted/decrypted(it returns a string of unknown characters)? $\endgroup$ Commented May 20, 2021 at 11:01

1 Answer 1

2
$\begingroup$

As pointed in comment

  1. "encrypt" with sender's private key is improper terminology; the proper term is sign. I took the liberty to fix that common error in the question. If we used anything else than trapdoor-permutation-based public-key cryptography (RSA, Rabin for the most), "encrypt" with sender's private key would be plain unintelligible.
  2. It's assumed $n_r>n_s$ so that $c_1<n_r$, which is necessary to get $\widetilde{c_1}=c_1$. Thus the technique won't "work" with 100% reliability in both directions unless we take some precaution.

Also, the issue of padding is ignored. And more broadly the security goals are not stated.

Issue 2 can be solved in a number of ways:

  • The best is to use separate keys for signature and encryption, with a maximum $n$ for the first applied (signature in the question) lower than the minimum for the second. That abides to the time-proven security design principle: one use, one key. It also avoids the embarrassment of ending up with $c_2=m$ when the sender is the receiver, something an adversary might induce.
  • Another option is to force all $n$ to share the same bit size and many high order bits (say 512, which become a fixed public arbitrary constant). That insures $c_1<n_r$ with overwhelming probability. It does not solve the send-to-self issue.
  • Yet another is to require all $n$ to have the same bit size, restrict to (say) even $m$, and use $c_2\gets\min\bigl(c_1,n_s-c_1)^{e_r}\bmod n_r$. On decryption, we'll end up with either $m=\widetilde{c_2}$ or $m=n_s-\widetilde{c_2}$, and the parity of $m$ allows to determine which (wince $n_s$ is odd). That does not solve the send-to-self issue.
  • A small variant of the above forces $m<n_s/2$ (typically by requiring $m$ to fit into two bits less than any $n$), which also allows recovery of $m$.

On the aforementioned padding issue: both RSA encryption and signing require redundancy for security; plus encryption requires random padding (or non-guessable plaintext) in what's processed with textbook RSA encryption. That can be solved by:

  • pad $m$ with randomness and redundancy
  • sign with textbook RSA using private key $(n_s,e_s)$ [in it's instance for signature, if distinct from encryption]
  • encrypt with textbook RSA using public key $(n_r,e_r)$ [in it's instance for encryption].

Proper checking of the redundancy on decryption insures integrity and protects against decryption padding oracle attacks.

Encrypt-then-Sign works as well, and is used in the RSA-based authentication protocol¹ between (pan-)European Digital Tachograph and it's Smart Cards. It uses the $\min$ and even padded message trick.


¹ Designed circa the end of the 20th century, I wish I knew exactly by who. A modern variant would use RSAES-OAEP padding and the $m<n/2$ trick, or best separate encryption and signature keys. It would also use public moduli much larger than 1024-bit, but I was instrumental in not increasing that value (see this) late in the project and causing yet another launch delay. I don't regret that bet: roads would have been rather less than more safe from drivers lacking sleep or over-speeding if Europe had waited 2048-bit RSA gizmos to be available to enforce something better than the old recording disc. As predicted, fraud on the new system ended up focusing elsewhere: pulling the power fuse during part of a long drive (pretending the battery was disconnected to avoid vehicle theft or fire risk to explain the record of that incident), using a magnet on the motion sensor (as an alternate way to stop recording while driving), unscrewing the real motion sensor and stimulating it artificially (to lower the recorded speed), somewhat getting the calibration factors off (to the same effect), drivers getting hold of two cards (doubling their driving allowance), plain not introducing their card, or purposely using old trucks (without the new Digital Tachograph mandated).

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.