【问题标题】:Generate public key from numbers从数字生成公钥
【发布时间】:2014-09-05 01:50:49
【问题描述】:

我在 javascript 中使用 RSA,来自:http://www-cs-students.stanford.edu/~tjw/jsbn/

在他们的演示页面上,它能够生成所需的密钥:http://www-cs-students.stanford.edu/~tjw/jsbn/rsa2.html

但我不明白的是,如何将它们的密钥(即一些变量)从十六进制字符串转换为看起来像私钥/公钥字符串的字符串。

他们的输出看起来像这样......

Modulus (hex):
a32464be9bef16a6186a7f29d5ebc3223346faab91ea10cc00e68ba26322a1b0
3dc3e1ec61832fca37ed84018db73ae6b79bd8f3fa2945c8606766402658d0c1

Public exponent (hex, F4=0x10001):
10001

Private exponent (hex):
c3f5730d81402e7453df97df2895884e0c49b5cf5ff54737c3dd28dc6537b3fd
8b0eb2fd82148ebbf81fe16128ec1ebf39fd9a6e62d42aad245b172f4ed8661

P (hex):
f542cdc91f73747ecc20076962a2ed91749b8e0af66693ba6f67dd92f99b1533

Q (hex):
aa491917d8b05a75db7a5c1f6b592b0f0a9bb30a530cbef44ae233b9bf3d5a3b

D mod (P-1) (hex):
e5bfbea639201e70e926d7ca90ebaf4022cbd533cfbe2784edf78e48b029e6a1

D mod (Q-1) (hex):
3e3e17d8fa9083903ed83be21427f4b03bcd6ba523742e3c373ef56f38b2e14f

1/Q mod P (hex):
d93a8828e23458c2115fc1dd003274485af1483e8ec35866d5cffd214c5f853e

我希望私钥看起来像这样......

-----BEGIN RSA PRIVATE KEY-----
MIIBOQIBAAJBALH5w2R5Zl3hHoH7zDF6i5IbO7W1Ry9bT2bxTy2sJa7mZC+p3rQG
YNrX+M/KuOUuVqFaohrWLwU28+VlFjFHG5MCAwEAAQJAFZCi+Viwa62saamd+1zS
7pg4KvNVNcrFmz6gDnOueTujcBdaNfxOzWy39SxSOtPe62qYHPculci/QiJfLvOf
MQIhAOqxj69QPRqervf6ARMI1zV0+UAGA1tWgOeU7NH0U9vLAiEAwiIJu+986PKT
qmjAKDFx3OJxUuEay8kJ5apDjC6s1lkCIBVRdazGDBbb7SbHRcu11N6dNnrTUQC9
9c2TYIOdvvRLAiB7bbDKsKW2ZiTEv/0MkQNX8REkJMMothV41BxGUJbLYQIgF/yA
54gqfRtj3aPB6lDETMOZbZIWgNpn8hU2kFGdlIs=
-----END RSA PRIVATE KEY-----

【问题讨论】:

  • 在 RSA 中,“密钥”并不总是一个十六进制数字。如果您将所有参数(例如 pub 和 priv 指数、模数等)提供给数学代码(在本例中为 rsa.js),它们将为您生成密钥
  • 另外,密钥的长度将始终取决于您使用多少位加密

标签: javascript encryption rsa jsbn


【解决方案1】:

您熟悉的私钥和公钥采用称为 PEM 的格式。 PEM 是指封装 base64 编码数据的页眉和页脚。

编码的二进制数据采用 ASN.1(抽象语法符号一)的 DER(可分辨编码规则)格式。

这是一个 javascript ASN.1 解码器。 http://lapo.it/asn1js/

公钥和私钥分别包含模数和公钥或私钥。

【讨论】:

  • 感谢您的链接和建议。这是一个很大的帮助。到目前为止,我发现这个 php 脚本完全符合我的要求。如果我找不到其他任何东西,我可能会尝试将它移植到 javascript:pumka.net/2009/12/19/…
【解决方案2】:

以下代码来自几位作者(在 cmets 中列出),在 Ohloh 代码 (http://code.ohloh.net/file?fid=gxO4OHIDEgc0cAHG4K2iptOVOUY&cid=b0rvFixqcq4&s=&fp=285256&mp=&projSelected=true#L0) 上编译。所有功劳归功于他们的辛勤工作。但是编译对我有用。将此添加为 .js 库,然后您可以像这样从主例程中调用它:

publicPem = publicPEM();
privatePemUnencrypted = privatePEM(); //you will probably want to encrypt this

输出为 PEM 格式。

代码:

//From git://github.com/titanous/pem-js.gitmaster

var ASNIntValue, ASNLength, int2hex;
function privatePEM() {
  var encoded;
  encoded = '020100';
  encoded += ASNIntValue(this.n, true);
  encoded += ASNIntValue(this.e, false);
  encoded += ASNIntValue(this.d, false);
  encoded += ASNIntValue(this.p, true);
  encoded += ASNIntValue(this.q, true);
  encoded += ASNIntValue(this.dmp1, true);
  encoded += ASNIntValue(this.dmq1, false);
  encoded += ASNIntValue(this.coeff, false);
  encoded = '30' + ASNLength(encoded) + encoded;
  return "-----BEGIN RSA PRIVATE KEY-----\n" + encode64(chars_from_hex(encoded)) + "\n-----END RSA PRIVATE KEY-----";
};

function publicPEM() {
  var encoded;
  encoded = ASNIntValue(this.n, true);
  encoded += ASNIntValue(this.e, false);
  encoded = '30' + ASNLength(encoded) + encoded;
  encoded = '03' + ASNLength(encoded, 1) + '00' + encoded;
  encoded = '300d06092a864886f70d0101010500' + encoded;
  encoded = '30' + ASNLength(encoded) + encoded;
  return "-----BEGIN PUBLIC KEY-----\n" + encode64(chars_from_hex(encoded)) + "\n-----END PUBLIC KEY-----";
};

RSAKey.prototype.parsePEM = function(pem) {
  pem = ASN1.decode(Base64.unarmor(pem)).sub;
  return this.setPrivateEx(pem[1].content(), pem[2].content(), pem[3].content(), pem[4].content(), pem[5].content(), pem[6].content(), pem[7].content(), pem[8].content());
};

ASNIntValue = function(integer, nullPrefixed) {
  integer = int2hex(integer);
  if (nullPrefixed) {
    integer = '00' + integer;
  }
  return '02' + ASNLength(integer) + integer;
};

ASNLength = function(content, extra) {
  var length;
  if (!(typeof extra !== "undefined" && extra !== null)) {
    extra = 0;
  }
  length = (content.length / 2) + extra;
  if (length > 127) {
    length = int2hex(length);
    return int2hex(0x80 + length.length / 2) + length;
  } else {
    return int2hex(length);
  }
};

int2hex = function(integer) {
  integer = integer.toString(16);
  if (integer.length % 2 !== 0) {
    integer = '0' + integer;
  }
  return integer;
};

/* CryptoMX Tools - Base64 encoder/decoder
 * http://www.java2s.com/Code/JavaScriptDemo/Base64EncodingDecoding.htm
 *
 * Copyright (C) 2004 - 2006 Derek Buitenhuis
 * Modified February 2009 by TimStamp.co.uk
 * GPL v2 Licensed
 */
function encode64(a){a=a.replace(/\0*$/g,"");var b="",d,e,g="",h,i,f="",c=0;do{d=a.charCodeAt(c++);e=a.charCodeAt(c++);g=a.charCodeAt(c++);h=d>>2;d=(d&3)<<4|e>>4;i=(e&15)<<2|g>>6;f=g&63;if(isNaN(e))i=f=64;else if(isNaN(g))f=64;b=b+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(h)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(d)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(i)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(f)}while(c<
a.length);a="";b=b.split("");for(c=0;c<b.length;c++){if(c%64==0&&c>0)a+="\n";a+=b[c]}b.join();b=a%4;for(c=0;c<b;c++)a+="=";return a}
function decode64(a){var b="",d,e,g="",h,i="",f=0;a=a.replace(/[^A-Za-z0-9\+\/\=\/n]/g,"");do{d="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(f++));e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(f++));h="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(f++));i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(f++));d=d<<2|e>>4;e=(e&15)<<4|h>>2;g=(h&3)<<
6|i;b+=String.fromCharCode(d);if(h!=64)b+=String.fromCharCode(e);if(i!=64)b+=String.fromCharCode(g)}while(f<a.length);return b=b.replace(/\0*$/g,"")};

