分类 PHP 下的文章

开发银行或海关的项目经常遇到加密解密的问题

一般客户会提供两个文件
.pfx 结尾的(公钥加私钥文件)
.cer 结尾的文件 (包含公钥文件)

私钥一般都是pfx格式(私钥用来加密生成签名发送报文)
公钥是cer格式(公钥用来验证返回报文里的签名)。
但是phpopenssl只能用pem格式,需要把这两个文件转化成pem的:

一、PHP的方式转化

//filePath为pfx文件路径
function signfrompfx($strData, $filePath, $keyPass)
{
    if (!file_exists($filePath)) {
        return false;
    }
    $pkcs12 = file_get_contents($filePath);
    if (openssl_pkcs12_read($pkcs12, $certs, $keyPass)) {
        $privateKey = $certs['pkey'];
        $publicKey = $certs['cert'];
        $signedMsg = "";
        if (openssl_sign($strData, $signedMsg, $privateKey)) {
            $signedMsg = bin2hex($signedMsg);//这个看情况。有些不需要转换成16进制,有些需要base64编码。看各个接口
            return $signedMsg;
        } else {
            return '';
        }
    } else {
        return '0';
    }
}

公钥cer转pem(即x.509证书dem格式转换为pem)

function verifyReturn($data,$signature,$filePath){
    /**
     * filePath为crt,cert文件路径。x.509证书
     * cer to dem, Convert .cer to .pem, cURL uses .pem
     */
    $certificateCAcerContent = file_get_contents($filePath);
    $certificateCApemContent =
        '-----BEGIN CERTIFICATE-----'.PHP_EOL
        .chunk_split(base64_encode($certificateCAcerContent), 64, PHP_EOL)
        .'-----END CERTIFICATE-----'.PHP_EOL;

    $pubkeyid = openssl_get_publickey($certificateCApemContent);

    $len = strlen($signature);
    $signature= pack("H" . $len, $signature); //Php-16进制转换为2进制,看情况。有些接口不需要,有些需要base64解码

    $data=str_replace('<?xml version=\"1.0\" encoding=\"GBK\"?>', '<?xml version="1.0" encoding="GBK"?>', $data);//这个看情况。
    // state whether signature is okay or not
    return openssl_verify($data, $signature, $pubkeyid);
}

二、openssl命令转化

openssl x509 -inform der -in pub.cer -out pub.pem


三、附上截图

20200804133845380.png

转自:https://blog.csdn.net/weixin_38626872/article/details/107784610

PHP

<?php
ini_set("error_reporting","E_ALL & ~E_NOTICE");
header('Content-Type:application/json; charset=utf-8');
if (!function_exists('openssl_encrypt')){
    exit('请开启php.ini的openssl扩展');
}
$input="123456";   //要加密的字符串
$deskey='QDHPJKOS';//算法密钥 
$des = new Des();
$encode=$des->encrypt($input,$deskey);
echo "DES加密结果:".$encode;
$decode=$des->decrypt($encode,$deskey);
echo "\r\nDES解密结果:".$decode;

/**
 * DES加解密类
 */
class Des{
   
    /**
     *
     * 加密函数
     * 算法:des
     * 加密模式:ecb
     * 补齐方法:PKCS5
     *
     * @param unknown_type $input
     */
    public function encrypt($input, $key)
    {
        //由于php7.1废弃了mcrypt_* 一系列函数,所以采用openssl版本
        $str = $this->pkcsPadding($input, 8);
        $key = str_pad($key, 8, '0'); //3DES加密将8改为24
        $sign = @openssl_encrypt($str, 'DES-ECB', $key,OPENSSL_RAW_DATA | OPENSSL_NO_PADDING);
        //转为base64,可以有效解决乱码等问题
        $sign = base64_encode($sign);
        return $sign;
    }
    
    /**
     * 解密函数
     * 算法:des
     * 加密模式:ecb
     * 补齐方法:PKCS5
     * @param unknown_type $input
     */
    public function decrypt($input, $key)
    {
        //由于php7.1废弃了mcrypt_* 一系列函数 所以采用openssl版本
        $encrypted = base64_decode($input);
        $key = str_pad($key, 8, '0'); //3DES加密将8改为24
        $sign = @openssl_decrypt($encrypted, 'DES-ECB', $key,OPENSSL_RAW_DATA | OPENSSL_NO_PADDING);
        $sign = $this->unPkcsPadding($sign);
        $sign = rtrim($sign);
        return $sign;
    }
    /**
     * 填充
     *
     * @param $text
     * @param $blocksize
     * @return string
     */
    private function pkcsPadding($text, $blocksize)
    {
        $pad = $blocksize - (strlen($text) % $blocksize);
        return $text . str_repeat(chr($pad), $pad);
    }
 
     /**
     * 去填充
     * 
     * @param $text
     * @return string
     */
    private function unPkcsPadding($text)
    {
        $pad = ord($text {strlen($text) - 1});
        if ($pad > strlen($text))
            return false;
        if (strspn($text, chr($pad), strlen($text) - $pad) != $pad)
            return false;
        return substr($text, 0, - 1 * $pad);
    }
    
}

JAVA

package demoSys;

import java.security.SecureRandom;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;

public class Test {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String input = "123456";
        String deskey = "QDHPJKOS";
        String encode = encrypt(input, deskey);
        System.out.println("DES加密结果:" + encode);
        String decode = decrypt(encode, deskey);
        System.out.println("DES解密结果:" + decode);
    }

    public static String encrypt(String souce, String key) {
        try {
            // DES算法要求有一个可信任的随机数源
            SecureRandom sr = new SecureRandom();
            // 从原始密匙数据创建DESKeySpec对象
            DESKeySpec dks = new DESKeySpec(key.getBytes("UTF-8"));
            // 创建一个密匙工厂,然后用它把DESKeySpec转换成 一个SecretKey对象
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey secretkey = keyFactory.generateSecret(dks);
            // Cipher对象实际完成加密操作
            Cipher cipher = Cipher.getInstance("DES");
            // 用密匙初始化Cipher对象
            cipher.init(Cipher.ENCRYPT_MODE, secretkey, sr);
            // 获取数据并加密
            byte encryptedData[] = cipher.doFinal(souce.getBytes("UTF-8"));
            // JDK1.8及以上可直接使用Base64,JDK1.7及以下可以使用BASE64Encoder
            // Android平台可以使用android.util.Base64
            return new String(Base64.getEncoder().encode(encryptedData));
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            return souce;
        }

    }

    public static String decrypt(String souce, String key) {
        try {
            // DES算法要求有一个可信任的随机数源
            SecureRandom sr = new SecureRandom();
            // 从原始密匙数据创建DESKeySpec对象
            DESKeySpec dks = new DESKeySpec(key.getBytes());
            // 创建一个密匙工厂,然后用它把DESKeySpec转换成 一个SecretKey对象
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey secretkey = keyFactory.generateSecret(dks);
            // Cipher对象实际完成加密操作
            Cipher cipher = Cipher.getInstance("DES");
            // 用密匙初始化Cipher对象
            cipher.init(Cipher.DECRYPT_MODE, secretkey, sr);
            // 将加密报文用BASE64算法转化为字节数组
            byte[] encryptedData = Base64.getDecoder().decode(souce);
            // 用DES算法解密报文
            byte decryptedData[] = cipher.doFinal(encryptedData);
            return new String(decryptedData, "UTF-8");
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            return souce;
        }

    }

}


PHP扩展gmp被加载两次,报错如下:

PHP Warning: Module "gmp" is already loaded in Unknown on line 0

解决办法:

Not all PHP modules are loaded as separate modules and are part of the
PHP base binary. In this case the module is loaded without a separate
load statement/configuration. If in addition a separate configuration
file exists, e.g. /etc/php7/fpm/conf.d/gmp.ini, the web server tries
to load the module twice which can cause these kind of messages.
//打开.ini配置文件,将下面这行注销即可;
extension=gmp.so

原因:

是因为.ini文件底部又加载了一次,如下:

extension =  /www/server/php/80/lib/php/extensions/no-debug-non-zts-20200930/gmp.so

与G银行对接时用到的

<?php
/**
 * Describe:
 * Author: liziyu
 * Date: 2022/10/31 11:10 AM
 */

namespace App\encrypt;

class MD5WithRSA
{
    /**
     * 利用约定数据和私钥生成数字签名
     * @param $data 待签数据
     * @return String 返回签名
     */
    public function sign($data='')
    {
        if (empty($data))
        {
            return false;
        }
        $private_key="MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgE==";
        $private_key = chunk_split($private_key, 64, "\n");
        $private_key = "-----BEGIN PRIVATE KEY-----\n$private_key-----END PRIVATE KEY-----";

        if (empty($private_key))
        {
            echo "私钥为空";
            return false;
        }
        // 生成密钥资源id
        $private_key_resource_id = openssl_get_privatekey($private_key);
        if (empty($private_key_resource_id))
        {
            echo "私钥资源id为空";
            return false;
        }

        $verify = openssl_sign($data, $signature, $private_key_resource_id, OPENSSL_ALGO_MD5);
        openssl_free_key($private_key_resource_id);
        // 二次Base64编码
        return base64_encode(base64_encode($signature));
    }

    /**
     * 利用公钥和数字签名以及约定数据验证合法性
     * @param string $data 待验证数据
     * @param string $signature 数字签名
     * @return  -1 验证错误;0 验证失败;1 验证成功
     */
    public function isValid($data='', $signature='')
    {
        if (empty($data) || empty($signature))
        {
            return false;
        }

        $public_key ="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB";
        $public_key = chunk_split($public_key, 64, "\n");
        $public_key = "-----BEGIN PUBLIC KEY-----\n$public_key-----END PUBLIC KEY-----";

        if (empty($public_key))
        {
            echo "公钥为空";
            return false;
        }
        // 生成密钥资源id
        $public_key_resource_id = openssl_get_publickey($public_key);
        if (empty($public_key_resource_id))
        {
            echo "公钥资源id为空";
            return false;
        }

        // 对签名进行二次Base64解码
        $signature=base64_decode(base64_decode($signature));
        $ret = openssl_verify($data, $signature, $public_key_resource_id, OPENSSL_ALGO_MD5);
        openssl_pkey_free($public_key_resource_id);
        return $ret;
    }
}

步骤一:

先检测本机是否有gmp扩展在mamp下,命令如下:

/Applications/MAMP/bin/php/php8.0.8/bin/php -i | grep gmp

如果没有此扩展,此时什么也不会出现,如果已经成功安装了扩展,将会出现如下所示的提示:

PWD => /Applications/MAMP/bin/php/php8.0.8/include/php/ext/gmp
$_SERVER['PWD'] =>
/Applications/MAMP/bin/php/php8.0.8/include/php/ext/gmp $_ENV['PWD']
=> /Applications/MAMP/bin/php/php8.0.8/include/php/ext/gmp

步骤二:

php官网下载其源码包php-8.0.8.tar.gz
https://www.php.net/releases/

注意:一定要与你的现在版本一致辞,必须,必须,必须!

可以下载到任意目录,下载完成以后,解压。找到目录.
111.png

步骤三:

将目录/Users/liziyu/Downloads/php-8.0.8/ext/,将gmp目录整拷贝到/Applications/MAMP/bin/php/php8.0.8/include/php/ext目录下,如图:
222.png

步骤四:

同时进入该目录下:

cd /Applications/MAMP/bin/php/php8.0.8/include/php/ext/gmp

执行命令:

/Applications/MAMP/bin/php/php8.0.8/bin/phpize

然后执行:

./configure --with-php-config=/Applications/MAMP/bin/php/php8.0.8/bin/php-config

再执行:

make

最后:

make install

3333.png

当出现如下图所示的提示时说明已经安装成功了。
此时重启php,然后执行

php -m 

成功的话会出现如下图的提示:
444.png

如果还没有此提示的话,则进入php的配置文件ini去修改,增加下面一行代码:

extension=gmp.so

再重启php查看。即可

本文参考:https://mycodde.blogspot.com/2020/01/install-php-gmp-extension-in-mamp-2020.html

这几天和业务方有个签名验签的的需求,对方使用Java对业务数据进行签名,我方使用PHP验签,使用SHA256withRSA算法签名验签,PHP和Java单独签名和验签都没问题,但是由Java签名的数据请求到PHP端时一直验签不通过。
分别对比了Java和PHP生成的待验签字符串和生成的签名,都是一致的,同时也确认过私钥和公钥是一对,但是一到PHP的验签方法openssl_verify()时就是通不过,是在不知道原因出在哪。请求大神们帮忙看看是什么原因,万分感激。

PHP代码

class LubanPay{
    /**
     * 字符串签名
     * @param string $resource 经过urlencode处理过的字符串
     * @return string
     */
    public function SignStrMessage(string $resource):string {
        $privateKey = openssl_get_privatekey($this->GetPrivateKey());
        $res        = openssl_sign($resource, $signature, $privateKey,OPENSSL_ALGO_SHA256);
        openssl_free_key($privateKey);
        if ($res) {
            return base64_encode($signature);
        }else {
            throw new \Exception("String Sign Failed",10004);
        }
    }
    /**
     * 字符串验签
     * @param string $resource
     * @param string $signature
     * @return bool
     */
    public function VerifyStrMessage(string $resource,string $signature,string $mch_public_key):bool {
        $signature  = base64_decode($signature);
        $publicKey  = openssl_get_publickey($this->GetPublicKey($mch_public_key));
        $res        = openssl_verify($resource, $signature, $publicKey,OPENSSL_ALGO_SHA256);
        openssl_free_key($publicKey);
        return $res===1?true:false;
    }

    /**
     * 获取平台私钥
     * @return string
     */
    private function GetPrivateKey():string {
        $privateKey     = Config::$luban_private_key;
        $privateKey     = chunk_split($privateKey, 64, "\n");
        $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n$privateKey-----END RSA PRIVATE KEY-----\n";
        return $privateKey;
    }

    /**
     * 获取商户公钥
     * @param string $type
     * @return string
     */
    private function GetPublicKey($mer_public_key):string{
        $publicKey               = chunk_split($mer_public_key, 64, "\n");
        $publicKey = "-----BEGIN PUBLIC KEY-----\n$publicKey-----END PUBLIC KEY-----\n";
        return $publicKey;
    }

}

Java代码

public static String private_key = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC4RPxDH2GdsIcyDq5ApUfLU0+Gst6aJZwjoqvVxxGSRTlKx10VZHHZ7goqrNSNWVK+WPbz6vCItSgHb1t3eRMkwPEaGcstvcf7krEGOH6CWYXe74OTbFet9Oq1Jdzz7UHNNeRyHO/JnNbVh8kPSqIL2368g+lII0u7zP4OWGw+qht75VY0vKI3dQdK2jFidkRHtEmV0+ui+9RVEtIlURYxkaAHWoUNWov8505cAc+pmKrUYOJf7Q50t+AlClIgngzF2yqT5HAKKYbQ9R6CgKvB9JcAXL0nXi2bExjOQsQa3SalLXxjeNKpjJKtytUAEVaehtA5PCi0VV1jZSGKpTxlAgMBAAECggEAGIJsf00UQdIyGVFkkgqp4vyAzmzKOPyZqQ/BBV1GFAuLFEwyMF882XzU81orp2VjIRhaOJVeSwC1g0+nfdun1TKonw0hPkNI70hSrX4kLZhUuxNmj9xQST4TXebcXcGICBCMAzWgG1P2K061Sohlx2f5kn+FLugq8Z7Rh/zw4OCrFeDZ0YJZKpNk1JwBp3arbSphlbw3ZzTF6g7kukfilXuWNnoqDf341H5rVUY7k4cbej8x6pc6Zrnla33VM5m37+5bXnGnE1Sx7PjVgn+2ZADIhTn8e94bLZacJofrF1kCcScO1TRH4eaqlyhiXsjMy0Cm4OxCY+Uydj3NtOFvHQKBgQD5Nsr83bfAuHCA3dEaclQoBbAD1Qiz3rQPvHh2Zt7WIzKJ4NWVnypp22eCYTgft/X7cXXF7D2yokfOCc9DMfLVOOZzcRSwifsakPWXWKAtcNZZW+1ffgObq4I4tNO9Llb6hIN3pcxf7yBtrMib9C/cqwUAQM3k5bsFTZgjCZI5qwKBgQC9SXsJel+FDFlT4jfda/9fEx8uSOP4jflSn83CLI7UQ9/utR5ldJbllwWwrS5k6nqgi5K9AzOhxxfQ3m6FGJRDzqwyfK05uvuM1CqRMpe5FyQKTpPodWJ3+8oIcluRNaWwuAp+FNWPiGSNnhksS7i54jJAOh9nIdB1aSseq9vyLwKBgFJENinW/wuNVwYTMy2pxAIaLop1TpQh1grDyng7aSADKnG9WIQ1sIiVNswhT6eY0IiaYaheXdeUHmPzdQnXeTPNvrUpBQ1p3wxcAdZeGTIm53tED03QiVxf93LErojqvSehisx6XMbmZywNN4PTzeDoS5RT0CPZei07+hbG2BBVAoGAW6HszAPPper6e18xyCD1+SKan59trO+d2N+/jdZgNmW9TCOl2Vt9iRt5B7Ruly/juUCYAqRAJHrrDpP/ULM7Yy/zsGUmvqHEEMLM8IlbZaDMM6kidRAOYSMlBL3Hkh40Xb5aZfrT/635b40vhoAJpwLXbLw2Y4i9D3mgBDMSQMUCgYBLkj6pnTvJtgR7xCAKaVoERYXmsDjP/gIhSrmnqfO8EZDheVoh+XbN5o3D27aBmWiWTwgJoMuF5GxEL2tSbQBvkT6smTrVBDF8Q8+tgBvP2CV0gwto+Spv5b9h7dNWk5UI0ZOWnlF1aI37UJBKmyp2kYCoDwnA1vtFpXncmaNHxw==";

