Discussion on PHP SHA1withRSA Encryption to Generate Signature and Verify Signature

  • 2021-12-04 09:42:24
  • OfStack

Recently, the company docked the payment business of the third-party payment platform of XX. Since the other company only has demo of JAVA, it can only integrate the signature encryption of PHP according to the document itself. After looking for several methods on the Internet, it stepped on various pits. Fortunately, it was finally settled. Without saying much, the code was shared.

Business requirements: The contents of each signature assembly are sorted and connected in ascending dictionary order of field names

Assemble the content to be signed first:


/**
   *  Splice the content that needs to be signed 
   * Author: Tao.
   *
   * @param array $data  Content of the field to be signed 
   * 
   * @return string
   */   
  public static function getSign($data)
  {
    foreach ($data as $k => $v) {
      $Parameters[$k] = $v;
    }
    // Sort parameters in lexicographic order 
    ksort($Parameters);
    $sign = '';
    foreach ($Parameters as $k => $v) {
      $sign .= $k . "=" . $v . "&";
    }
    $sign = '&' . rtrim($sign, '&');
    return $sign;
  }

The signature string is as follows:
& amount=amount value & ccy=ccy value & merchantId=merchantId value & notifyUrl=notifyUrl value & orderId=orderId value & payeeAcctNo = payeeAcctNo value (plaintext).

It should be noted that whether to splice before the signed content is selected according to business needs & Fu.

Then generate the secret key signature:


/**
   *  Secret key encryption 
   * Author: Tao.
   *
   * @param string $data  Previous generated content to be encrypted 
   * @param $key  Private key certificate location (.pfx Documents )
   * @param string $pwd  Certificate password 
   *
   * @return string
   */
  public static function SHA1withRSA($data, $key,$pwd)
  {
    openssl_pkcs12_read(file_get_contents($key), $certs, $pwd); 
    if (!$certs) return;
    $signature = '';
    openssl_sign($data, $signature, $certs['pkey']);
    return bin2hex($signature); 
  }

If the third party company requires to convert to hexadecimal, bin2hex () or base64_encode () can be selected according to the demand.

What should be noted here is whether the signed content requires case sensitivity according to business needs.

The signed content should be lowercase and can be converted to uppercase using strtoupper ().

The above is the private key encryption method sorted out for everyone.

However, in this service, it is also required to encrypt the bank card number with RSA public key
Here is how to get the public key:
Here is the public key for obtaining the opposite platform certificate (. cer file)


/**
   *  Get the public key 
   * Author: Tao.
   *
   * @param $path // Public key certificate location  (.cer Documents )
   *
   * @return mixed
   * @throws \Exception
   */
  public static function loadCert($path)
  {
    $file = file_get_contents($path);
    if (!$file) {
      throw new \Exception('loadx509Cert::file_get_contents ERROR');
    }

    $cert = chunk_split(base64_encode($file), 64, "\n");
    $cert = "-----BEGIN CERTIFICATE-----\n" . $cert . "-----END CERTIFICATE-----\n";

    $res = openssl_pkey_get_public($cert);
    $detail = openssl_pkey_get_details($res);
    openssl_free_key($res);

    if (!$detail) {
      throw new \Exception('loadX509Cert::openssl_pkey_get_details ERROR');
    }
    return $detail['key'];
  }

  /**
   *  Public key encryption 
   * Author: Tao.
   * 
   * @param $pubPath // Public key certificate location  (.cer Documents )
   * @param string $bankCode // Bank card number 
   * 
   * @return string
   */
  public static function rsa_encode($bankCode,$pubPath)
  {
    $pubkey = self::loadCert($pubPath);
    $encrypt_data = '';
    openssl_public_encrypt($bankCode, $encrypt_data, $pubkey);
    $encrypt_data = base64_encode($encrypt_data);
    return $encrypt_data;
  }

You have to ask me why the private key is bin2hex () and the public key is changed to base64_encode (). I do not know why, asked said is 16, but request signature 1 straight failed, for 64 successful. The other party said that the document was too old and forgot. . Choose according to your needs
Verification of the final callback result

First, take out the contents of the assembly signature field in the callback data and sort it according to the getSign () method above.
Then verify:


/**
   *  Verify that the signature returned is correct 
   *
   * @param string $data  Original signature to verify 
   * @param string $signature  Signature content 
   *@param $pubPath  Public key certificate location  (.cer Documents )
   *
   * @return bool
   */
  public static function verifyRespondSign($data, $signature,$pubPath)
  {
    $keys = self::loadCert($pubPath);
    $signature = hex2bin($signature);
    $ok = openssl_verify($data, $signature, $keys);
    if ($ok == 1) {
      return true;
    }
    return false;
  }

Related articles: