【问题标题】:When should numbers used to derive RSA private key have leading `00`?用于派生 RSA 私钥的数字何时应以“00”开头?
【发布时间】:2013-01-28 22:07:27
【问题描述】:

给定一个 RSA 私钥...

-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBAMpZrx0gTluJEu6+fop1e60lwbnlBD6kHvoRx85GBhUgD8SQknjc
LcU2qqM/pV9ZX8MV8x49h2mzrmRyH7kDmpcCAwEAAQJAYf2GYMt5Rrids4IKk5CL
IPFs3FH8eT1PRvh/UvP0FBwDMZu/Q4m+3PNTM3ARQhFuCvWgCalMmZkyVx0HYRLe
4QIhAOaaQm+b/bSoHqolvVTcyfBL09rrLFZhgGkETX3R6cVRAiEA4KLdUm97YBxP
T6/jkn/P7K8SUWEO9o9u8Bif1UKQB2cCIETqoSQ92EqfW9q5wKWV/nvkDYKFehCu
vvOjp40MqPKhAiA2sPBZpbLQD5Rvvk8V1/Bzm5xGG+9csEc+RYCEl5QheQIhAKgi
Xb3zY9lqtpX/mgTIrW6RPB3GocviJOibqtpfNxRU
-----END RSA PRIVATE KEY-----

无装甲,base64 解码给出...

30 82 01 3a 02 01 00 02 41 00 ca 59 af 1d 20 4e 5b 89 12 ee be 7e 8a 75 7b ad 25 c1 b9 e5 04 3e 
a4 1e fa 11 c7 ce 46 06 15 20 0f c4 90 92 78 dc 2d c5 36 aa a3 3f a5 5f 59 5f c3 15 f3 1e 3d 87 
69 b3 ae 64 72 1f b9 03 9a 97 02 03 01 00 01 02 40 61 fd 86 60 cb 79 46 b8 9d b3 82 0a 93 90 8b 
20 f1 6c dc 51 fc 79 3d 4f 46 f8 7f 52 f3 f4 14 1c 03 31 9b bf 43 89 be dc f3 53 33 70 11 42 11 
6e 0a f5 a0 09 a9 4c 99 99 32 57 1d 07 61 12 de e1 02 21 00 e6 9a 42 6f 9b fd b4 a8 1e aa 25 bd 
54 dc c9 f0 4b d3 da eb 2c 56 61 80 69 04 4d 7d d1 e9 c5 51 02 21 00 e0 a2 dd 52 6f 7b 60 1c 4f 
4f af e3 92 7f cf ec af 12 51 61 0e f6 8f 6e f0 18 9f d5 42 90 07 67 02 20 44 ea a1 24 3d d8 4a 
9f 5b da b9 c0 a5 95 fe 7b e4 0d 82 85 7a 10 ae be f3 a3 a7 8d 0c a8 f2 a1 02 20 36 b0 f0 59 a5 
b2 d0 0f 94 6f be 4f 15 d7 f0 73 9b 9c 46 1b ef 5c b0 47 3e 45 80 84 97 94 21 79 02 21 00 a8 22 
5d bd f3 63 d9 6a b6 95 ff 9a 04 c8 ad 6e 91 3c 1d c6 a1 cb e2 24 e8 9b aa da 5f 37 14 54 

我将其中的所有数字提取到一个数组中...

[ '00',
  '00ca59af1d204e5b8912eebe7e8a757bad25c1b9e5043ea41efa11c7ce460615200fc4909278dc2dc536aaa33fa55f595fc315f31e3d8769b3ae64721fb9039a97',
  '010001',
  '61fd8660cb7946b89db3820a93908b20f16cdc51fc793d4f46f87f52f3f4141c03319bbf4389bedcf35333701142116e0af5a009a94c999932571d076112dee1',
  '00e69a426f9bfdb4a81eaa25bd54dcc9f04bd3daeb2c56618069044d7dd1e9c551',
  '00e0a2dd526f7b601c4f4fafe3927fcfecaf1251610ef68f6ef0189fd542900767',
  '44eaa1243dd84a9f5bdab9c0a595fe7be40d82857a10aebef3a3a78d0ca8f2a1',
  '36b0f059a5b2d00f946fbe4f15d7f0739b9c461bef5cb0473e45808497942179',
  '00a8225dbdf363d96ab695ff9a04c8ad6e913c1dc6a1cbe224e89baada5f371454' 
]

我不明白的是……

1。为什么有些提取的值有前导 00 而有些没有?

2。第一个值 (00) 的用途是什么?这对我来说似乎是多余的。

我想知道的原因是我想在 javascript 中生成 RSA 密钥。我正在生成所需的数字,但我不知道它们什么时候应该有前导 00s。

编辑

另外,我不太明白整个事情的第一部分......

30 82 01 3a

我知道01 3a 是消息其余部分的长度,但我不知道30 82 代表什么。

3。解码密钥开头的30 82 是什么?

