Encrypt/Decryption RSA通信Java-PHP

Encrypt/Decryption RSA communication Java-PHP

我正在尝试实现使用 RSA 加密的客户端-服务器通信。 我在 java 中生成私有和 public 密钥对,然后通过 GET 参数将一些输入传递给 PHP 脚本。如果只有密钥设置为 GET 参数,脚本将加密一个短语,该短语将从客户端(通过 java)解密,否则如果它设置了一条消息(m 参数见 PHP 脚本)它应该能够解密它。

我编码如下:

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()));
    }

和 PHP 脚本:

<?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']);
  }
?>

所以当我尝试通过服务器端加密内容然后在 Java 中解密时它工作正常。我假设使用生成的 public 密钥加密内容(将其发送到服务器)并使用客户端上存储的私钥解密内容。

问题: 我不能做相反的事情:使用 PHP 函数 RSADecryptMessage 由客户端加密并由服务器端解密。传递参数时它不起作用并且什么都不写。

编辑: 由于我需要一个通信双方都加密以避免中间人攻击,例如我使用客户端生成的PUBLICKEY,然后我将密钥发送到服务器。所以我使用相同的 public 密钥从服务器加密我的消息。通过这种方式,我可以从客户端使用私钥解密服务器的响应。最好的方法是服务器生成 public 和私钥对,但不幸的是,如果密钥对由 PHP 生成和共享,Java 无法解密消息,这就是为什么我允许客户端生成密钥对。这行得通。现在我也需要做相反的事情,使用之前发送到服务器的相同 public 密钥,我需要解密这次由客户端加密的消息(在图像中你可以看到字符串“crypted”)。不幸的是,这似乎不起作用,因为当我调用 PHP 脚本时,内容没有被解密。

已解决。

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.

@Progman 的评论

代码。

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']);
  }
?>

截图: