PHP 和 Web 端对称加密传输|JSEncrypt|CryptoJS
38

前后端对称加密传输

JSEncrypt

Git地址:https://github.com/travist/jsencrypt

首先这个包 需要提前生成 公钥 私钥 和 依赖 openssl

注意:

​ 明文长度最大为公钥长度-11,假如我的公钥长度是128,那明文最长也就117

​ 如果需要加密解密长文本,请看下一种前后端对称加解密方式

​ 这个库是与openssl一起工作。

如何使用这个库:

  • 在您的终端(基于Unix的操作系统)中输入以下内容。
openssl genrsa -out rsa_1024_priv.pem 1024
  • 这会生成一个私钥,您可以通过执行以下操作来看到...
cat rsa_1024_priv.pem
  • 然后,您可以将其复制并粘贴到index.html中的私钥部分。
  • 接下来,您可以通过执行以下命令获取公钥。
openssl rsa -pubout -in rsa_1024_priv.pem -out rsa_1024_pub.pem
  • 您可以通过键入...来查看公钥
cat rsa_1024_pub.pem

前端加密解密的写法如下:

<script src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
<script src="bin/jsencrypt.min.js"></script>
<script type="text/javascript">
$(function () {
    //被加密的信息
    var data = 'my message';

    //公钥
    var pub_key = '-----BEGIN PUBLIC KEY-----\n' +
        'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlOJu6TyygqxfWT7eLtGDwajtN\n' +
        'FOb9I5XRb6khyfD1Yt3YiCgQWMNW649887VGJiGr/L5i2osbl8C9+WJTeucF+S76\n' +
        'xFxdU6jE0NQ+Z+zEdhUTooNRaY5nZiu5PgDB0ED/ZKBUSLKL7eibMxZtMlUDHjm4\n' +
        'gwQco1KRMDSmXSMkDwIDAQAB\n' +
        '-----END PUBLIC KEY-----';

    //私钥
    var private_key = '-----BEGIN RSA PRIVATE KEY-----\n' +
        'MIICXQIBAAKBgQDlOJu6TyygqxfWT7eLtGDwajtNFOb9I5XRb6khyfD1Yt3YiCgQ\n' +
        'WMNW649887VGJiGr/L5i2osbl8C9+WJTeucF+S76xFxdU6jE0NQ+Z+zEdhUTooNR\n' +
        'aY5nZiu5PgDB0ED/ZKBUSLKL7eibMxZtMlUDHjm4gwQco1KRMDSmXSMkDwIDAQAB\n' +
        'AoGAfY9LpnuWK5Bs50UVep5c93SJdUi82u7yMx4iHFMc/Z2hfenfYEzu+57fI4fv\n' +
        'xTQ//5DbzRR/XKb8ulNv6+CHyPF31xk7YOBfkGI8qjLoq06V+FyBfDSwL8KbLyeH\n' +
        'm7KUZnLNQbk8yGLzB3iYKkRHlmUanQGaNMIJziWOkN+N9dECQQD0ONYRNZeuM8zd\n' +
        '8XJTSdcIX4a3gy3GGCJxOzv16XHxD03GW6UNLmfPwenKu+cdrQeaqEixrCejXdAF\n' +
        'z/7+BSMpAkEA8EaSOeP5Xr3ZrbiKzi6TGMwHMvC7HdJxaBJbVRfApFrE0/mPwmP5\n' +
        'rN7QwjrMY+0+AbXcm8mRQyQ1+IGEembsdwJBAN6az8Rv7QnD/YBvi52POIlRSSIM\n' +
        'V7SwWvSK4WSMnGb1ZBbhgdg57DXaspcwHsFV7hByQ5BvMtIduHcT14ECfcECQATe\n' +
        'aTgjFnqE/lQ22Rk0eGaYO80cc643BXVGafNfd9fcvwBMnk0iGX0XRsOozVt5Azil\n' +
        'psLBYuApa66NcVHJpCECQQDTjI2AQhFc1yRnCU/YgDnSpJVm1nASoRUnU8Jfm3Oz\n' +
        'uku7JUXcVpt08DFSceCEX9unCuMcT72rAQlLpdZir876\n' +
        '-----END RSA PRIVATE KEY-----';

    //new JSEncrypt
    var js_encrypt = new JSEncrypt();
    //初始化公钥
    js_encrypt.setPublicKey(pub_key);
    //初始化私钥
    js_encrypt.setPrivateKey(private_key);

    //通过 公钥 加密
    var encrypted = js_encrypt.encrypt(data);
    console.log(encrypted);

    //通过 私钥 解密
    var uncrypted = js_encrypt.decrypt(encrypted);
    console.log(uncrypted);
});
</script>

对应PHP后台加密解密的写法如下:

class Encrypt
{
    const JS_ENCRYPT = '-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlOJu6TyygqxfWT7eLtGDwajtN
FOb9I5XRb6khyfD1Yt3YiCgQWMNW649887VGJiGr/L5i2osbl8C9+WJTeucF+S76
xFxdU6jE0NQ+Z+zEdhUTooNRaY5nZiu5PgDB0ED/ZKBUSLKL7eibMxZtMlUDHjm4
gwQco1KRMDSmXSMkDwIDAQAB
-----END PUBLIC KEY-----';

    const JS_DECRYPT = '-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDlOJu6TyygqxfWT7eLtGDwajtNFOb9I5XRb6khyfD1Yt3YiCgQ
WMNW649887VGJiGr/L5i2osbl8C9+WJTeucF+S76xFxdU6jE0NQ+Z+zEdhUTooNR
aY5nZiu5PgDB0ED/ZKBUSLKL7eibMxZtMlUDHjm4gwQco1KRMDSmXSMkDwIDAQAB
AoGAfY9LpnuWK5Bs50UVep5c93SJdUi82u7yMx4iHFMc/Z2hfenfYEzu+57fI4fv
xTQ//5DbzRR/XKb8ulNv6+CHyPF31xk7YOBfkGI8qjLoq06V+FyBfDSwL8KbLyeH
m7KUZnLNQbk8yGLzB3iYKkRHlmUanQGaNMIJziWOkN+N9dECQQD0ONYRNZeuM8zd
8XJTSdcIX4a3gy3GGCJxOzv16XHxD03GW6UNLmfPwenKu+cdrQeaqEixrCejXdAF
z/7+BSMpAkEA8EaSOeP5Xr3ZrbiKzi6TGMwHMvC7HdJxaBJbVRfApFrE0/mPwmP5
rN7QwjrMY+0+AbXcm8mRQyQ1+IGEembsdwJBAN6az8Rv7QnD/YBvi52POIlRSSIM
V7SwWvSK4WSMnGb1ZBbhgdg57DXaspcwHsFV7hByQ5BvMtIduHcT14ECfcECQATe
aTgjFnqE/lQ22Rk0eGaYO80cc643BXVGafNfd9fcvwBMnk0iGX0XRsOozVt5Azil
psLBYuApa66NcVHJpCECQQDTjI2AQhFc1yRnCU/YgDnSpJVm1nASoRUnU8Jfm3Oz
uku7JUXcVpt08DFSceCEX9unCuMcT72rAQlLpdZir876
-----END RSA PRIVATE KEY-----';

    /**
     * 解密
     *
     * @param string $encryptString
     *
     * @return bool|string
     */
    public static function privateDecrypt($encryptString = '')
    {
        $decrypted  = '';
        $privateKey = self::JS_DECRYPT;
        openssl_private_decrypt(base64_decode($encryptString), $decrypted, $privateKey);

        return $decrypted;
    }

    /**
     * 加密
     *
     * @param string $data
     *
     * @return bool|string
     */
    public static function publicEncrypt($data = '')
    {
        $encrypt_data = '';
        openssl_public_encrypt($data, $encrypt_data, self::JS_ENCRYPT);
        $encrypt_data = base64_encode($encrypt_data);

        return $encrypt_data;
    }
}

如果出现了以下情况
opensll_public_encrypt() :key parameter is not a valid public key in /www/xxxxxx.php on line 123

class Encrypt
{
    public function publicEncrypt()
    {
        $password  = "habx-2018";
        $key       = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlOJu6TyygqxfWT7eLtGDwajtNFOb9I5XRb6khyfD1Yt3YiCgQWMNW649887VGJiGr/L5i2osbl8C9+WJTeucF+S76xFxdU6jE0NQ+Z+zEdhUTooNRaY5nZiu5PgDB0ED/ZKBUSLKL7eibMxZtMlUDHjm4gwQco1KRMDSmXSMkDwIDAQAB";
        $key_eol   = (string) implode("\n", str_split((string) $key, 64));
        $publicKey = (string) "-----BEGIN PUBLIC KEY-----\n" . $key_eol . "\n-----END PUBLIC KEY-----";

        openssl_public_encrypt($password, $encryptData, $publicKey, OPENSSL_PKCS1_PADDING);
        $encryptData = base64_encode($encryptData);

        print_r("encryptData=============" . $encryptData);

        return $encryptData;
    }
}

(new Encrypt())->publicEncrypt();

使用

$key_eol   = (string) implode("\n", str_split((string) $key, 64));
$publicKey = (string) "-----BEGIN PUBLIC KEY-----\n" . $key_eol . "\n-----END PUBLIC KEY-----";

把KEY按照64位增加一个 \n的方式处理


CryptoJS

Git地址:https://github.com/brix/crypto-js

文档地址:https://code.google.com/archive/p/crypto-js/

注意:

​ 明文长度没有限制但是加密的文本也会随之变长

​ 这个库也是与openssl一起工作。

前端加密解密的写法如下:

<script src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
<script src="http://cdn.bootcss.com/crypto-js/3.1.9/crypto-js.js"></script>
<script>
const AES_KEY = "qq3217834abcdefg"; //16位
const AES_IV = "1234567890123456";  //16位

function aes_encrypt(plainText) {
    var encrypted = CryptoJS.AES.encrypt(plainText, CryptoJS.enc.Utf8.parse(AES_KEY), {iv:  CryptoJS.enc.Utf8.parse(AES_IV)});
    return CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
}