【问题讨论】:

    标签: javascript base64 rsa asn.1 der


    【解决方案1】:

    大部分答案都在这里:http://pumka.net/2009/12/19/reading-writing-and-converting-rsa-keys-in-pem-der-publickeyblob-and-privatekeyblob-formats/

    1. 请注意,DER 中的许多整数值都以零字节开头。这是因为 ASN.1 标准要求如果将多字节整数的第一位设置为 1,则在多字节整数的开头添加零字节。

    2. 在@Gwyn 的回答后更新(http://www.regular-expressions.info/conditional.html) 是版本号,目前仅用于区分多素数和普通,但也将用于区分修订版RSA 规范

    3. 发件人:http://msdn.microsoft.com/en-us/library/windows/desktop/bb648645(v=vs.85).aspx 和:http://msdn.microsoft.com/en-us/library/windows/desktop/bb648641(v=vs.85).aspx

    一个 SEQUENCE 包含一个或多个类型的有序字段。它是 编码为以 0x30 标记字节开头的 TLV 三元组。

    ...

    如果 SEQUENCE 包含少于 128 个字节,则 TLV 三元组只需要一个字节来指定内容长度。如果 它超过 127 个字节,长度字段的第 7 位设置为 1,并且 位 6 到 0 指定用于 确定内容长度。

    代码...

    这是 javascript 代码,有点粗糙,但它是如何生成 RSA 密钥的工作证明,可以使用 javascript 与 OpenSSL 互操作,以及输入生成密钥所需的数字...

    function hex2b64(h) {
      var b64map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
      var b64pad="=";
    
      var i;
      var c;
      var ret = "";
      for(i = 0; i+3 <= h.length; i+=3) {
        c = parseInt(h.substring(i,i+3),16);
        ret += b64map.charAt(c >> 6) + b64map.charAt(c & 63);
      }
      if(i+1 == h.length) {
        c = parseInt(h.substring(i,i+1),16);
        ret += b64map.charAt(c << 2);
      }
      else if(i+2 == h.length) {
        c = parseInt(h.substring(i,i+2),16);
        ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4);
      }
      while((ret.length & 3) > 0) ret += b64pad;
      return ret;
    }
    
    var private_key = "-----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-----";
    
    // these numbers have been extracted from the given RSA private key
    numbers = ['00',
      'ca59af1d204e5b8912eebe7e8a757bad25c1b9e5043ea41efa11c7ce460615200fc4909278dc2dc536aaa33fa55f595fc315f31e3d8769b3ae64721fb9039a97',
      '010001',
      '61fd8660cb7946b89db3820a93908b20f16cdc51fc793d4f46f87f52f3f4141c03319bbf4389bedcf35333701142116e0af5a009a94c999932571d076112dee1',
      'e69a426f9bfdb4a81eaa25bd54dcc9f04bd3daeb2c56618069044d7dd1e9c551',
      'e0a2dd526f7b601c4f4fafe3927fcfecaf1251610ef68f6ef0189fd542900767',
      '44eaa1243dd84a9f5bdab9c0a595fe7be40d82857a10aebef3a3a78d0ca8f2a1',
      '36b0f059a5b2d00f946fbe4f15d7f0739b9c461bef5cb0473e45808497942179',
      'a8225dbdf363d96ab695ff9a04c8ad6e913c1dc6a1cbe224e89baada5f371454' ]
    
    var data = ""
    
    // loop through all the numbers
    for(i in numbers){
      var item = numbers[i]
    
      // if the first binary bit is 1 (if the first hex pair is greater than 127)
      // add a `00` prefix, as ASN1 demands
      if(parseInt(item.match(/^../),16) > 127){ item = "00"+item }
    
      // calculate the length
      var len = ("0"+(item.length/2).toString(16)).replace(/.(..)/,"$1")
    
      // build data string, the `02` is ASN1 code for string
      data += "02"+len+item
    
      // can check all the input data here
      // console.log("02", len, item)
    }
    
    // calculate the length of all the data
    var datalen = (data.length/2).toString(16)
    // add leading `0` if required to ensure hex pairs
    if(datalen.length % 2 == 1){ datalen = "0"+datalen }
    
    // set the extra bits to define the length if required, or make it empty
    var exlen = parseInt(datalen,16) >= 128 ? (datalen.length/2 + 128).toString(16) : ''
    
    // create the full data string
    var full_data = "30"+exlen+datalen+data
    
    // encode the data
    var encoded = hex2b64(full_data)
    
    // split it into lines no longer than 64 characters
    var lines = encoded.match(/.{1,64}/g)
    
    // add the armour
    lines.unshift("-----BEGIN RSA PRIVATE KEY-----")
    lines.push("-----END RSA PRIVATE KEY-----")
    
    // join it all together
    var generated_key = lines.join("\n")
    
    // check if the generated key matches the expected key
    console.log(generated_key == private_key)
    

    【讨论】:

    • 希望你不介意,但我已经修饰了你对第 [2] 点的回答。
    • @DuncanJones 问题 2 实际上不是关于填充,而是从数字数组中提取的第一个看似多余的 00。你的观点虽然很好,但实际上属于问题 1 的末尾。
    【解决方案2】:

    只是回答2,因为其他的已经解决了,第一个00是一个版本字段。使用 ASN.1 解码器here 解码 Base64 或十六进制以显示 9 个整数的序列。您需要查看 specs(参见 A.1.2)以了解它们是什么,虽然虽然不是确定的,但维基百科有一些信息 here

    【讨论】:

    • 谢谢,那回答了我所有的问题!
    猜你喜欢
    • 2011-07-11
    • 1970-01-01
    • 2019-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-21
    • 1970-01-01
    相关资源
    最近更新 更多