zoukankan      html  css  js  c++  java
  • jsencrypt代码分析——openssl的rsa加密解密在js的实现

    在js上做rsa,感觉jsencrypt这个是封装的比较好的,但用起来还是遇到了些坑,所以踩进代码里填填坑~

    项目在这里 https://github.com/travist/jsencrypt

    【rsa算法】

    首先科普一下rsa:公钥私钥成对,用其中一个加密只能用另一个解密,常用公钥加密私钥解密。

    一开始看到斯坦佛那个库,原始的算法实现:

    长度,建议至少1024。模数n(常取默认65537)两边都要用。

    指数e,和n一起就是公钥。指数d,和n一起就是私钥。质数p和q用于生成密钥对,然后就丢弃不公开。

    具体算法网上一大把,就不多说了。

    【ssl/ssh密钥对】

    用服务器上的工具生成的密钥对,格式一般是如下,有开始行结束行,内容用base64转码。这里涉及一些国际编码规范,代码分析时在逐一解释。

    -----BEGIN PUBLIC KEY-----
    MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIA4OVgB4FRq4l5zjEmd4r/jswRcHlZQ
    kg10p9rzG3VyXCPpa/ZkwOYy+kGq7a7BjAKTpic2cUNRim4m8HKTdc8CAwEAAQ==
    -----END PUBLIC KEY-----

    【代码分析】

    加载公钥:setPublicKey->setKey->JSEncryptRSAKey->parseKey

    RSAKey.prototype.parseKey = function (pem) {...}

    注释写得非常好!

    /**
    ...省略...
     *This method accepts public key * in the rsaencryption pkcs #1 format (oid: 1.2.840.113549.1.1.1). * The format is defined as: * PublicKeyInfo ::= SEQUENCE { * algorithm AlgorithmIdentifier, * PublicKey BIT STRING * } * Where AlgorithmIdentifier is: * AlgorithmIdentifier ::= SEQUENCE { * algorithm OBJECT IDENTIFIER, the OID of the enc algorithm * parameters ANY DEFINED BY algorithm OPTIONAL (NULL for PKCS #1) * } * and PublicKey is a SEQUENCE encapsulated in a BIT STRING * RSAPublicKey ::= SEQUENCE { * modulus INTEGER, -- n * publicExponent INTEGER -- e * }
    */

    代码如下:

     1 RSAKey.prototype.parseKey = function (pem) {
     2   try {
     3 ...
     4     var der = reHex.test(pem) ? Hex.decode(pem) : Base64.unarmor(pem);
     5     var asn1 = ASN1.decode(der);
     6 ...
     7     if (asn1.sub.length === 9) {
     8       // Parse the private key.
     9     }
    10     else if (asn1.sub.length === 2) {
    11       // Parse the public key.
    12       var bit_string = asn1.sub[1];
    13       var sequence = bit_string.sub[0];
    14       modulus = sequence.sub[0].getHexStringValue();
    15       this.n = parseBigInt(modulus, 16);
    16       public_exponent = sequence.sub[1].getHexStringValue();
    17       this.e = parseInt(public_exponent, 16);
    18     }
    19     else {
    20       return false;
    21     }
    22     return true;
    23   }
    24   catch (ex) {
    25     return false;
    26   }
    27 };

    这里需要了解一下各种编码格式。hex和base64就不解释了。

    ASN.1抽象语法标记,我的理解就是对数据进行结构化解析的规范:一个标准的ASN.1编码对象有四个域:对象标识域、数据长度域、数据域以及结束标志(可选,在长度不可知情况下需要,openssl中没有该标志)。

    DER则是具体的编码实现。 http://baike.baidu.com/view/100318.htm#4

    PKCS#1则是RSA中最基础的算法定义和密钥规定,讲人话就是定义了:公钥是元组(n,e),算法是n=q*p等一系列公式。https://en.wikipedia.org/wiki/PKCS1

    这段代码两处亮点,一个是else if (asn1.sub.length === 2)判断公钥(没深究,反正靠子节点数判断);
    两一个是var bit_string = asn1.sub[1];即前面有一段asn1.sub[0]是算法标识。

    这里本身没有坑,但是生成公钥时如果命令不对应是会踩坑的(有一种命令是生成无算法标记的公钥)~~~

    加密:encrypt->RSAEncrypt->pkcs1pad2 | doPublic->RSADoPublic

    pkcs1pad2是做补位处理:

    入参 var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3); 是(模位数+7)/8得到模的字节长度?

    然后是一系列的移位操作,此处的作用是填入随机位使得每次加密的密文都不一样。http://blog.chinaunix.net/uid-21880738-id-1813144.html

    RSADoPublic最终执行加密算法x^e (mod n)

    至此,主要的算法、规范和坑基本上覆盖了,私钥和解密坑比较少,就不多说了。

    最后,附送一个千年大坑,跨语言跨类库的时候不小心可能会遇到的 http://blog.chinaunix.net/uid-23069658-id-4282969.html

    于这个js类库而言,-pubout出来的是可用的,而-RSAPublicKey_out出来的是不可用的。

    与此对应,服务器openssl的类库中PEM_read_RSA_PUBKEY()读入是对应的,而PEM_read_RSAPublicKEY()读入是不对应的。
     

  • 相关阅读:
    JavaScript 数组进行拼接的函数
    Scrum工件
    Scrum角色
    Scrum
    看板kanban
    敏捷估算
    用户故事地图
    用户故事
    用户画像
    AARRR模型
  • 原文地址:https://www.cnblogs.com/syjkfind/p/4617492.html
Copyright © 2011-2022 走看看