加密策略

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了加密策略相关的知识,希望对你有一定的参考价值。

一、前言

  这两天研究了一下项目中的密码加密,可以说得上是学到了很多。下面来大致说一下。

二、常用加密

  1.单向加密算法

  单向加密算法主要用来验证数据传输的过程中,是否被篡改过。

  • BASE64 严格地说,属于编码格式,而非加密算法

  • MD5(Message Digest algorithm 5,信息摘要算法)

  • SHA(Secure Hash Algorithm,安全散列算法)

  • HMAC(Hash Message Authentication Code,散列消息鉴别码

    2.对称和非对称加密算法
    对称和非对称加密算法主要采用公钥和私钥的形式,来对数据加密。
  • DES(Data Encryption Standard,数据加密算法)

  • PBE(Password-based encryption,基于密码验证)

  • RSA(算法的名字以发明者的名字命名:Ron Rivest, AdiShamir 和Leonard Adleman)

  • DH(Diffie-Hellman算法,密钥一致协议)

  • DSA(Digital Signature Algorithm,数字签名)

  • ECC(Elliptic Curves Cryptography,椭圆曲线密码编码学)

 

  参考:Java中的各种加密算法   聊一聊MD5   各种Java加密算法

三、项目中的加密策略

  创建用户:同一默认密码处理,用户登录时要修改密码。获取到默认密码之后先进行一次sha1加密,然后根据user code 和 salt进行第二次sha1加密存入数据库。

  校验用户:用户登录时,js处理密码。首先进行一次sha1加密,然后进行一次rsa加密(需要向后台请求ras公钥的modulus 和 exponent)。后台通过rsa公钥对应的私钥进行解密,然后根据user code进行找到对应的用户,通过user code 和 salt进行sha1加密,将得到的结果和数据库中的进行对比。

四、代码

  js sha1算法加密(sha1.js)

\'use strict\';
(function (E) {
    function t(c, a, e) {
        var g = 0, b = [], d = 0, f, k, l, h, m, w, n, q = !1, r = !1, p = [], t = [], v, u = !1;
        e = e || {};
        f = e.encoding || "UTF8";
        v = e.numRounds || 1;
        l = y(a, f);
        if (v !== parseInt(v, 10) || 1 > v)throw Error("numRounds must a integer >= 1");
        if ("SHA-1" === c)m = 512, w = z, n = F, h = 160; else throw Error("Chosen SHA variant is not supported");
        k = x(c);
        this.setHMACKey = function (a, b, d) {
            var e;
            if (!0 === r)throw Error("HMAC key already set");
            if (!0 === q)throw Error("Cannot set HMAC key after finalizing hash");
            if (!0 === u)throw Error("Cannot set HMAC key after calling update");
            f = (d || {}).encoding || "UTF8";
            b = y(b, f)(a);
            a = b.binLen;
            b = b.value;
            e = m >>> 3;
            d = e / 4 - 1;
            if (e < a / 8) {
                for (b = n(b, a, 0, x(c)); b.length <= d;)b.push(0);
                b[d] &= 4294967040
            } else if (e > a / 8) {
                for (; b.length <= d;)b.push(0);
                b[d] &= 4294967040
            }
            for (a = 0; a <= d; a += 1)p[a] = b[a] ^ 909522486, t[a] = b[a] ^ 1549556828;
            k = w(p, k);
            g = m;
            r = !0
        };
        this.update = function (a) {
            var c, e, f, h = 0, n = m >>> 5;
            c = l(a, b, d);
            a = c.binLen;
            e = c.value;
            c = a >>> 5;
            for (f = 0; f < c; f += n)h + m <= a && (k = w(e.slice(f, f + n), k), h += m);
            g += h;
            b = e.slice(h >>> 5);
            d = a % m;
            u = !0
        };
        this.getHash = function (a, e) {
            var f, l, m;
            if (!0 ===
                r)throw Error("Cannot call getHash after setting HMAC key");
            m = A(e);
            switch (a) {
                case "HEX":
                    f = function (a) {
                        return B(a, m)
                    };
                    break;
                case "B64":
                    f = function (a) {
                        return C(a, m)
                    };
                    break;
                case "BYTES":
                    f = D;
                    break;
                default:
                    throw Error("format must be HEX, B64, or BYTES");
            }
            if (!1 === q)for (k = n(b, d, g, k), l = 1; l < v; l += 1)k = n(k, h, 0, x(c));
            q = !0;
            return f(k)
        };
        this.getHMAC = function (a, e) {
            var f, l, p;
            if (!1 === r)throw Error("Cannot call getHMAC without first setting HMAC key");
            p = A(e);
            switch (a) {
                case "HEX":
                    f = function (a) {
                        return B(a, p)
                    };
                    break;
                case "B64":
                    f =
                        function (a) {
                            return C(a, p)
                        };
                    break;
                case "BYTES":
                    f = D;
                    break;
                default:
                    throw Error("outputFormat must be HEX, B64, or BYTES");
            }
            !1 === q && (l = n(b, d, g, k), k = w(t, x(c)), k = n(l, h, m, k));
            q = !0;
            return f(k)
        }
    }

    function G(c, a, e) {
        var g = c.length, b, d, f, k, l;
        a = a || [0];
        e = e || 0;
        l = e >>> 3;
        if (0 !== g % 2)throw Error("String of HEX type must be in byte increments");
        for (b = 0; b < g; b += 2) {
            d = parseInt(c.substr(b, 2), 16);
            if (isNaN(d))throw Error("String of HEX type contains invalid characters");
            k = (b >>> 1) + l;
            for (f = k >>> 2; a.length <= f;)a.push(0);
            a[f] |= d <<
                8 * (3 - k % 4)
        }
        return {value: a, binLen: 4 * g + e}
    }

    function H(c, a, e) {
        var g = [], b, d, f, k, g = a || [0];
        e = e || 0;
        d = e >>> 3;
        for (b = 0; b < c.length; b += 1)a = c.charCodeAt(b), k = b + d, f = k >>> 2, g.length <= f && g.push(0), g[f] |= a << 8 * (3 - k % 4);
        return {value: g, binLen: 8 * c.length + e}
    }

    function I(c, a, e) {
        var g = [], b = 0, d, f, k, l, h, m, g = a || [0];
        e = e || 0;
        a = e >>> 3;
        if (-1 === c.search(/^[a-zA-Z0-9=+\\/]+$/))throw Error("Invalid character in base-64 string");
        f = c.indexOf("=");
        c = c.replace(/\\=/g, "");
        if (-1 !== f && f < c.length)throw Error("Invalid \'=\' found in base-64 string");
        for (f = 0; f < c.length; f += 4) {
            h = c.substr(f, 4);
            for (k = l = 0; k < h.length; k += 1)d = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(h[k]), l |= d << 18 - 6 * k;
            for (k = 0; k < h.length - 1; k += 1) {
                m = b + a;
                for (d = m >>> 2; g.length <= d;)g.push(0);
                g[d] |= (l >>> 16 - 8 * k & 255) << 8 * (3 - m % 4);
                b += 1
            }
        }
        return {value: g, binLen: 8 * b + e}
    }

    function B(c, a) {
        var e = "", g = 4 * c.length, b, d;
        for (b = 0; b < g; b += 1)d = c[b >>> 2] >>> 8 * (3 - b % 4), e += "0123456789abcdef".charAt(d >>> 4 & 15) + "0123456789abcdef".charAt(d & 15);
        return a.outputUpper ? e.toUpperCase() : e
    }

    function C(c,
               a) {
        var e = "", g = 4 * c.length, b, d, f;
        for (b = 0; b < g; b += 3)for (f = b + 1 >>> 2, d = c.length <= f ? 0 : c[f], f = b + 2 >>> 2, f = c.length <= f ? 0 : c[f], f = (c[b >>> 2] >>> 8 * (3 - b % 4) & 255) << 16 | (d >>> 8 * (3 - (b + 1) % 4) & 255) << 8 | f >>> 8 * (3 - (b + 2) % 4) & 255, d = 0; 4 > d; d += 1)8 * b + 6 * d <= 32 * c.length ? e += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(f >>> 6 * (3 - d) & 63) : e += a.b64Pad;
        return e
    }

    function D(c) {
        var a = "", e = 4 * c.length, g, b;
        for (g = 0; g < e; g += 1)b = c[g >>> 2] >>> 8 * (3 - g % 4) & 255, a += String.fromCharCode(b);
        return a
    }

    function A(c) {
        var a = {outputUpper: !1, b64Pad: "="};
        c = c || {};
        a.outputUpper = c.outputUpper || !1;
        a.b64Pad = c.b64Pad || "=";
        if ("boolean" !== typeof a.outputUpper)throw Error("Invalid outputUpper formatting option");
        if ("string" !== typeof a.b64Pad)throw Error("Invalid b64Pad formatting option");
        return a
    }

    function y(c, a) {
        var e;
        switch (a) {
            case "UTF8":
            case "UTF16BE":
            case "UTF16LE":
                break;
            default:
                throw Error("encoding must be UTF8, UTF16BE, or UTF16LE");
        }
        switch (c) {
            case "HEX":
                e = G;
                break;
            case "TEXT":
                e = function (e, b, d) {
                    var f = [], c = [], l = 0, h, m, p, n, q, f = b || [0];
                    b = d || 0;
                    p = b >>> 3;
                    if ("UTF8" ===
                        a)for (h = 0; h < e.length; h += 1)for (d = e.charCodeAt(h), c = [], 128 > d ? c.push(d) : 2048 > d ? (c.push(192 | d >>> 6), c.push(128 | d & 63)) : 55296 > d || 57344 <= d ? c.push(224 | d >>> 12, 128 | d >>> 6 & 63, 128 | d & 63) : (h += 1, d = 65536 + ((d & 1023) << 10 | e.charCodeAt(h) & 1023), c.push(240 | d >>> 18, 128 | d >>> 12 & 63, 128 | d >>> 6 & 63, 128 | d & 63)), m = 0; m < c.length; m += 1) {
                        q = l + p;
                        for (n = q >>> 2; f.length <= n;)f.push(0);
                        f[n] |= c[m] << 8 * (3 - q % 4);
                        l += 1
                    } else if ("UTF16BE" === a || "UTF16LE" === a)for (h = 0; h < e.length; h += 1) {
                        d = e.charCodeAt(h);
                        "UTF16LE" === a && (m = d & 255, d = m << 8 | d >>> 8);
                        q = l + p;
                        for (n = q >>>
                            2; f.length <= n;)f.push(0);
                        f[n] |= d << 8 * (2 - q % 4);
                        l += 2
                    }
                    return {value: f, binLen: 8 * l + b}
                };
                break;
            case "B64":
                e = I;
                break;
            case "BYTES":
                e = H;
                break;
            default:
                throw Error("format must be HEX, TEXT, B64, or BYTES");
        }
        return e
    }

    function r(c, a) {
        return c << a | c >>> 32 - a
    }

    function p(c, a) {
        var e = (c & 65535) + (a & 65535);
        return ((c >>> 16) + (a >>> 16) + (e >>> 16) & 65535) << 16 | e & 65535
    }

    function u(c, a, e, g, b) {
        var d = (c & 65535) + (a & 65535) + (e & 65535) + (g & 65535) + (b & 65535);
        return ((c >>> 16) + (a >>> 16) + (e >>> 16) + (g >>> 16) + (b >>> 16) + (d >>> 16) & 65535) << 16 | d & 65535
    }

    function x(c) {
        if ("SHA-1" ===
            c)c = [1732584193, 4023233417, 2562383102, 271733878, 3285377520]; else throw Error("No SHA variants supported");
        return c
    }

    function z(c, a) {
        var e = [], g, b, d, f, k, l, h;
        g = a[0];
        b = a[1];
        d = a[2];
        f = a[3];
        k = a[4];
        for (h = 0; 80 > h; h += 1)e[h] = 16 > h ? c[h] : r(e[h - 3] ^ e[h - 8] ^ e[h - 14] ^ e[h - 16], 1), l = 20 > h ? u(r(g, 5), b & d ^ ~b & f, k, 1518500249, e[h]) : 40 > h ? u(r(g, 5), b ^ d ^ f, k, 1859775393, e[h]) : 60 > h ? u(r(g, 5), b & d ^ b & f ^ d & f, k, 2400959708, e[h]) : u(r(g, 5), b ^ d ^ f, k, 3395469782, e[h]), k = f, f = d, d = r(b, 30), b = g, g = l;
        a[0] = p(g, a[0]);
        a[1] = p(b, a[1]);
        a[2] = p(d, a[2]);
        a[3] = p(f, a[3]);
        a[4] = p(k, a[4]);
        return a
    }

    function F(c, a, e, g) {
        var b;
        for (b = (a + 65 >>> 9 << 4) + 15; c.length <= b;)c.push(0);
        c[a >>> 5] |= 128 << 24 - a % 32;
        c[b] = a + e;
        e = c.length;
        for (a = 0; a < e; a += 16)g = z(c.slice(a, a + 16), g);
        return g
    }

    "function" === typeof define && define.amd ? define(function () {
        return t
    }) : "undefined" !== typeof exports ? "undefined" !== typeof module && module.exports ? module.exports = exports = t : exports = t : E.jsSHA = t
})(this);
View Code

  encrypt.js

var sha1 = require (\'xxx/sha1.js\');

function _encrypt(jsSHA) {
    var encrypt = function(string){
        var shaObj = new jsSHA("SHA-1", "TEXT");
        shaObj.update(string);
        var hash = shaObj.getHash("HEX");
        return hash;
    }
    return encrypt;
}

const encrypt = _encrypt(sha1)

export default encrypt;

  js rsa 算法加密(security.js)

var RSAUtils = {};

var biRadixBase = 2;
var biRadixBits = 16;
var bitsPerDigit = biRadixBits;
var biRadix = 1 << 16; // = 2^16 = 65536
var biHalfRadix = biRadix >>> 1;
var biRadixSquared = biRadix * biRadix;
var maxDigitVal = biRadix - 1;
var maxInteger = 9999999999999998;

//maxDigits:
//Change this to accommodate your largest number size. Use setMaxDigits()
//to change it!
//
//In general, if you\'re working with numbers of size N bits, you\'ll need 2*N
//bits of storage. Each digit holds 16 bits. So, a 1024-bit key will need
//
//1024 * 2 / 16 = 128 digits of storage.
//
var maxDigits;
var ZERO_ARRAY;
var bigZero, bigOne;

var BigInt = window.BigInt = function(flag) {
    if (typeof flag == "boolean" && flag == true) {
        this.digits = null;
    } else {
        this.digits = ZERO_ARRAY.slice(0);
    }
    this.isNeg = false;
};

RSAUtils.setMaxDigits = function(value) {
    maxDigits = value;
    ZERO_ARRAY = new Array(maxDigits);
    for (var iza = 0; iza < ZERO_ARRAY.length; iza++) ZERO_ARRAY[iza] = 0;
    bigZero = new BigInt();
    bigOne = new BigInt();
    bigOne.digits[0] = 1;
};
RSAUtils.setMaxDigits(20);

//The maximum number of digits in base 10 you can convert to an
//integer without javascript throwing up on you.
var dpl10 = 15;

RSAUtils.biFromNumber = function(i) {
    var result = new BigInt();
    result.isNeg = i < 0;
    i = Math.abs(i);
    var j = 0;
    while (i > 0) {
        result.digits[j++] = i & maxDigitVal;
        i = Math.floor(i / biRadix);
    }
    return result;
};

//lr10 = 10 ^ dpl10
var lr10 = RSAUtils.biFromNumber(1000000000000000);

RSAUtils.biFromDecimal = function(s) {
    var isNeg = s.charAt(0) == \'-\';
    var i = isNeg ? 1 : 0;
    var result;
    // Skip leading zeros.
    while (i < s.length && s.charAt(i) == \'0\') ++i;
    if (i == s.length) {
        result = new BigInt();
    }
    else {
        var digitCount = s.length - i;
        var fgl = digitCount % dpl10;
        if (fgl == 0) fgl = dpl10;
        result = RSAUtils.biFromNumber(Number(s.substr(i, fgl)));
        i += fgl;
        while (i < s.length) {
            result = RSAUtils.biAdd(RSAUtils.biMultiply(result, lr10),
                    RSAUtils.biFromNumber(Number(s.substr(i, dpl10))));
            i += dpl10;
        }
        result.isNeg = isNeg;
    }
    return result;
};

RSAUtils.biCopy = function(bi) {
    var result = new BigInt(true);
    result.digits = bi.digits.slice(0);
    result.isNeg = bi.isNeg;
    return result;
};

RSAUtils.reverseStr = function(s) {
    var result = "";
    for (var i = s.length - 1; i > -1; --i) {
        result += s.charAt(i);
    }
    return result;
};

var hexatrigesimalToChar = [
    \'0\', \'1\', \'2\', \'3\', \'4\', \'5\', \'6\', \'7\', \'8\', \'9\',
    \'a\', \'b\', \'c\', \'d\', \'e\', \'f\', \'g\', \'h\', \'i\', \'j\',
    \'k\', \'l\', \'m\', \'n\', \'o\', \'p\', \'q\', \'r\', \'s\', \'t\',
    \'u\', \'v\', \'w\', \'x\', \'y\', \'z\'
];

RSAUtils.biToString = function(x, radix) { // 2 <= radix <= 36
    var b = new BigInt();
    b.digits[0] = radix;
    var qr = RSAUtils.biDivideModulo(x, b);
    var result = hexatrigesimalToChar[qr[1].digits[0]];
    while (RSAUtils.biCompare(qr[0], bigZero) == 1) {
        qr = RSAUtils.biDivideModulo(qr[0], b);
        digit = qr[1].digits[0];
        result += hexatrigesimalToChar[qr[1].digits[0]];
    }
    return (x.isNeg ? "-" : "") + RSAUtils.reverseStr(result);
};

RSAUtils.biToDecimal = function(x) {
    var b = new BigInt();
    b.digits[0] = 10;
    var qr = RSAUtils.biDivideModulo(x, b);
    var result = String(qr[1].digits[0]);
    while (RSAUtils.biCompare(qr[0], bigZero) == 1) {
        qr = RSAUtils.biDivideModulo(qr[0], b);
        result += String(qr[1].digits[0]);
    }
    return (x.isNeg ? "-" : "") + RSAUtils.reverseStr(result);
};

var hexToChar = [\'0\', \'1\', \'2\', \'3\', \'4\', \'5\', \'6\', \'7\', \'8\', \'9\',
        \'a\', \'b\', \'c\', \'d\', \'e\', \'f\'];

RSAUtils.digitToHex = function(n) {
    var mask = 0xf;
    var result = "";
    for (var i = 0; i < 4; ++i) {
        result += hexToChar[n & mask];
        n >>>= 4;
    }
    return RSAUtils.reverseStr(result);
};

RSAUtils.biToHex = function(x) {
    var result = "";
    var n = RSAUtils.biHighIndex(x);
    for (var i = RSAUtils.biHighIndex(x); i > -1; --i) {
        result += RSAUtils.digitToHex(x.digits[i]);
    }
    return result;
};

RSAUtils.charToHex = function(c) {
    var ZERO = 48;
    var NINE = ZERO + 9;
    var littleA = 97;
    var littleZ = littleA + 25;
    var bigA = 65;
    var bigZ = 65 + 25;
    var result;

    if (c >= ZERO && c <= NINE) {
        result = c - ZERO;
    } else if (c >= bigA && c <= bigZ) {
        result = 10 + c - bigA;
    } else if (c >= littleA && c <= littleZ) {
        result = 10 + c - littleA;
    } else {
        result = 0;
    }
    return result;
};

RSAUtils.hexToDigit = function(s) {
    var result = 0;
    var sl = Math.min(s.length, 4);
    for (var i = 0; i < sl; ++i) {
        result <<= 4;
        result |= RSAUtils.charToHex(s.charCodeAt(i));
    }
    return result;
};

RSAUtils.biFromHex = function(s) {
    var result = new BigInt();
    var sl = s.length;
    for (var i = sl, j = 0; i > 0; i -= 4, ++j) {
        result.digits[j] = RSAUtils.hexToDigit(s.substr(Math.max(i - 4, 0), Math.min(i, 4)));
    }
    return result;
};

RSAUtils.biFromString = function(s, radix) {
    var isNeg = s.charAt(0) == \'-\';
    var istop = isNeg ? 1 : 0;
    var result = new BigInt();
    var place = new BigInt();
    place.digits[0] = 1; // radix^0
    for (var i = s.length - 1; i >= istop; i--) {
        var c = s.charCodeAt(i);
        var digit = RSAUtils.charToHex(c);
        var biDigit = RSAUtils.biMultiplyDigit(place, digit);
        result = RSAUtils.biAdd(result, biDigit);
        place = RSAUtils.biMultiplyDigit(place, radix);
    }
    result.isNeg = isNeg;
    return result;
};

RSAUtils.biDump = function(b) {
    return (b.isNeg ? "-" : "") + b.digits.join(" ");
};

RSAUtils.biAdd = function(x, y) {
    var result;

    if (x.isNeg != y.isNeg) {
        y.isNeg = !y.isNeg;
        result = RSAUtils.biSubtract(x, y);
        y.isNeg = !y.isNeg;
    }
    else {
        result = new BigInt();
        var c = 0;
        var n;
        for (var i = 0; i < x.digits.length; ++i) {
            n = x.digits[i] + y.digits[i] + c;
            result.digits[i] = n % biRadix;
            c = Number(n >= biRadix);
        }
        result.isNeg = x.isNeg;
    }
    return result;
};

RSAUtils.biSubtract = function(x, y) {
    var result;
    if (x.isNeg != y.isNeg) {
        y.isNeg = !y.isNeg;
        result = RSAUtils.biAdd(x, y);
        y.isNeg = !y.isNeg;
    } else {
        result = new BigInt();
        var n, c;
        c = 0;
        for (var i = 0; i < x.digits.length; ++i) {
            n = x.digits[i] - y.digits[i] + c;
            result.digits[i] = n % biRadix;
            // Stupid non-conforming modulus operation.
            if (result.digits[i] < 0) result.digits[i] += biRadix;
            c = 0 - Number(n < 0);
        }
        // Fix up the negative sign, if any.
        if (c == -1) {
            c = 0;
            for (var i = 0; i < x.digits.length; ++i) {
                n = 0 - result.digits[i] + c;
                result.digits[i] = n % biRadix;
                // Stupid non-conforming modulus operation.
                if (result.digits[i] < 0) result.digits[i] += biRadix;
                c = 0 - Number(n < 0);
            }
            // Result is opposite sign of arguments.
            result.isNeg = !x.isNeg;
        } else {
            // Result is same sign.
            result.isNeg = x.isNeg;
        }
    }
    return result;
};

RSAUtils.biHighIndex = function(x) {
    var result = x.digits.length - 1;
    while (result > 0 && x.digits[result] == 0) --result;
    return result;
};

RSAUtils.biNumBits = function(x) {
    var n = RSAUtils.biHighIndex(x);
    var d = x.digits[n];
    var m = (n + 1) * bitsPerDigit;
    var result;
    for (result = m; result > m - bitsPerDigit; --result) {
        if ((d & 0x8000) != 0) break;
        d <<= 1;
    }
    return result;
};

RSAUtils.biMultiply = function(x, y) {
    var result = new BigInt();
    var c;
    var n = RSAUtils.biHighIndex(x);
    var t = RSAUtils.biHighIndex(y);
    var uv, k;

    for (var i = 0; i <= t; ++i) {
        c = 0;
        k = i;
        for (var j = 0; j <= n; ++j, ++k) {
            uv = result.digits[k] + x.digits[j] * y.digits[i] + c;
            result.digits[k] = uv & maxDigitVal;
            c = uv >>> biRadixBits;
            //c = Math.floor(uv / biRadix);
        }
        result.digits[i + n + 1] = c;
    }
    // Someone give me a logical xor, please.
    result.isNeg = x.isNeg != y.isNeg;
    return result;
};

RSAUtils.biMultiplyDigit = function(x, y) {
    var n, c, uv, result;

    result = new BigInt();
    n = RSAUtils.biHighIndex(x);
    c = 0;
    for (var j = 0; j <= n; ++j) {
        uv = result.digits[j] + x.digits[j] * y + c;
        result.digits[j] = uv & maxDigitVal;
        c = uv >>> biRadixBits;
        //c = Math.floor(uv / biRadix);
    }
    result.digits[1 + n] = c;
    return result;
};

RSAUtils.arrayCopy = 可以解密加密数据的片段吗?

如何使用预设策略加密 Amazon CloudFront 签名以进行私有内容访问

Java设计模式之策略模式(Strategy)

论如何设计一款端对端加密通讯软件

JSPatch 部署安全策略

加密策略