/* JavaScript ASCII Converter
 * http://www.vortex.prodigynet.co.uk/misc/ascii_conv.html
 *
 * TPO 2001/2004
 * Modified Feb 2009 by Tim Stamp (timstamp.co.uk)
 * License Unknown
 */
function chars_from_hex(a){var c="";a=a.replace(/^(0x)?/g,"");a=a.replace(/[^A-Fa-f0-9]/g,"");a=a.split("");for(var b=0;b<a.length;b+=2)c+=String.fromCharCode(parseInt(a[b]+""+a[b+1],16));return c};

【讨论】:

    【解决方案3】:

    使用@borkencode (http://lapo.it/asn1js/) 建议的链接,我已经能够成功提取公钥的所有组件,以及私钥的相关组件。

    测试提取键的数字部分的代码

    // copy and paste into console whilst on `http://lapo.it/asn1js/`
    // so that dependancies are met
    var getParts, pri, pub;
    
    getParts = function(key) {
      var end, current, output, crypt, slice, sa, so, start, tree, _i, _len;
    
      // strip out the BEGIN/END tags from the input keys
      crypt = key.replace(/^-.+/mg, '');
    
      // decode the input text into ASN1 object
      tree = ASN1.decode(Base64.decode(crypt));
    
    
      // set root object based on type of key
      if (key.match(/PUBLIC KEY-/)) {
        root = tree.sub[1].sub[0].sub;
      } else if (key.match(/PRIVATE KEY-/)) {
        root = tree.sub;
      } else {
        console.error("Not happening");
        return;
      }
    
      // initialize empty array to populate with results
      output = [];
    
      // loop through the root ASN1 object
      for (_i = 0, _len = root.length; _i < _len; _i++) {
    
        // get the current object
        current = root[_i];
    
        // set the start and end positions in the stream
        start = current.stream.pos + current.header;
        end = start + current.length;
    
        // cut the required compnent out of the stream
        slice = current.stream.enc.slice(start, end);
    
        // format the slice in HEX, joining as string
        // and replacing leading `00`s
        str = slice.map(function(i) {
          return ("0" + i.toString(16)).replace(/.(..)/, "$1");
        }).join('').replace(/^(00)+/, '');
    
        // add to output array
        output.push(str);
    
      }
    
      // send the results!
      return output;
    
    };
    
    pri = "-----BEGIN RSA PRIVATE KEY-----\n"+
    "MIIBOgIBAAJBAMpZrx0gTluJEu6+fop1e60lwbnlBD6kHvoRx85GBhUgD8SQknjc\n"+
    "LcU2qqM/pV9ZX8MV8x49h2mzrmRyH7kDmpcCAwEAAQJAYf2GYMt5Rrids4IKk5CL\n"+
    "IPFs3FH8eT1PRvh/UvP0FBwDMZu/Q4m+3PNTM3ARQhFuCvWgCalMmZkyVx0HYRLe\n"+
    "4QIhAOaaQm+b/bSoHqolvVTcyfBL09rrLFZhgGkETX3R6cVRAiEA4KLdUm97YBxP\n"+
    "T6/jkn/P7K8SUWEO9o9u8Bif1UKQB2cCIETqoSQ92EqfW9q5wKWV/nvkDYKFehCu\n"+
    "vvOjp40MqPKhAiA2sPBZpbLQD5Rvvk8V1/Bzm5xGG+9csEc+RYCEl5QheQIhAKgi\n"+
    "Xb3zY9lqtpX/mgTIrW6RPB3GocviJOibqtpfNxRU\n"+
    "-----END RSA PRIVATE KEY-----";
    
    pub = "-----BEGIN PUBLIC KEY-----\n"+
    "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMpZrx0gTluJEu6+fop1e60lwbnlBD6k\n"+
    "HvoRx85GBhUgD8SQknjcLcU2qqM/pV9ZX8MV8x49h2mzrmRyH7kDmpcCAwEAAQ==\n"+
    "-----END PUBLIC KEY-----";
    
    console.dir(getParts(pri));
    console.dir(getParts(pub));
    

    输出到控制台:

    0: ""
    1: "ca59af1d204e5b8912eebe7...d8769b3ae64721fb9039a97"
    2: "010001"
    3: "61fd8660cb7946b89db3820...94c999932571d076112dee1"
    4: "e69a426f9bfdb4a81eaa25b...c56618069044d7dd1e9c551"
    5: "e0a2dd526f7b601c4f4fafe...ef68f6ef0189fd542900767"
    6: "44eaa1243dd84a9f5bdab9c...a10aebef3a3a78d0ca8f2a1"
    7: "36b0f059a5b2d00f946fbe4...f5cb0473e45808497942179"
    8: "a8225dbdf363d96ab695ff9...1cbe224e89baada5f371454"
    ------------------------------------------------------
    0: "ca59af1d204e5b8912eebe7...d8769b3ae64721fb9039a97"
    1: "010001"
    

    我已经测试了这些值,将它们用作输入来使用我的 javascript 库加密和解密消息。我现在真正想做的,是通过另一种方式创建.pem.pub 文件,从数字到格式化文本。

    到目前为止,我发现的最接近的是一个 PHP 函数,我可能会将其转换为 javascript。详情在这里:http://pumka.net/2009/12/19/reading-writing-and-converting-rsa-keys-in-pem-der-publickeyblob-and-privatekeyblob-formats/

    //Encode key sequence
    $modulus = new ASNValue(ASNValue::TAG_INTEGER);
    $modulus->SetIntBuffer($Modulus);
    $publicExponent = new ASNValue(ASNValue::TAG_INTEGER);
    $publicExponent->SetInt($PublicExponent);
    $keySequenceItems = array($modulus, $publicExponent);
    $keySequence = new ASNValue(ASNValue::TAG_SEQUENCE);
    $keySequence->SetSequence($keySequenceItems);
    //Encode bit string
    $bitStringValue = $keySequence->Encode();
    $bitStringValue = chr(0x00) . $bitStringValue; //Add unused bits byte
    $bitString = new ASNValue(ASNValue::TAG_BITSTRING);
    $bitString->Value = $bitStringValue;
    //Encode body
    $bodyValue = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00" . $bitString->Encode();
    $body = new ASNValue(ASNValue::TAG_SEQUENCE);
    $body->Value = $bodyValue;
    //Get DER encoded public key:
    $PublicDER = $body->Encode();
    

    【讨论】:

      【解决方案4】:

      如需回答您的问题,请查看RSA algorithm 的实际公式。对于复杂的部分,最好把转换留给 JS

      如果有帮助,我在我的应用程序中使用了 this code,效果非常好:)

      【讨论】:

        猜你喜欢
        • 2015-05-16
        • 1970-01-01
        • 2022-01-13
        • 2019-10-12
        • 1970-01-01
        • 1970-01-01
        • 2012-04-14
        • 2017-02-28
        • 2019-08-15
        相关资源
        最近更新 更多