sha256,aes,rsa, jwt签名加密解密

ThinkPhp 发表时间:2021-04-23 14:11:03 作者:梁子亮 浏览次数:1318

1、HMAC-SHA256 加密(不可逆无法解密)

假设:

appkey 为:testappkey

appsecret 为:testappsecret

当前时间戳为:1562912285

拼装原串为:testappkey1562912285testappsecret

则 HMAC-SHA256 加密后得到的值:559c4fa92e47b7f148a788300c6a345feddbd327b9fc551f100de679bbb247c5

$string = hash_hmac("sha256",'testappkey1562912285testappsecret','testappsecret'); // 559c4fa92e47b7f148a788300c6a345feddbd327b9fc551f100de679bbb247c5


2、AES(CipherMode.CBC PaddingMode.PKCS7)加密

假设:

appsecret 为:0688f7a191da4fbab177fd1c8ef19901

aesIV 为:ff465fdecc764337


加密前数据示例:{"settlement_code":["JS19BUB14F5D8D4C"],"random_code":["19BUB14F5D8D4C","19BUAD0E89D780"]}


则 加密后的值为(以下三个方法都一样):{"data":"236agZcupcSsMZghtlmzhb7lEWzGZc3FO5GWQyrSB5kP/y1ESvd+CuBgQiWU/fwAICY/s0mideku/rXSKEb8In41F4SkUVLyLzYoYGed4QTjsqohTM0T6wmbkOiT1TH3"}

$string = '{"settlement_code":["JS19BUB14F5D8D4C"],"random_code":["19BUB14F5D8D4C","19BUAD0E89D780"]}';
$aesIV = 'ff465fdecc764337';
$appsecret = '0688f7a191da4fbab177fd1c8ef19901';
$newText = openssl_encrypt($string,'AES-256-CBC',$appsecret,false,$aesIV);
$newText = openssl_encrypt($string,'AES-256-CBC-HMAC-SHA1',$appsecret,false,$aesIV);
$newText = openssl_encrypt($string,'AES-256-CBC-HMAC-SHA256',$appsecret,false,$aesIV);

若不知道使openssl_encrypt的第二个参数传什么方法,则可以使用以下方法再for循环逐个尝试

$method = openssl_get_cipher_methods();


3、AES(CipherMode.CBC PaddingMode.PKCS7)解密

$data = openssl_decrypt($newText,'AES-256-CBC',$appsecret,false,$aesIV);

得到的data即为加密前数据:{"settlement_code":["JS19BUB14F5D8D4C"],"random_code":["19BUB14F5D8D4C","19BUAD0E89D780"]}



4、舌诊的aes加密示例

$key = 'y82caGkvfRvjtRNngcpHzA==';
$age_sex['age'] = $age;
$age_sex['sex'] = $sex;
$encrypt_data =  openssl_encrypt(json_encode($age_sex), 'aes-128-ecb', base64_decode($key), OPENSSL_RAW_DATA);
$data['encryptData'] = base64_encode($encrypt_data);


5、舌诊的aes解密示例

$key = 'y82caGkvfRvjtRNngcpHzA==';
$encrypted = base64_decode($param['encryptData']); // $param['encryptData']为舌诊接口返回的待解密数据
$data = json_decode(openssl_decrypt($encrypted, 'aes-128-ecb', base64_decode($key), OPENSSL_RAW_DATA),true);


6、rsa签名(使用私钥生成签名)

/**********************************私钥格式化*************************************/
private function formatPriKey($priKey) {
    $fKey = "-----BEGIN PRIVATE KEY-----\n";

    $len = strlen($priKey);

    for($i = 0; $i < $len; ) {
        $fKey = $fKey . substr($priKey, $i, 64) . "\n";
        $i += 64;
    }

    $fKey .= "-----END PRIVATE KEY-----";

    return $fKey;
}

/**********************************公钥格式化*************************************/
private function formatPubKey($pubKey) {
    $fKey = "-----BEGIN PUBLIC KEY-----\n";
    $len = strlen($pubKey);
    for($i = 0; $i < $len; ) {
        $fKey = $fKey . substr($pubKey, $i, 64) . "\n";
        $i += 64;
    }
    $fKey .= "-----END PUBLIC KEY-----";
    return $fKey;
}

/**
 * 配置私钥
 * openssl_pkey_get_private这个函数可用来判断私钥是否是可用的,可用,返回资源
 * @return bool|resource
 */
private function getPrivateKey()
{
    $original_private_key = $this->private_key;
    return openssl_pkey_get_private($original_private_key);
}


/**
 * 配置公钥
 * openssl_pkey_get_public这个函数可用来判断私钥是否是可用的,可用,返回资源
 * @return resource
 */
public function getPublicKey()
{
    $original_public_key = $this->public_key;
    return openssl_pkey_get_public($original_public_key);
}


/**
 * 生成签名
 * @param    string     $signString 待签名字符串
 * @return   string     base64结果值
 * OPENSSL_ALGO_DSS1
 * OPENSSL_ALGO_SHA1
 * OPENSSL_ALGO_SHA224
 * OPENSSL_ALGO_SHA256
 * OPENSSL_ALGO_SHA384
 * OPENSSL_ALGO_SHA512
 * OPENSSL_ALGO_RMD160
 * OPENSSL_ALGO_MD5
 * OPENSSL_ALGO_MD4
 * OPENSSL_ALGO_MD2
 */