function aes_decrypt(ciphertext) {
    var decrypted = CryptoJS.AES.decrypt(ciphertext, CryptoJS.enc.Utf8.parse(AES_KEY), {iv: CryptoJS.enc.Utf8.parse(AES_IV)});
    return decrypted.toString(CryptoJS.enc.Utf8);
}

data = 'my message';
encrypt_data = aes_encrypt(data);
console.log(encrypt_data);
decrypt_data = aes_decrypt(encrypt_data);
console.log(decrypt_data);
</script>

对应PHP后台加密解密的写法如下:

class AesEncrypt
{
    const AES_KEY = "qq3217834abcdefg"; //16位
    const AES_IV  = "1234567890123456"; //16位

    public static function aes_decrypt($str)
    {
        $decrypted = openssl_decrypt(base64_decode($str), 'aes-128-cbc', self::AES_KEY, OPENSSL_RAW_DATA, self::AES_IV);

        return $decrypted;
    }

    public static function aes_encrypt($plain_text)
    {
        $encrypted_data = openssl_encrypt($plain_text, 'aes-128-cbc', self::AES_KEY, OPENSSL_RAW_DATA, self::AES_IV);

        return base64_encode($encrypted_data);
    }
}
本帖由系统于 9个月前 自动加精
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 16
leung0826

不错,但如何保证前端密钥安全不泄露呢?js 混淆?

9个月前
不忘初心

默默的给小超哥点个赞 @ overtrue

9个月前
不忘初心

@leung0826 这个我觉得只能防君子,防不了牛逼的小人了. 别人要是直接抓页面元素也是能把数据拿走的对吧

9个月前
不忘初心

@leung0826 而且如果用第二种加密方式,我们也可以每次分配key去做加解密的事情.但是我觉得也没有什么太大的作用.只能防止和前端交互接口的数据不能很容易的被拿出来.但是凡是暴露给前端用户的.有很多办法可以数据被拿走.比如用 phantomJs

9个月前
leung0826

@不忘初心 那倒是,没有绝对的安全,可以定期更换密钥,增加混淆的复杂度,应该可以防大部分

9个月前

其实第一种 demo 写的有歧义;应该是前端公钥加密,后端私钥解密;但是现在demo把公钥和私钥一起设置了,看起来就好像公钥和私钥都放在前端,所以加密好像没什么意义了。

这应该是一种比较科学的方式,即便公钥泄露也只能用于加密,攻击者无法解密请求中已被加密的数据。

9个月前
不忘初心

@Abel94 demo里面写着加密和解密的方式.可以根据需求而定如果前端加密后端解密那就可以不用把私钥暴露给前端啦

9个月前

前端公钥加密,后端私钥解密,这样就不怕js代码泄露

9个月前
不忘初心

@MarksGui 我觉得第二种可能应用场景会更多一些, CryptoJS 那种

9个月前
kylesean

第二种不错

9个月前

为啥我用jsencrypt加密后的数据 和php加密后的数据不一致呢 公钥是一致的

9个月前
不忘初心

@Flex 是哪里出现问题了呢? 要不你加我微信我帮你看一下

9个月前

@不忘初心 是key格式可能有点问题 导致PHP没解出来 已经解决了 我把登录数据用RSA加密传给后端解密再验证有效性

9个月前
不忘初心

@Flex 建议用 第二种 CryptoJS . 第一种的对长度有限制

9个月前

@不忘初心 账号密码不会超过127位呀 而且我也是分开加密的 AES加密 key和iv都暴露在前端 别人也是可以取的到的 RSA我只要暴露公钥就可以了 感觉相对来说安全很多

9个月前
不忘初心

JSEncrypt

如果出现了以下情况
opensll_public_encrypt() :key parameter is not a valid public key in /www/xxxxxx.php on line 123

class Encrypt
{
public function publicEncrypt()
{
$password = "habx-2018";
$key = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlOJu6TyygqxfWT7eLtGDwajtNFOb9I5XRb6khyfD1Yt3YiCgQWMNW649887VGJiGr/L5i2osbl8C9+WJTeucF+S76xFxdU6jE0NQ+Z+zEdhUTooNRaY5nZiu5PgDB0ED/ZKBUSLKL7eibMxZtMlUDHjm4gwQco1KRMDSmXSMkDwIDAQAB";
$key_eol = (string) implode("\n", str_split((string) $key, 64));
$publicKey = (string) "-----BEGIN PUBLIC KEY-----\n" . $key_eol . "\n-----END PUBLIC KEY-----";

    openssl_public_encrypt($password, $encryptData, $publicKey, OPENSSL_PKCS1_PADDING);
    $encryptData = base64_encode($encryptData);

    print_r("encryptData=============" . $encryptData);

    return $encryptData;
}

}

(new Encrypt())->publicEncrypt();
使用

$key_eol = (string) implode("\n", str_split((string) $key, 64));
$publicKey = (string) "-----BEGIN PUBLIC KEY-----\n" . $key_eol . "\n-----END PUBLIC KEY-----";
把KEY按照64位增加一个 \n的方式处理

5个月前

  • 请注意单词拼写,以及中英文排版,参考此页
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
  • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
  • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
  • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
  请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!