1

我在 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-----
4

4 回答 4

3

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

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

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

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

于 2013-02-05T17:57:18.167 回答
1

以下代码来自几位作者(在评论中列出),在 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};
于 2014-09-04T17:50:49.877 回答
0

使用@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();
于 2013-02-05T21:20:03.870 回答
-1

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

万一它有帮助,我在我的应用程序中使用了这段代码,它创造了奇迹:)

于 2013-02-05T17:47:00.607 回答