public static void main(String[] args) throws UnsupportedEncodingException {
    test();
}


public static void test() throws UnsupportedEncodingException {
//构造待签名字符串  
    String string = "content=hello" + "&mch_id=1&method=pay&nonce_str=1&time_stamp=1";
    System.out.println("拼接字符串:" + string);
//加密字符串  
    String str = sign(string.getBytes("utf-8"));
    System.out.println("字符串加密:" + str);
    String authorization = "mch_id=1&method=pay&nonce_str=1&sign=" + URLEncoder.encode(str, "UTF-8") + "&time_stamp=1";
    System.out.println("请求头:" + authorization);
    System.out.println("请求结果:" + DemoCall.doPost("pay/api/v1", "hello", authorization));
}


/**
 * 将字节数组内信息使用签名进行加密
 *
 * @param message
 * @return
 */
public static String sign(byte[] message) {
    Signature sign;
    try {
        sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(getPrivateKey());
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (SignatureException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    }
    return null;
}

/**
 * 获取私钥。
 *
 * @param filename 私钥文件路径 (required)
 * @return 私钥对象
 */
public static PrivateKey getPrivateKey() {
    String content = private_key;
    try {
        String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                .replace("-----END PRIVATE KEY-----", "")
                .replaceAll("\\s+", "");
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return kf.generatePrivate(
                new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException("当前Java环境不支持RSA", e);
    } catch (InvalidKeySpecException e) {
        throw new RuntimeException("无效的密钥格式");
    }
}

转载:https://segmentfault.com/q/1010000022260275

这个需求先后尝试了几个aes.js包,先后不尽如意,可能水平有限没有调通。准确的说,前后端没有调通,下面这种方法亲测有效,下面记录并分享给需要的人:

PHP代码:

$key = 'aaaaaaaaaaaaaaaa'; //16位,这个长度需要与前端匹配
$iv = 'bbbbbbbbbbbbbbbb'; //16位,这个需要与前端吻合

if(!function_exists('aes_encrypt')) {
    /**
     * 加密并转BASE64 AES-128-CBC
     * @param $data
     * @return string
     */
    function aes_encrypt($data): string
    {
        return base64_encode(openssl_encrypt($data, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv));
    }
}

if(!function_exists('aes_decrypt')) {
    /**
     * 解密并转BASE64 AES-128-CBC
     * @param $data
     * @return string
     */
    function aes_decrypt($data): string
    {
        return openssl_decrypt(base64_decode($data), 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
    }
}



前端js部分

第一部分eas.js

https://github.com/kk196110/wechat/blob/master/utils/aes.js

考虑到有许多网友打开github困难,所以我把它直接拷贝下来了,如下:

/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
var CryptoJS=CryptoJS||function(u,p){var d={},l=d.lib={},s=function(){},t=l.Base={extend:function(a){s.prototype=this;var c=new s;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
r=l.WordArray=t.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=p?c:4*a.length},toString:function(a){return(a||v).stringify(this)},concat:function(a){var c=this.words,e=a.words,j=this.sigBytes;a=a.sigBytes;this.clamp();if(j%4)for(var k=0;k<a;k++)c[j+k>>>2]|=(e[k>>>2]>>>24-8*(k%4)&255)<<24-8*((j+k)%4);else if(65535<e.length)for(k=0;k<a;k+=4)c[j+k>>>2]=e[k>>>2];else c.push.apply(c,e);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
32-8*(c%4);a.length=u.ceil(c/4)},clone:function(){var a=t.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],e=0;e<a;e+=4)c.push(4294967296*u.random()|0);return new r.init(c,a)}}),w=d.enc={},v=w.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j<a;j++){var k=c[j>>>2]>>>24-8*(j%4)&255;e.push((k>>>4).toString(16));e.push((k&15).toString(16))}return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j<c;j+=2)e[j>>>3]|=parseInt(a.substr(j,
2),16)<<24-4*(j%8);return new r.init(e,c/2)}},b=w.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j<a;j++)e.push(String.fromCharCode(c[j>>>2]>>>24-8*(j%4)&255));return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j<c;j++)e[j>>>2]|=(a.charCodeAt(j)&255)<<24-8*(j%4);return new r.init(e,c)}},x=w.Utf8={stringify:function(a){try{return decodeURIComponent(escape(b.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return b.parse(unescape(encodeURIComponent(a)))}},
q=l.BufferedBlockAlgorithm=t.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=x.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,e=c.words,j=c.sigBytes,k=this.blockSize,b=j/(4*k),b=a?u.ceil(b):u.max((b|0)-this._minBufferSize,0);a=b*k;j=u.min(4*a,j);if(a){for(var q=0;q<a;q+=k)this._doProcessBlock(e,q);q=e.splice(0,a);c.sigBytes-=j}return new r.init(q,j)},clone:function(){var a=t.clone.call(this);
a._data=this._data.clone();return a},_minBufferSize:0});l.Hasher=q.extend({cfg:t.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){q.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(b,e){return(new a.init(e)).finalize(b)}},_createHmacHelper:function(a){return function(b,e){return(new n.HMAC.init(a,
e)).finalize(b)}}});var n=d.algo={};return d}(Math);
(function(){var u=CryptoJS,p=u.lib.WordArray;u.enc.Base64={stringify:function(d){var l=d.words,p=d.sigBytes,t=this._map;d.clamp();d=[];for(var r=0;r<p;r+=3)for(var w=(l[r>>>2]>>>24-8*(r%4)&255)<<16|(l[r+1>>>2]>>>24-8*((r+1)%4)&255)<<8|l[r+2>>>2]>>>24-8*((r+2)%4)&255,v=0;4>v&&r+0.75*v<p;v++)d.push(t.charAt(w>>>6*(3-v)&63));if(l=t.charAt(64))for(;d.length%4;)d.push(l);return d.join("")},parse:function(d){var l=d.length,s=this._map,t=s.charAt(64);t&&(t=d.indexOf(t),-1!=t&&(l=t));for(var t=[],r=0,w=0;w<
l;w++)if(w%4){var v=s.indexOf(d.charAt(w-1))<<2*(w%4),b=s.indexOf(d.charAt(w))>>>6-2*(w%4);t[r>>>2]|=(v|b)<<24-8*(r%4);r++}return p.create(t,r)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})();
(function(u){function p(b,n,a,c,e,j,k){b=b+(n&a|~n&c)+e+k;return(b<<j|b>>>32-j)+n}function d(b,n,a,c,e,j,k){b=b+(n&c|a&~c)+e+k;return(b<<j|b>>>32-j)+n}function l(b,n,a,c,e,j,k){b=b+(n^a^c)+e+k;return(b<<j|b>>>32-j)+n}function s(b,n,a,c,e,j,k){b=b+(a^(n|~c))+e+k;return(b<<j|b>>>32-j)+n}for(var t=CryptoJS,r=t.lib,w=r.WordArray,v=r.Hasher,r=t.algo,b=[],x=0;64>x;x++)b[x]=4294967296*u.abs(u.sin(x+1))|0;r=r.MD5=v.extend({_doReset:function(){this._hash=new w.init([1732584193,4023233417,2562383102,271733878])},
_doProcessBlock:function(q,n){for(var a=0;16>a;a++){var c=n+a,e=q[c];q[c]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360}var a=this._hash.words,c=q[n+0],e=q[n+1],j=q[n+2],k=q[n+3],z=q[n+4],r=q[n+5],t=q[n+6],w=q[n+7],v=q[n+8],A=q[n+9],B=q[n+10],C=q[n+11],u=q[n+12],D=q[n+13],E=q[n+14],x=q[n+15],f=a[0],m=a[1],g=a[2],h=a[3],f=p(f,m,g,h,c,7,b[0]),h=p(h,f,m,g,e,12,b[1]),g=p(g,h,f,m,j,17,b[2]),m=p(m,g,h,f,k,22,b[3]),f=p(f,m,g,h,z,7,b[4]),h=p(h,f,m,g,r,12,b[5]),g=p(g,h,f,m,t,17,b[6]),m=p(m,g,h,f,w,22,b[7]),
f=p(f,m,g,h,v,7,b[8]),h=p(h,f,m,g,A,12,b[9]),g=p(g,h,f,m,B,17,b[10]),m=p(m,g,h,f,C,22,b[11]),f=p(f,m,g,h,u,7,b[12]),h=p(h,f,m,g,D,12,b[13]),g=p(g,h,f,m,E,17,b[14]),m=p(m,g,h,f,x,22,b[15]),f=d(f,m,g,h,e,5,b[16]),h=d(h,f,m,g,t,9,b[17]),g=d(g,h,f,m,C,14,b[18]),m=d(m,g,h,f,c,20,b[19]),f=d(f,m,g,h,r,5,b[20]),h=d(h,f,m,g,B,9,b[21]),g=d(g,h,f,m,x,14,b[22]),m=d(m,g,h,f,z,20,b[23]),f=d(f,m,g,h,A,5,b[24]),h=d(h,f,m,g,E,9,b[25]),g=d(g,h,f,m,k,14,b[26]),m=d(m,g,h,f,v,20,b[27]),f=d(f,m,g,h,D,5,b[28]),h=d(h,f,
m,g,j,9,b[29]),g=d(g,h,f,m,w,14,b[30]),m=d(m,g,h,f,u,20,b[31]),f=l(f,m,g,h,r,4,b[32]),h=l(h,f,m,g,v,11,b[33]),g=l(g,h,f,m,C,16,b[34]),m=l(m,g,h,f,E,23,b[35]),f=l(f,m,g,h,e,4,b[36]),h=l(h,f,m,g,z,11,b[37]),g=l(g,h,f,m,w,16,b[38]),m=l(m,g,h,f,B,23,b[39]),f=l(f,m,g,h,D,4,b[40]),h=l(h,f,m,g,c,11,b[41]),g=l(g,h,f,m,k,16,b[42]),m=l(m,g,h,f,t,23,b[43]),f=l(f,m,g,h,A,4,b[44]),h=l(h,f,m,g,u,11,b[45]),g=l(g,h,f,m,x,16,b[46]),m=l(m,g,h,f,j,23,b[47]),f=s(f,m,g,h,c,6,b[48]),h=s(h,f,m,g,w,10,b[49]),g=s(g,h,f,m,
E,15,b[50]),m=s(m,g,h,f,r,21,b[51]),f=s(f,m,g,h,u,6,b[52]),h=s(h,f,m,g,k,10,b[53]),g=s(g,h,f,m,B,15,b[54]),m=s(m,g,h,f,e,21,b[55]),f=s(f,m,g,h,v,6,b[56]),h=s(h,f,m,g,x,10,b[57]),g=s(g,h,f,m,t,15,b[58]),m=s(m,g,h,f,D,21,b[59]),f=s(f,m,g,h,z,6,b[60]),h=s(h,f,m,g,C,10,b[61]),g=s(g,h,f,m,j,15,b[62]),m=s(m,g,h,f,A,21,b[63]);a[0]=a[0]+f|0;a[1]=a[1]+m|0;a[2]=a[2]+g|0;a[3]=a[3]+h|0},_doFinalize:function(){var b=this._data,n=b.words,a=8*this._nDataBytes,c=8*b.sigBytes;n[c>>>5]|=128<<24-c%32;var e=u.floor(a/
4294967296);n[(c+64>>>9<<4)+15]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360;n[(c+64>>>9<<4)+14]=(a<<8|a>>>24)&16711935|(a<<24|a>>>8)&4278255360;b.sigBytes=4*(n.length+1);this._process();b=this._hash;n=b.words;for(a=0;4>a;a++)c=n[a],n[a]=(c<<8|c>>>24)&16711935|(c<<24|c>>>8)&4278255360;return b},clone:function(){var b=v.clone.call(this);b._hash=this._hash.clone();return b}});t.MD5=v._createHelper(r);t.HmacMD5=v._createHmacHelper(r)})(Math);
(function(){var u=CryptoJS,p=u.lib,d=p.Base,l=p.WordArray,p=u.algo,s=p.EvpKDF=d.extend({cfg:d.extend({keySize:4,hasher:p.MD5,iterations:1}),init:function(d){this.cfg=this.cfg.extend(d)},compute:function(d,r){for(var p=this.cfg,s=p.hasher.create(),b=l.create(),u=b.words,q=p.keySize,p=p.iterations;u.length<q;){n&&s.update(n);var n=s.update(d).finalize(r);s.reset();for(var a=1;a<p;a++)n=s.finalize(n),s.reset();b.concat(n)}b.sigBytes=4*q;return b}});u.EvpKDF=function(d,l,p){return s.create(p).compute(d,
l)}})();
CryptoJS.lib.Cipher||function(u){var p=CryptoJS,d=p.lib,l=d.Base,s=d.WordArray,t=d.BufferedBlockAlgorithm,r=p.enc.Base64,w=p.algo.EvpKDF,v=d.Cipher=t.extend({cfg:l.extend(),createEncryptor:function(e,a){return this.create(this._ENC_XFORM_MODE,e,a)},createDecryptor:function(e,a){return this.create(this._DEC_XFORM_MODE,e,a)},init:function(e,a,b){this.cfg=this.cfg.extend(b);this._xformMode=e;this._key=a;this.reset()},reset:function(){t.reset.call(this);this._doReset()},process:function(e){this._append(e);return this._process()},
finalize:function(e){e&&this._append(e);return this._doFinalize()},keySize:4,ivSize:4,_ENC_XFORM_MODE:1,_DEC_XFORM_MODE:2,_createHelper:function(e){return{encrypt:function(b,k,d){return("string"==typeof k?c:a).encrypt(e,b,k,d)},decrypt:function(b,k,d){return("string"==typeof k?c:a).decrypt(e,b,k,d)}}}});d.StreamCipher=v.extend({_doFinalize:function(){return this._process(!0)},blockSize:1});var b=p.mode={},x=function(e,a,b){var c=this._iv;c?this._iv=u:c=this._prevBlock;for(var d=0;d<b;d++)e[a+d]^=
c[d]},q=(d.BlockCipherMode=l.extend({createEncryptor:function(e,a){return this.Encryptor.create(e,a)},createDecryptor:function(e,a){return this.Decryptor.create(e,a)},init:function(e,a){this._cipher=e;this._iv=a}})).extend();q.Encryptor=q.extend({processBlock:function(e,a){var b=this._cipher,c=b.blockSize;x.call(this,e,a,c);b.encryptBlock(e,a);this._prevBlock=e.slice(a,a+c)}});q.Decryptor=q.extend({processBlock:function(e,a){var b=this._cipher,c=b.blockSize,d=e.slice(a,a+c);b.decryptBlock(e,a);x.call(this,
e,a,c);this._prevBlock=d}});b=b.CBC=q;q=(p.pad={}).Pkcs7={pad:function(a,b){for(var c=4*b,c=c-a.sigBytes%c,d=c<<24|c<<16|c<<8|c,l=[],n=0;n<c;n+=4)l.push(d);c=s.create(l,c);a.concat(c)},unpad:function(a){a.sigBytes-=a.words[a.sigBytes-1>>>2]&255}};d.BlockCipher=v.extend({cfg:v.cfg.extend({mode:b,padding:q}),reset:function(){v.reset.call(this);var a=this.cfg,b=a.iv,a=a.mode;if(this._xformMode==this._ENC_XFORM_MODE)var c=a.createEncryptor;else c=a.createDecryptor,this._minBufferSize=1;this._mode=c.call(a,
this,b&&b.words)},_doProcessBlock:function(a,b){this._mode.processBlock(a,b)},_doFinalize:function(){var a=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){a.pad(this._data,this.blockSize);var b=this._process(!0)}else b=this._process(!0),a.unpad(b);return b},blockSize:4});var n=d.CipherParams=l.extend({init:function(a){this.mixIn(a)},toString:function(a){return(a||this.formatter).stringify(this)}}),b=(p.format={}).OpenSSL={stringify:function(a){var b=a.ciphertext;a=a.salt;return(a?s.create([1398893684,
1701076831]).concat(a).concat(b):b).toString(r)},parse:function(a){a=r.parse(a);var b=a.words;if(1398893684==b[0]&&1701076831==b[1]){var c=s.create(b.slice(2,4));b.splice(0,4);a.sigBytes-=16}return n.create({ciphertext:a,salt:c})}},a=d.SerializableCipher=l.extend({cfg:l.extend({format:b}),encrypt:function(a,b,c,d){d=this.cfg.extend(d);var l=a.createEncryptor(c,d);b=l.finalize(b);l=l.cfg;return n.create({ciphertext:b,key:c,iv:l.iv,algorithm:a,mode:l.mode,padding:l.padding,blockSize:a.blockSize,formatter:d.format})},
decrypt:function(a,b,c,d){d=this.cfg.extend(d);b=this._parse(b,d.format);return a.createDecryptor(c,d).finalize(b.ciphertext)},_parse:function(a,b){return"string"==typeof a?b.parse(a,this):a}}),p=(p.kdf={}).OpenSSL={execute:function(a,b,c,d){d||(d=s.random(8));a=w.create({keySize:b+c}).compute(a,d);c=s.create(a.words.slice(b),4*c);a.sigBytes=4*b;return n.create({key:a,iv:c,salt:d})}},c=d.PasswordBasedCipher=a.extend({cfg:a.cfg.extend({kdf:p}),encrypt:function(b,c,d,l){l=this.cfg.extend(l);d=l.kdf.execute(d,
b.keySize,b.ivSize);l.iv=d.iv;b=a.encrypt.call(this,b,c,d.key,l);b.mixIn(d);return b},decrypt:function(b,c,d,l){l=this.cfg.extend(l);c=this._parse(c,l.format);d=l.kdf.execute(d,b.keySize,b.ivSize,c.salt);l.iv=d.iv;return a.decrypt.call(this,b,c,d.key,l)}})}();
(function(){for(var u=CryptoJS,p=u.lib.BlockCipher,d=u.algo,l=[],s=[],t=[],r=[],w=[],v=[],b=[],x=[],q=[],n=[],a=[],c=0;256>c;c++)a[c]=128>c?c<<1:c<<1^283;for(var e=0,j=0,c=0;256>c;c++){var k=j^j<<1^j<<2^j<<3^j<<4,k=k>>>8^k&255^99;l[e]=k;s[k]=e;var z=a[e],F=a[z],G=a[F],y=257*a[k]^16843008*k;t[e]=y<<24|y>>>8;r[e]=y<<16|y>>>16;w[e]=y<<8|y>>>24;v[e]=y;y=16843009*G^65537*F^257*z^16843008*e;b[k]=y<<24|y>>>8;x[k]=y<<16|y>>>16;q[k]=y<<8|y>>>24;n[k]=y;e?(e=z^a[a[a[G^z]]],j^=a[a[j]]):e=j=1}var H=[0,1,2,4,8,
16,32,64,128,27,54],d=d.AES=p.extend({_doReset:function(){for(var a=this._key,c=a.words,d=a.sigBytes/4,a=4*((this._nRounds=d+6)+1),e=this._keySchedule=[],j=0;j<a;j++)if(j<d)e[j]=c[j];else{var k=e[j-1];j%d?6<d&&4==j%d&&(k=l[k>>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255]):(k=k<<8|k>>>24,k=l[k>>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255],k^=H[j/d|0]<<24);e[j]=e[j-d]^k}c=this._invKeySchedule=[];for(d=0;d<a;d++)j=a-d,k=d%4?e[j]:e[j-4],c[d]=4>d||4>=j?k:b[l[k>>>24]]^x[l[k>>>16&255]]^q[l[k>>>
8&255]]^n[l[k&255]]},encryptBlock:function(a,b){this._doCryptBlock(a,b,this._keySchedule,t,r,w,v,l)},decryptBlock:function(a,c){var d=a[c+1];a[c+1]=a[c+3];a[c+3]=d;this._doCryptBlock(a,c,this._invKeySchedule,b,x,q,n,s);d=a[c+1];a[c+1]=a[c+3];a[c+3]=d},_doCryptBlock:function(a,b,c,d,e,j,l,f){for(var m=this._nRounds,g=a[b]^c[0],h=a[b+1]^c[1],k=a[b+2]^c[2],n=a[b+3]^c[3],p=4,r=1;r<m;r++)var q=d[g>>>24]^e[h>>>16&255]^j[k>>>8&255]^l[n&255]^c[p++],s=d[h>>>24]^e[k>>>16&255]^j[n>>>8&255]^l[g&255]^c[p++],t=
d[k>>>24]^e[n>>>16&255]^j[g>>>8&255]^l[h&255]^c[p++],n=d[n>>>24]^e[g>>>16&255]^j[h>>>8&255]^l[k&255]^c[p++],g=q,h=s,k=t;q=(f[g>>>24]<<24|f[h>>>16&255]<<16|f[k>>>8&255]<<8|f[n&255])^c[p++];s=(f[h>>>24]<<24|f[k>>>16&255]<<16|f[n>>>8&255]<<8|f[g&255])^c[p++];t=(f[k>>>24]<<24|f[n>>>16&255]<<16|f[g>>>8&255]<<8|f[h&255])^c[p++];n=(f[n>>>24]<<24|f[g>>>16&255]<<16|f[h>>>8&255]<<8|f[k&255])^c[p++];a[b]=q;a[b+1]=s;a[b+2]=t;a[b+3]=n},keySize:8});u.AES=p._createHelper(d)})();
CryptoJS.mode.ECB = (function () {
  var ECB = CryptoJS.lib.BlockCipherMode.extend();

  ECB.Encryptor = ECB.extend({
    processBlock: function (words, offset) {
      this._cipher.encryptBlock(words, offset);
    }
  });

  ECB.Decryptor = ECB.extend({
    processBlock: function (words, offset) {
      this._cipher.decryptBlock(words, offset);
    }
  });

  return ECB;
}());
module.exports = {
  CryptoJS: CryptoJS
}

第二部分,即配置文件

任意命名在此暂且叫它aesUtil.js吧,代码如下:

// util 文件
var aes = require('aes.js'); //引入aes类包
//URl: https://github.com/kk196110/wechat/blob/master/utils/aes.js
 
//十六位十六进制数作为秘钥
var aeskey = aes.CryptoJS.enc.Utf8.parse("pxcvbnmaskoghjkl");
//十六位十六进制数作为秘钥偏移量
var aesiv = aes.CryptoJS.enc.Utf8.parse('frbvcxzlkhugfdhy');
 
// 加密
function encrypt(data) {
    var srcs = aes.CryptoJS.enc.Utf8.parse(data);
    var encrypted = aes.CryptoJS.AES.encrypt(srcs, aeskey, { iv: aesiv, mode: aes.CryptoJS.mode.CBC, padding: aes.CryptoJS.pad.Pkcs7 });
    //返回base64加密结果
    return encrypted.toString();
}
 
//解密
function decrypt(data) {
   // data是base64编码数据
    var decrypt = aes.CryptoJS.AES.decrypt(data, aeskey, { iv: aesiv, mode: aes.CryptoJS.mode.CBC, padding: aes.CryptoJS.pad.Pkcs7 });
    var decryptedStr = decrypt.toString(aes.CryptoJS.enc.Utf8);
    return decryptedStr.toString();
}
 
module.exports ={
     encrypt: encrypt,
     decrypt: decrypt
}

第三部分:引用

微信小程序端引用,比如首页index.js用它。

const encrypt = require('../../utils/aesUtil');
console.log(encrypt.AESEncrypt('hello md5 你好 _-% % &123'))
console.log(encrypt.AESDecrypt('1OloInqltdND24HK7GuecEtkn06kX0h4jPDVRHMGpe8='))
//结果:
//1OloInqltdND24HK7GuecEtkn06kX0h4jPDVRHMGpe8=
//hello md5 你好 _-% % &123









 /**
 * 检查登录频率限制
 *
 * @param $username
 * @return void
 * @throws BusinessException
 */
protected function checkLoginLimit($username)
{
    $username = 'liziyu';
    $limit_log_path = runtime_path() . '/login';
    if (!is_dir($limit_log_path)) {
        mkdir($limit_log_path, 0777, true);
    }
    $limit_file = $limit_log_path . '/' . md5($username) . '.limit';
    $time = date('YmdH') . ceil(date('i')/5);
    $limit_info = [];
    if (is_file($limit_file)) {
        $json_str = file_get_contents($limit_file);
        $limit_info = json_decode($json_str, true);
    }

    if (!$limit_info || $limit_info['time'] != $time) {
        $limit_info = [
            'username' => $username,
            'count' => 0,
            'time' => $time
        ];
    }
    $limit_info['count']++;
    file_put_contents($limit_file, json_encode($limit_info));
    if ($limit_info['count'] >= 5) {
        throw new BusinessException('登录失败次数过多,请5分钟后再试');
    }
}

转自:https://github.com/webman-php/admin/blob/main/src/plugin/admin/app/controller/common/AccountController.php

<?php
/**
 * MineAdmin is committed to providing solutions for quickly building web applications
 * Please view the LICENSE file that was distributed with this source code,
 * For the full copyright and license information.
 * Thank you very much for using MineAdmin.
 *
 * @Author X.Mo<root@imoi.cn>
 * @Link   https://gitee.com/xmo/MineAdmin
 */

namespace Mine\Redis;

use Hyperf\Utils\Coroutine;
use Mine\Abstracts\AbstractRedis;
use Mine\Exception\NormalStatusException;
use Mine\Interfaces\MineRedisInterface;

class MineLockRedis extends AbstractRedis implements MineRedisInterface
{
    /**
     * 设置 key 类型名
     * @param string $typeName
     */
    public function setTypeName(string $typeName): void
    {
        $this->typeName = $typeName;
    }

    /**
     * 获取key 类型名
     * @return string
     */
    public function getTypeName(): string
    {
        return $this->typeName;
    }

    /**
     * 运行锁,简单封装
     * @param \Closure $closure
     * @param string $key
     * @param int $expired
     * @param int $timeout
     * @param float $sleep
     * @return bool
     * @throws \Throwable
     */
    public function run(\Closure $closure, string $key, int $expired, int $timeout = 0, float $sleep = 0.1): bool
    {
        if (! $this->lock($key, $expired, $timeout, $sleep)) {
            return false;
        }

        try {
            call_user_func($closure);
        } catch (\Throwable $e) {
            logger('Redis Lock')->error(t('mineadmin.redis_lock_error'));
            throw new NormalStatusException(t('mineadmin.redis_lock_error'), 500);
        } finally {
            $this->freed($key);
        }

        return true;
    }

    /**
     * 检查锁
     * @param string $key
     * @return bool
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function check(string $key): bool
    {
        return redis()->exists($this->getKey($key));
    }

    /**
     * 添加锁
     * @param string $key
     * @param int $expired
     * @param int $timeout
     * @param float $sleep
     * @return bool
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function lock(string $key, int $expired, int $timeout = 0, float $sleep = 0.1): bool
    {
        $retry = $timeout > 0 ? intdiv($timeout * 100, 10) : 1;

        $name = $this->getKey($key);

        while ($retry > 0) {

            $lock = redis()->set($name, 1, ['nx', 'ex' => $expired]);
            if ($lock || $timeout === 0) {
                break;
            }
            Coroutine::id() ? Coroutine::sleep($sleep) : usleep(9999999);

            $retry--;
        }

        return true;
    }

    /**
     * 释放锁
     * @param string $key
     * @return bool
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function freed(string $key): bool
    {
        $luaScript = <<<Lua
            if redis.call("GET", KEYS[1]) == ARGV[1] then
                return redis.call("DEL", KEYS[1])
            else
                return 0
            end
        Lua;

        return redis()->eval($luaScript, [$this->getKey($key), 1], 1) > 0;
    }

}