简体   繁体   中英

Encrypt/Decryption RSA communication Java-PHP

I'm trying to implement a client-server communication encrypted with RSA. I generate private and public key pairs in java then I pass some inputs to a PHP script through GET parameters. If there is only the key set as GET parameter the script encrypts a phrase that will be decrypted from the client (by java) otherwise if it's set a message (the m parameters see the PHP script) it should be able to decrypt it.

I coded the following:

java:

   public static void main(String[] args) throws Exception {
        // Generate public and private keys using RSA
        Map<String, Object> keys = getRSAKeys();

        PrivateKey privateKey = (PrivateKey) keys.get("private");
        PublicKey publicKey = (PublicKey) keys.get("public");

        StringBuilder keypublic = new StringBuilder();

        keypublic.append("-----BEGIN PUBLIC KEY-----\n");
        keypublic.append(Base64.getMimeEncoder().encodeToString(publicKey.getEncoded()) + "\n");
        keypublic.append("-----END PUBLIC KEY-----\n");

        StringBuilder keyprivate = new StringBuilder();

        keypublic.append("-----BEGIN PRIVATE KEY-----\n");
        keypublic.append(Base64.getMimeEncoder().encodeToString(privateKey.getEncoded()) + "\n");
        keypublic.append("-----END PRIVATE  KEY-----\n");


        String keyEncodedPublic = Base64.getEncoder().encodeToString(keypublic.toString().getBytes());
        String keyEncodedPrivate = Base64.getEncoder().encodeToString(keypublic.toString().getBytes());

        String signature = sign("MyEncryptedInternalString", privateKey);
        //Offuscare la MyEncryptedInternalString in qualche modo
        System.out.println("key: \n" + Base64.getEncoder().encodeToString(signature.getBytes()) + ":" + 
                                                     keyEncodedPublic);


        System.out.println("\n");

        System.out.println("Crypted: \n" +  Base64.getEncoder().encodeToString(encryptMessage("hello", privateKey).getBytes()));
        //System.out.println("Verified? " + verify("test",signature,publicKey));

        while(true) {

            Scanner out = new Scanner(System.in);
            System.out.print("Insert text: ");
            String enc = out.nextLine();


            String descryptedText = decryptMessage(enc, privateKey);
            System.out.println("Decrypted: " + descryptedText);
            System.out.println();

        }



    }


    public static boolean verify(String plainText, String signature, PublicKey publicKey) throws Exception {
        Signature publicSignature = Signature.getInstance("SHA256withRSA");
        publicSignature.initVerify(publicKey);
        publicSignature.update(plainText.getBytes(StandardCharsets.UTF_8));

        byte[] signatureBytes = Base64.getDecoder().decode(signature);

        return publicSignature.verify(signatureBytes);
    }

    public static String sign(String plainText, PrivateKey privateKey) throws Exception {
        Signature privateSignature = Signature.getInstance("SHA256withRSA");
        privateSignature.initSign(privateKey);
        privateSignature.update(plainText.getBytes(StandardCharsets.UTF_8));

        byte[] signature = privateSignature.sign();

        return Base64.getEncoder().encodeToString(signature);
    }


    // Get RSA keys. Uses key size of 2048.
    private static Map<String,Object> getRSAKeys() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(2048);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        PrivateKey privateKey = keyPair.getPrivate();
        PublicKey publicKey = keyPair.getPublic();

        Map<String, Object> keys = new HashMap<String,Object>();
        keys.put("private", privateKey);
        keys.put("public", publicKey);
        return keys;
    }

    // Decrypt using RSA public key
    private static String decryptMessage(String encryptedText, PrivateKey privateKey) throws Exception {
        Cipher cipher =  Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return new String(cipher.doFinal(Base64.getDecoder().decode(encryptedText)));
    }


    // Encrypt using RSA private key
    private static String encryptMessage(String plainText, PrivateKey privateKey) throws Exception {
        Cipher cipher =  Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return Base64.getEncoder().encodeToString(cipher.doFinal(plainText.getBytes()));
    }

And the PHP script:

<?php    

  function RSAEncryptMessage($string, $key){
      $data = "MyEncryptedInternalString";
      $parts = explode(":",$key);
      $signature = base64_decode($parts[0]);
      $pubkeyid = $parts[1];
      $ok = openssl_verify($data, base64_decode($signature), base64_decode($pubkeyid), "sha256WithRSAEncryption");
      if ($ok == 1){
          $pubkeyid = base64_decode($pubkeyid);
          openssl_public_encrypt($string, $crypted, $pubkeyid);
      }
      return base64_encode($crypted);
  }

  function RSADecryptMessage($string, $key){
      $data = "MyEncryptedInternalString";
      $parts = explode(":",$key);
      $signature = base64_decode($parts[0]);
      $pubkeyid = $parts[1];
      //$ok = openssl_verify($data, base64_decode($signature), base64_decode($pubkeyid), "sha256WithRSAEncryption");
      $pubkeyid = base64_decode($pubkeyid);
      openssl_private_decrypt(base64_decode($string), $decrypted, $pubkeyid);
      echo "Decrypted text: ". $decrypted;
      return $decrypted;
  }

  $mex = "hello";
  //echo RSAEncryptMessage($m,$_GET['key']);
  if(isset($_GET['m'])){
    echo RSADecryptMessage($_GET['m'],$_GET['key']);
  }else{
    echo RSAEncryptMessage($mex,$_GET['key']);
  }
?>

So when I try to encrypt the content by server-side and then decrypting it in Java it works correctly. I'm assuming to use the generated public key to encrypt the content (sending it to the server) and decrypting the content by using the stored private key on the client too. enter image description here

The question: I'm not able to do the contrary: encrypt by client-side and decrypting by server-side using the PHP function RSADecryptMessage . When passing the parameters it doesn't work and writes nothing.

EDIT: Since I need a communication both sides encrypted to avoid MITM attacks, for example, I use PUBLICKEY generated by the client, then I send the key to the server. So I encrypt my message from the server using the same public key. In this way, from the client, I'm able to decrypt the response by the server using the private key. The best way would be which server generates both public and private key pairs but unfortunately, Java is not able to decrypt a message if the key pair is generated and shared by PHP and that's why I allow the client to generate the key pair. This works. Now I need to do the contrary too, with the same public key sent previously to the server I need to decrypt a message encrypted by the client this time (in the image you can see the string "crypted"). Unfortunately, this seems not to work, because when I call the PHP script, the content is not decrypted.

Solved.

When you want to send an encrypted message from PHP to Java, PHP will have to encrypt it with the public key from Java. Then Java will decrypt it with his private key. When you want to send an encrypted message from Java to PHP, Java will have to encrypt it the public key from PHP. Then PHP will decrypt it with his private key. Both parties generate their own key-pair and send the public-key to the other party. Btw.: Signing does not encrypt the message, the message will still be visible/readable.

Comment of @Progman

Code.

java:

public static void main(String[] args) throws Exception {
    // Generate public and private keys using RSA
    Map<String, Object> keys = getRSAKeys();

    PrivateKey privateKey = (PrivateKey) keys.get("private");
    PublicKey publicKey = (PublicKey) keys.get("public");

    StringBuilder keypublic = new StringBuilder();

    keypublic.append("-----BEGIN PUBLIC KEY-----\n");
    keypublic.append(Base64.getMimeEncoder().encodeToString(publicKey.getEncoded()) + "\n");
    keypublic.append("-----END PUBLIC KEY-----\n");


    String keyEncodedPublic = Base64.getEncoder().encodeToString(keypublic.toString().getBytes());

    String signature = sign("MyEncryptedInternalString", privateKey);
    System.out.println("key: \n" + Base64.getEncoder().encodeToString(signature.getBytes()) + ":" + keyEncodedPublic);


    System.out.println("\n");
    
    while(true) {


        Scanner out = new Scanner(System.in);
        System.out.print("Insert text: ");
        String enc = out.nextLine();


        String descryptedText = decryptMessage(enc, privateKey);
        System.out.println("Decrypted: " + descryptedText);
        System.out.println();

        Scanner out2 = new Scanner(System.in);
        System.out.print("Insert text2: ");
        String enc2 = out2.nextLine();

        byte[] decodedBytesKey = Base64.getDecoder().decode(enc2);

        //String content = encryptMessage("message from the client", new String(decodedBytes));

        String publicKeyPEM = new String(decodedBytesKey)
                .replace("-----BEGIN PUBLIC KEY-----", "")
                .replaceAll(System.lineSeparator(), "")
                .replace("-----END PUBLIC KEY-----", "");

        byte[] encoded = Base64.getDecoder().decode(publicKeyPEM);

        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded);
        PublicKey p = keyFactory.generatePublic(keySpec);

        System.out.println(encryptMessage("Message from client",p));

    }


}


public static boolean verify(String plainText, String signature, PublicKey publicKey) throws Exception {
    Signature publicSignature = Signature.getInstance("SHA256withRSA");
    publicSignature.initVerify(publicKey);
    publicSignature.update(plainText.getBytes(StandardCharsets.UTF_8));

    byte[] signatureBytes = Base64.getDecoder().decode(signature);

    return publicSignature.verify(signatureBytes);
}

public static String sign(String plainText, PrivateKey privateKey) throws Exception {
    Signature privateSignature = Signature.getInstance("SHA256withRSA");
    privateSignature.initSign(privateKey);
    privateSignature.update(plainText.getBytes(StandardCharsets.UTF_8));

    byte[] signature = privateSignature.sign();

    return Base64.getEncoder().encodeToString(signature);
}


// Get RSA keys. Uses key size of 2048.
private static Map<String,Object> getRSAKeys() throws Exception {
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
    keyPairGenerator.initialize(2048);
    KeyPair keyPair = keyPairGenerator.generateKeyPair();
    PrivateKey privateKey = keyPair.getPrivate();
    PublicKey publicKey = keyPair.getPublic();

    Map<String, Object> keys = new HashMap<String,Object>();
    keys.put("private", privateKey);
    keys.put("public", publicKey);
    return keys;
}


private static String decryptMessage(String encryptedText, PrivateKey privateKey) throws Exception {
    Cipher cipher =  Cipher.getInstance("RSA");
    cipher.init(Cipher.DECRYPT_MODE, privateKey);
    return new String(cipher.doFinal(Base64.getDecoder().decode(encryptedText)));
}



private static String encryptMessage(String plainText, PublicKey publicKey) throws Exception {
    Cipher cipher =  Cipher.getInstance("RSA");
    cipher.init(Cipher.ENCRYPT_MODE, publicKey);
    return URLEncoder.encode(Base64.getEncoder().encodeToString(cipher.doFinal(plainText.getBytes())));
}

PHP:

<?php
  include ("utils/RSA.php");

  $config = array(
      "digest_alg" => "sha512",
      "private_key_bits" => 2048,
      "private_key_type" => OPENSSL_KEYTYPE_RSA,
  );

  $res = openssl_pkey_new($config);
  openssl_pkey_export($res, $privKey);
  $pubKey = openssl_pkey_get_details($res);
  $pubKey = $pubKey["key"];

  echo "PubKey: " . base64_encode($pubKey) ."<br>";
  echo "PrivateKey: " . base64_encode($privKey) .  "<br>";


  //echo "Encrypted: " . base64_encode($encrypted);


  function RSAEncryptMessage($string, $key){
      $data = "MyEncryptedInternalString";
      $parts = explode(":",$key);
      $signature = base64_decode($parts[0]);
      $pubkeyid = $parts[1];
      $ok = openssl_verify($data, base64_decode($signature), base64_decode($pubkeyid), "sha256WithRSAEncryption");
      if ($ok == 1){
          $pubkeyid = base64_decode($pubkeyid);
          openssl_public_encrypt($string, $crypted, $pubkeyid);
      }
      return base64_encode($crypted);
  }

  function RSADecryptMessage($encrypted, $key){
        //todo
        openssl_private_decrypt(base64_decode($encrypted), $decrypted, base64_decode($key));
        echo "Decrypted: " . $decrypted;
  }

  if((isset($_GET['providemessage']))){
    echo RSADecryptMessage($_GET['m'],$_GET['key']);
  }else if (isset($_GET['getmessage'])){
    $mex = "Message from server";
    echo RSAEncryptMessage($mex,$_GET['key']);
  }
?>

Screenshots:

在此处输入图片说明

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM