1. The OpenSSL tool generates the key

  • 1. The OpenSSL tools download address: https://www.openssl.org/source/
  • 2. Generate a 1024-bit RSA key PEM file in the specified directory
Generate the private.pem private key
mkdir temp_key && cd temp_key && openssl genrsa -out private.pem 1024
Copy the code
  • 3. Generate a CER certificate using the key
## Generate the public. Cer public key
openssl req -new -x509 -key private.pem -out public.cer -days 3650 -subj /CN=topbooking.natapp1.cc
Copy the code
  • 4. Generate a PFX file using the PEM key and cer certificate
For example, if the password is set to 111111, the file openssl.pfx will be generated
openssl pkcs12 -export -out openssl.pfx -inkey private.pem -in public.cer
Copy the code
  • 5. Use the PEM key to generate a PEM public key to the data exchangeSuch as the long lungConfigure your own public key
Generate the public key app_public_key.pem
openssl rsa -in private.pem -pubout -out app_public_key.pem 
Copy the code
  • 6. Public key to be generatedapp_public_key.pemTo the data interaction party

2. Data signature


      

namespace App\Services\ChimeLong;

use Illuminate\Support\Str;

/** * RSA encryption * Class RSACryptoService *@package App\Services\MPay
 */
class RSACryptoService
{
    private $_mer_no;
    private $_aes_key;
    private $_ver_no;
    private $_privateKey;
    public  $_publicKey;

    RSACryptoService constructor. */
    public function __construct()
    {
        if (app()->environment('production')) {// Formal environment
            $this->_mer_no   = config('api_token.chimeLong.prod.mer_no');
            $this->_aes_key  = substr(md5(config('api_token.chimeLong.prod.key')), 8.16);
            $this->_ver_no   = config('api_token.chimeLong.prod.ver_no');
            $privatePemPath  = public_path(config('api_token.chimeLong.prod.privatePemPath'));
            $publicPemPath   = public_path(config('api_token.chimeLong.prod.publicPemPath'));
            $publicPemPathCL = public_path(config('api_token.chimeLong.prod.publicPemPathCL'));
        } else {// Test the environment
            $this->_mer_no   = config('api_token.chimeLong.test.mer_no');
            $this->_aes_key  = substr(md5(config('api_token.chimeLong.test.key')), 8.16);
            $this->_ver_no   = config('api_token.chimeLong.test.ver_no');
            $privatePemPath  = public_path(config('api_token.chimeLong.test.privatePemPath'));
            $publicPemPath   = public_path(config('api_token.chimeLong.test.publicPemPath'));
            $publicPemPathCL = public_path(config('api_token.chimeLong.test.publicPemPathCL'));
        }
        $this->get_private_key($privatePemPath);// The requested data is signed with our private key, and Chimelong receives the requested data by checking with our public key
        $this->get_public_key($publicPemPathCL);// The received response data is checked with chimelong's public key
    }

    /** * get the private key *@param $privatePemPath
     */
    private function get_private_key($privatePemPath)
    {
        $this->_privateKey = openssl_pkey_get_private(file_get_contents($privatePemPath));
    }

    /** * Parses the public key from the certificate for use *@param $publicPemPath
     */
    private function get_public_key($publicPemPath)
    {
        $this->_publicKey = openssl_pkey_get_public(file_get_contents($publicPemPath));
    }

    /** * Step 1 * Generate signature using the corresponding language SHA256WithRSA signature function to use the sender private key to sign the signature string, and Base64 encoding. *@param $data
     * @return string
     * @throws \Exception
     */
    public function rsa_sign($data)
    {
        $signData  = $this->_mer_no . $data['method'].$data['uuid'].$data['timestamp'].$this->_ver_no . json_encode($data['body']);
        $signature = ' ';
        $res       = openssl_sign($signData.$signature.$this->_privateKey, OPENSSL_ALGO_SHA256);
        openssl_free_key($this->_privateKey);
        if ($res) {
            return base64_encode($signature);
        } else {
            throw new \Exception("Error requesting data signature".10004); }}AES algorithm The encryption mode is ECB. The key length is 128 bits and the block length is 128 bits. *@param $data
     * @return string
     * @throws \Exception
     */
    public function aes_sign($data)
    {
        if(! key_exists('timestamp'.$data)) {
            $data['timestamp'] = now()->toDateTimeString();
        }
        if(! key_exists('uuid'.$data)) {
            $data['uuid'] = (string)Str::uuid();
        }
        if(! key_exists('body'.$data)) {
            $data['body'] = [];
        }
        $sign        = $this->rsa_sign($data);
        $encryptData = [
            "method"= >$data['method']."sign"= >$sign."nonce_str"= >$data['uuid']."timestamp"= >$data['timestamp']."ver_no"= >$this->_ver_no,
            "body"= >$data['body']];return $this->aes_encrypt(json_encode($encryptData));
    }

    / * * *@param string $resource
     * @param string $signature
     * @return bool
     * @todo: to be perfected, temporarily useless * string verification method a */
    public function verifyResData(string $resource.string $signature) :bool
    {
        $signature = base64_decode($signature);
        $res       = openssl_verify($resource.$signature.$this->_publicKey, OPENSSL_ALGO_SHA256);
        //openssl_free_key($this->_publicKey);
        return $res= = =1;
    }

    / * * *@param $response
     * @return int
     * @todo: To be perfected, temporarily useless * Verify signature method two */
    public function check_sign($response)
    {
        $json      = json_decode($response.false);
        $signature = base64_decode($json->sign);
        unset($json->sign);
        $data = $json->code . $json->msg . $json->nonce_str . $json->body;
        $res = openssl_verify($data.$signature.$this->_publicKey, OPENSSL_ALGO_SHA256);
        //dd($data, $signature, $res);
        openssl_free_key($this->_publicKey);
        return $res= = =1;
    }

    /** * decrypt *@param $str
     * @return false|string
     */
    public function aes_decrypt($str)
    {
        return openssl_decrypt(base64_decode($str), "AES-128-ECB".$this->_aes_key, OPENSSL_RAW_DATA);
    }

    /** * Encrypted string *@param $str
     * @return string
     */
    public function aes_encrypt($str)
    {
        return base64_encode(openssl_encrypt($str."AES-128-ECB".$this->_aes_key, OPENSSL_RAW_DATA));
    }

    /** * Returns the desired data after decryption, de-serialization, and signature verification@param string $resource
     * @return mixed
     * @throws \Exception
     */
    public function handleRes(string $resource)
    {
        // Step1: AES decryption
        $decryptRes = $this->aes_decrypt($resource);
        // step2: json disserialization
        $resData = json_decode($decryptRes.true);
        # response signature string code+ MSG +nonce_str+body
        $body      = $resData['body'];
        $signData  = $resData['code'].$resData['msg'].$resData['nonce_str'].$body;
        $signature = $resData['sign'];
        if ($this->verifyResData($signData.$signature) | |$this->check_sign($decryptRes)) {// Check successful
            return json_decode($body.true);
        } else {
            throw new \Exception("Received data signature error".10004); }}}Copy the code