public function getSign($signString){
    $signature = '';
    $this->private_key = $this->formatPriKey(config('tongue.rsa_devprivate_key'));
    $this->private_key = $this->getPrivateKey();
    openssl_sign($signString, $signature, $this->private_key,OPENSSL_ALGO_MD5);
    return base64_encode($signature);
}


7、rsa签名验签(使用公钥验签)

/**
 * 校验签名
 * @param    string     $sign   签名
 * @param    string     $toSign 待签名字符串
 * OPENSSL_ALGO_DSS1
 * OPENSSL_ALGO_SHA1
 * OPENSSL_ALGO_SHA224
 * OPENSSL_ALGO_SHA256
 * OPENSSL_ALGO_SHA384
 * OPENSSL_ALGO_SHA512
 * OPENSSL_ALGO_RMD160
 * OPENSSL_ALGO_MD5
 * OPENSSL_ALGO_MD4
 * OPENSSL_ALGO_MD2
 * @return   bool
 */
function checkSign($sign,$toSign){
    $re = $this->formatPubKey(config('tongue.rsa_companypublic_key'));
    $re = openssl_pkey_get_public($re);
    $result = openssl_verify($toSign, base64_decode($sign), $re,OPENSSL_ALGO_MD5);
    return $result === 1 ? true : false;
}


8、JWT加密&签名验证(简单的PHP示例),详情可参考JWT官网:https://jwt.io

与jwt官网中的base64UrlEncode中对应的php的实现方法为

/**
     * base64UrlEncode   https://jwt.io/  中base64UrlEncode编码实现
     * @param string $input 需要编码的字符串
     * @return string
     */
private static function base64UrlEncode(string $input)
{
    return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
}

与jwt官网中的base64UrlEncode中对应的php的实现方法为

/**
     * base64UrlEncode  https://jwt.io/  中base64UrlEncode解码实现
     * @param string $input 需要解码的字符串
     * @return bool|string
     */
private static function base64UrlDecode(string $input)
{
    $remainder = strlen($input) % 4;
    if ($remainder) {
        $addlen = 4 - $remainder;
        $input .= str_repeat('=', $addlen);
    }
    return base64_decode(strtr($input, '-_', '+/'));
}

$header,$payload,$secret 3个参数分别如下

$header['alg'] = 'HS256';
$header['typ'] = 'JWT';
$payload['sub'] = '1234567890';
$payload['name'] = 'John!~#423▼Doe';
$payload['iat'] = 1516239022;
$secret = '123456';

jwt加密后的值为

$re = hash_hmac( 'sha256', $this->base64UrlEncode(json_encode($header,JSON_UNESCAPED_UNICODE)) . "." . $this->base64UrlEncode(json_encode($payload,JSON_UNESCAPED_UNICODE)), $secret, true );

打印后对比官网的值,一致

print_dump($this->base64UrlEncode(json_encode($header,JSON_UNESCAPED_UNICODE)) . "." . $this->base64UrlEncode(json_encode($payload,JSON_UNESCAPED_UNICODE)) . "." . $this->base64UrlEncode($re));die;

注意:若官网中勾选“secret base64 encoded”选项,表示官网中secret的值要填base64加密后的值(最好用base64UrlEncode方法加密的而不是用php的base64_encode加密的,即这里的private static function base64UrlEncode方法),即例如此php例子中$secret = '123456',则官网中若勾选“secret base64 encoded”后填入的值为'MTIzNDU2',加密的结果一致

另:jwt的其他加密方法入HS384等,均与此类似


9、js、c#与php公用的aes加密解密方式,详情可参考https://www.cnblogs.com/zjbky/p/6065829.html

先设置好key,iv,以及待加密的字符串str

$key = "zhimeikm                        ";
$iv = "1234567812345678";
$str = '{"d":"211022151725","u":"klfjeioadnfg"}';
$str_padded = '{"d":"211022151725","u":"klfjeioadnfg"}';

因为使用OPENSSL_NO_PADDING方式,所以待加密的字符串为16个字,不够的话要填充零

if (strlen($str) % 16) {
    $str = str_pad($str,strlen($str) + 16 - strlen($str) % 16, "\0");
}

加密的结果为

$en_data = base64_encode(openssl_encrypt($str, "AES-256-CBC", $key, OPENSSL_NO_PADDING, $iv));
print_dump($en_data);
print_dump(openssl_error_string());

解密时,需要注意去除填充的零(貌似不去除也没问题)

$de_data = openssl_decrypt(base64_decode($en_data), "AES-256-CBC", $key, OPENSSL_NO_PADDING, $iv);
print_dump(rtrim($de_data)); // 一般情况下rtrim一下即可得到想要的结果,无需以下的chr(0),chr(7)截取
print_dump( rtrim( rtrim( $de_data,chr(0) ), chr(7) ) );
print_dump(openssl_error_string());