<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      RSA加密算法在Java與C#中的跨語言密鑰轉換問題

      前言

      ??前段時間遇到個項目問題,涉及RSA加密,研究了幾天,終于解決了,情況是這樣的,項目使用的是.net framework的框架,需要對網絡傳輸的個人數據進行加密,首先能從服務器直接獲取一個加密用的公鑰,使用這個公鑰對本地個人數據加密后,再進行傳輸。
      ??出現的問題是完成加密后的數據在服務器端始終無法解析,折騰了好一段時間才知道是由于服務器端的Java和客戶端的C#兩者使用的RSA算法的密鑰格式不一致,而.net framework沒有提供對于相應格式的密鑰的轉換支持,C#對RSA相關密鑰格式的支持在.net core之后才完善。
      ??問題找到了,解決方案兩條思路,第一找開源庫,研究了一下RSA,看了一圈github上的開源庫,感覺開源庫功能過于全面而復雜,引入項目增加的代碼量太大,又看一下解析規則,感覺并不是太復雜,于是嘗試第二條路自己解析,從頭記錄一下解決方案:

      1 算法簡介

      ??RSA算法是一種廣泛使用的非對稱加密算法,是目前最優秀的公鑰方案之一。RSA算法基于一個數學理論支持,即獲取兩個大素數相乘的結果很簡單,而對它們的乘積進行因式分解卻極其困難。

      對稱加密:同一個密鑰可以同時用作加密和解密,稱為對稱加密。
      非對稱加密:非對稱加密算法需要兩個密鑰分別進行加密和解密,即公鑰和私鑰。

      1.1 算法步驟

      1. p、q(兩個大素數): 取兩個足夠大的素數p、q
      2. N(合數): 令N = p * q
      3. L: L = (p-1) * (q-1)
      4. E(公有冪): 使得E與L互質,且1 < E < L
      5. D(私有冪): 使得(D*E)% L = 1,且1 < D < L

      素數:除了1和它自身外,不能被其他自然數整除的大于1的自然數。
      合數:除了素數以外的大于1的自然數。
      互質:公約數只有1的兩個整數,叫做互質整數。

      其中p和q是兩個很大的素數,N是他們的乘積,用于之后求模,它是個合數,即合數模,E是公鑰中的冪指數,D是私鑰中的冪指數。

      得到的(E,N)即為公鑰,(D,N)即為私鑰。

      公鑰與私鑰是一對,如果用公鑰對數據進行加密,只有用對應的私鑰才能解密;如果用私鑰對數據進行加密,那么只有用對應的公鑰才能解密。

      公鑰加密——私鑰解密
      加密過程:密文=(明文^E)mod N
      解密過程:明文=(密文^D)mod N

      也可以反過來,私鑰加密——公鑰解密
      加密過程:密文=(明文^D)mod N
      解密過程:明文=(密文^E)mod N

      由于公鑰和私鑰都是數的組合,我們一般把合數模N的二進制位數長度稱作密鑰的長度。一般密鑰會要求一定的長度,RSA從提出到現在已近二十年,經歷了各種攻擊的考驗,目前被破解的最長RSA密鑰是768個二進制位,因此可以認為,768位的密鑰不用擔心受到除了國家安全管理(NSA)外的其他事物的危害,1024位的密鑰幾乎已經是安全的。

      加、解密的過程是個模指數運算過程。

      1.2 算法示例

      p = 3q = 11
      計算N:N = p * q = 3 * 11 = 33
      計算L:L = (p - 1) * (q - 1) = 20
      E = 3,可滿足E與L互質,且1 < E < L
      D = 7,可滿足(D * E) % L = 1,即(D * 3) % 20 = 1,且1 < D < L

      得到(3, 33)為公鑰,(7, 33)為私鑰

      該例中的N為33,即二進制的10 0001,即例子中密鑰的長度為5

      例子里的數取得很小以便于理解。

      給定要加密的明文“7”,演示加密和解密的過程:

      進行“公鑰加密、私鑰解密”
      加密過程:密文 = (明文^E)mod N = (7 ^ 3) % 33 = 343 % 33 = 13,得到加密后的密文為“13”
      解密過程:明文 = (密文^D)mod N = (13 ^ 7) % 33 = 62748517 % 33 = 7,解回明文“7”

      反過來,進行“私鑰加密,公鑰解密”
      加密過程:密文 = (明文^D)mod N = (7 ^ 7) % 33 = 823543 % 33 = 28,得到加密后的密文為“28”
      解密過程:明文 = (密文^E)mod N = (28 ^ 3) % 33 = 21952 % 33 = 7,解回明文“7”

      例子中的明文為數字,加密算法可以用于各種數據信息(數字、字母、符號、圖片、音頻、視頻),現實中的所有數據在計算機中都是使用二進制的“0”、“1”組合的形式表示的,再通過解碼可以轉換成能夠理解的數據信息格式。

      2 密鑰格式

      ??RSA算法的密鑰具有多種不同的描述格式,例如PEM格式、ASN格式、XML格式、DER格式等,使用不同的密鑰格式通過對應的規則都可以用來表示密鑰對象,解析出各個字段的內容。

      2.1 PEM格式

      PEM格式又具有多種填充方式從“PKCS#1”一直到“PKCS#15”,常用的是“PKCS#1”和“PKCS#8”,它們的文件頭也可能存在區別。

      公鑰

      -----BEGIN PUBLIC KEY-----
      MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALDTR8eu33xA4ru/JPbEsIapTTF1SzEi
      IjAbntCXdEK6xdwsuomv7kL7rVQXef2rzhvzBSyZxuRbf33u6fi4jW8CAwEAAQ==
      -----END PUBLIC KEY-----
      

      私鑰

      -----BEGIN PRIVATE KEY-----
      MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAsNNHx67ffEDiu78k
      9sSwhqlNMXVLMSIiMBue0Jd0QrrF3Cy6ia/uQvutVBd5/avOG/MFLJnG5Ft/fe7p
      +LiNbwIDAQABAkEAmKtS5k1OF/HN0VwPhh/8acfzJiinaxyVeAPg8yhQ8OryQxG2
      CnqTgG4V2PAMvxX42W+ZqA0zTFXx4EtWmq8FQQIhANjpAY7W6TAidjy2qlmfuSl4
      DoY75bKJRsg2GVVYDDyTAiEA0LD88irK80hKj2JeAgEP0NXyYV8QZSuEM5Qk0G3U
      0TUCIFpNhwyEhEg50KeuFHWDfX66MLHJtfMCG6m2fA1/vnhpAiEAowF7sdRHDdvr
      kS+uajZWGjLizbepYLyq2HbggoUnc/kCICj08MHdsE2excF0rtNi457J57ZhnTsj
      9uDBvPY+9JTT
      -----END PRIVATE KEY-----
      

      2.2 ASN格式

      ASN格式是一串純Base64字符串,可以看成去掉了PEM格式的頭尾描述后的形式。

      公鑰

      MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALDTR8eu33xA4ru/JPbEsIapTTF1SzEi
      IjAbntCXdEK6xdwsuomv7kL7rVQXef2rzhvzBSyZxuRbf33u6fi4jW8CAwEAAQ==
      

      私鑰

      MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAsNNHx67ffEDiu78k
      9sSwhqlNMXVLMSIiMBue0Jd0QrrF3Cy6ia/uQvutVBd5/avOG/MFLJnG5Ft/fe7p
      +LiNbwIDAQABAkEAmKtS5k1OF/HN0VwPhh/8acfzJiinaxyVeAPg8yhQ8OryQxG2
      CnqTgG4V2PAMvxX42W+ZqA0zTFXx4EtWmq8FQQIhANjpAY7W6TAidjy2qlmfuSl4
      DoY75bKJRsg2GVVYDDyTAiEA0LD88irK80hKj2JeAgEP0NXyYV8QZSuEM5Qk0G3U
      0TUCIFpNhwyEhEg50KeuFHWDfX66MLHJtfMCG6m2fA1/vnhpAiEAowF7sdRHDdvr
      kS+uajZWGjLizbepYLyq2HbggoUnc/kCICj08MHdsE2excF0rtNi457J57ZhnTsj
      9uDBvPY+9JTT
      

      2.3 XML格式

      XML格式密鑰是C#語言下的默認格式,使用XML描述語言將密鑰對象的各個字段表示出來。

      公鑰

      <RSAKeyValue>
          <Modulus>
              riLSQFVDC229P5F+Mkicbkpg5OC8+SeL6hvkJXIGYiN/e4YnprCxuIp5sH9AwWup4WJmObPKd1jOVGm07UwgVU7CDtTaVe1Uuk78yJBwgRuSteQjHYMmH6nG5YHvvONuvkmLnyIKGygJBL+4+Qmd3GaCHRtIrdfShlH3UbPINlM=
          </Modulus>
          <Exponent>
              AQAB
          </Exponent>
      </RSAKeyValue>
      

      私鑰

      <RSAKeyValue>
          <Modulus>
      		riLSQFVDC229P5F+Mkicbkpg5OC8+SeL6hvkJXIGYiN/e4YnprCxuIp5sH9AwWup4WJmObPKd1jOVGm07UwgVU7CDtTaVe1Uuk78yJBwgRuSteQjHYMmH6nG5YHvvONuvkmLnyIKGygJBL+4+Qmd3GaCHRtIrdfShlH3UbPINlM=
          </Modulus>
          <Exponent>
              AQAB
          </Exponent>
          <P>
              6yCboYtKzIezMOFzGzzW8dp7SBT8f7jTRzH1ZIKQYKF0Mq/39k80SeUvY578O031+bg6i3cbNvvAhL8XjqTtmQ==
          </P>
          <Q>
              vZgnL5LHnNE5uUW5NBwYvZbIz6hWNzc6kyDGimI8WBBFJOI06IdYGL2VMeGVs4lt5a1tM7T3c6gzBKgDQpL+yw==
          </Q>
          <DP>
              p5tV9YDyr/unq5d6Uxc6bar9qHN1TqJ00VJ2h9BelNNinmM70fPB5U8fSddiG/BGAF3oNdSQrNAm+zmw1DkTOQ==
          </DP>
          <DQ>
              fxS1b1XbJmm3X1A0y5DppGqlP0t+PpRuVp/pdGhUOlLthcN540KU8kBg+IZUaXr8hq6wO7BZDNT5HW3ggYc18Q==
          </DQ>
          <InverseQ>
              q29etXnlszOH0FlQWDL9yLfJ+EruH4VURY1mZGz/+/qvPewUwyEf+EqJkZHVXEijnSa1CiFELK2YE9PhkUp2Xw==
          </InverseQ>
          <D>
              bZUoLqf5KwYCJDDQ85/SIW3ZD++FvF1wpQCsUAwzjCq+nONNrI5hKLqr3bAW9iFkpJshrYpBDV3rah+jZfmUFk/UZeur2+kA2r5r1or34+HiIhT4sehU1lxww4DvTzf1/1ivG4LCvUPoFtT67Zdh8pNEC27N6bFDL8fbSU7GcmE=
          </D>
      </RSAKeyValue>
      

      2.4 DER格式

      DER格式是一種十六進制的密鑰格式

      公鑰

      30818902818100C19CFA5EA25F99C482499E3A557C7D0C3ABB375B19900CF4956E39F5B1EACA46A37CEE30DDBEA72979B6DABA11D4B9BD51CB1D79ED667607E65CF53491EE6BE35155072D5EFB96BA0E0FC0B9C1DFEDFA30886F645218CC680E55A7D5568AD59283E9BB3DC82970F6B3F6DD83FB308E2C610F362C71977D428614ED5FB59EFDB10203010001
      

      私鑰

      3082025D020100028181009951BFB322876658587C207F2AFC2F2638DFD4EEEE925669C4A9487A3774891AEBE638940318B1AE270784FCFBE768C8C989E33B3953D820326DFAC7862AA133F96EE216C1B3F5651D45194CA02E9926E8FF133B2C03BB22BFE8C60B13D4757F263D4A792B188A8183FAB53B193C8AF8AB8EAB7020547C20D5BC90E1B369DBA902030100010281800FD9829ECB28022D89E0331FD25AC5A906E224CA1A81A84B40D85B34BF3CDDDB999D7025E4F80D8E3A5CADA3D58AC3AB56225A0A4A4FDF9CDC79C01E16419BEE71997546C68E6408E5D9C044858DCB6393F285D3EBAAD0C75298F61B33B752EB8E1ABA4D66E5380FF52DE07929A96367673CDAE5945A0C13F3503FABEDB1758D024100C52F9745C13B77968D52F29ACDEE00F2DF07AF8025048348B054B7EFF460097CAB824212447F674B55CF74E489DD399E702D3D655C74484248E05CA2E9DCCEE7024100C70CA9EA361ED73C42627254F33A3FA81AD0AADD64D45A1E536E64C7E31737B7ED3FCC20E03A082673C6E7A7270640F6132AA295FF406D6133090E7D89397EEF024100883505936395065872AAB77683854216824536CF97C2744543B8618E5909F5C3AE5D3DF28C6A4D19D6DE84EA50E905A211EECE18343306AEF2D43869388E1445024100B061FE6776D1D974A276CE4D8CC2FF099DC96EBF84CBCF97B3E2CD177B9A655B6CB6EDD1EC20407CA2778D6B475F794D152AE0ABFE663F06B4CCBFB46A5732AD02404981E8728E85719A319C9A8C4C7D3B162BFA728AD5FD054C0A45A9A625385167F0822D2398680FD75BF29A3C20A4D72D2115ABD06F27B3819214AAC77284518A
      

      3 密鑰解析(ASN.1)

      ??不管RSA算法的密鑰格式形式如何,其最終目的是表示密鑰的內容,RSA算法的密鑰最終需要被解析成一個類似結構體的對象,其解析規則遵循ASN.1標準。

      ??ASN.1抽象語法標記(Abstract Syntax Notation One),描述了一種對數據進行表示、編碼、傳輸和解碼的數據格式。提供了一整套正規的格式用于描述對象的結構。數字1被加在ASN的后邊,是為了保持ASN的開放性,可以讓以后功能更加強大的ASN被命名為ASN.2等,但至今也沒有出現。

      ??ASN.1中關聯了多個標準化編碼規則,基本編碼規則(BER)、規范編碼規則(CER)、識別名編碼規則(DER)、壓縮編碼規則(PER)和 XML編碼規則(XER)。其中BER、CER、DER、PER都是二進制編碼規則,BER(BasicEncoding Rules)是ASN.1中最早定義的編碼規則,其他編碼規則是在BER的基礎上添加新的規則而構成的。

      3.1 BER編碼規則

      ??傳輸語法的格式為TLV三元組<Tag, Length, Value>,其中Value字段又能繼續包含新的TLV三元組,即可以產生嵌套<T, L, <T, L, V>>

      ??密鑰的各種格式最終都會被轉換成二進制,基于八位組的大字節序(Big-Endian)編碼方式,高八位在左,低八位在右來解析。例如DER格式的十六進制密鑰“30819f300d0609…………”的轉換如下:

      十六進制密鑰 二進制高八位 二進制低八位
      30 0011 0000
      81 1000 0001
      9F 1001 1111
      30 0011 0000
      0D 0000 1101
      06 0000 0110
      09 0000 1001

      跨語言加密解密的時候需要考慮各個語言的字節序編碼方式(Big-Endian與Little-Endian),C/C++語言的字節序跟編譯平臺的CPU相關,主流的Intel-x86架構采用小字節序,Java語言采用大字節序,用于網絡傳輸的網絡字節序是大字節序,多端互操作的情況下,需要考慮大小字節序的轉換問題,額外處理。

      3.1.1 Tag字段

      包含一個或若干個八位組(即字節),以十六進制“30”為例,二進制表示為“0011 0000”,各個位的含義如下:

      第7位 第6位 第5位 第4位 第3位 第2位 第1位 第0位
      0 0 1 1 0 0 0 0
      標簽類型 標簽類型 編碼方式 值類型 值類型 值類型 值類型 值類型

      第7、6位指明標簽分類:

      標簽分類
      00 universal 通用標簽
      01 application 應用標簽
      10 context-specific 上下文專用標簽
      11 private 私有標簽
      3.1.1.1 通用標簽

      當Tag分類為”universal 通用標簽“時,第5位指明標簽編碼方式:

      編碼方式
      0 primitive 原始類型
      1 constructed 構造類型

      當Tag分類為”universal 通用標簽“時,第4~0位:指明標簽值的類型:

      值(二進制) 值(十進制) 值類型
      00000 0 保留
      00001 1 Boolean 布爾類型
      00010 2 Integer 整型
      00011 3 Bit String 位串
      00100 4 Octet String 字節串(八位位組串)
      00101 5 NULL 空值
      00110 6 Object Identifier 對象標識符
      00111 7 Object Description
      01000 8 External,Instance of
      01001 9 Real 實數
      01010 10 Enumerated 枚舉類型
      01011 11 Embedded PDV
      01100 12 UTF8 String
      01101 13 Relative-oid
      01110 14 保留
      01111 15 保留
      10000 16 Sequence 序列,Sequence of 單類型序列
      10001 17 Set集合,Set of 單類型集合
      10010 18 Numeric String
      10011 19 Printable String
      10100 20 Teletex String,T61 String
      10101 21 Videotex String
      10110 22 IA5 String
      10111 23 UTC Time
      11000 24 Generalized Time
      11001 25 Graphic String
      11010 26 Visible String,ISO646 String
      11011 27 General String
      11100 28 Universal String
      11101 29 Character String
      11110 30 BMP String
      11111 31 保留
      3.1.1.2 非通用標簽

      當Tag分類不為”universal 通用標簽“,而是另外三種時,在后續的多個八位組中編碼,第一個八位組后五位固定全部為1,其余的八位組最高位為1表示后續還有,為0表示Tag結束。

      第一個八位組 第二個八位組 第二個八位組 …… 第n個八位組
      01 11 1111 1xxx xxxx 1xxx xxxx 1… … 0xxx xxxx
      高位01表示“application 應用標簽”
      后五位固定全為1
      高位為1,表示還在編碼 高位為1,表示還在編碼 高位為1,表示還在編碼 高位為0,表示結束

      3.1.2 Length字段

      Length字段的組織方式有兩大類:定長方式和不定長方式,第一個八位組不為0x80表示定長方式,為0x80表示不定長方式:

      3.1.2.1 定長方式

      定長方式中,按長度是否超過一個八位,又分為短形式、長形式,最高位為”0“表示短形式,最高位為”1“表示長形式:

      短形式

      各個位的含義如下:

      最高位 后7位
      0 xxx xxxx
      0表示短形式 表示TLV三元組中的Value字段占用的字節數(范圍0~127)
      長形式

      各個位的含義如下:

      第一個八位組 ……
      1xxx xxxx ……
      最高位”1“表示長形式
      后7位表示需要繼續編碼的長度(字節數)
      按照第一個八位提供的長度值,繼續編碼,其數值表示TLV三元組中的Value字段占用的字節數(范圍0~256^126-1)
      3.1.2.2 不定長方式

      Length所在八位組固定編碼為0x80,但在Value編碼結束后以兩個0x00結尾。

      第一個八位組 …… 第n-1個八位組 第n個八位組
      1000 0000 …… 0000 0000 0000 0000
      0x80表示不定長方式 不為0x00,表示還在編碼,其數值表示TLV三元組中的Value字段占用的字節數 遇到第一個0x00 遇到兩個連續的0x00表示結束

      3.1.3 Value字段

      該字段內可能包含基礎數據,也可能包含嵌套的TLV三元組,需要具體解析。

      3.2 公鑰解析示例

      按照上述規則,解析一個密鑰實例,以公鑰為例(DER格式)解析方式如下:

      30819f300d06092a864886f70d010101050003818d0030818902818100890f1a96bb296740990674217c96afe8bbbc63ba69123a55c87f03afe36f106522c2935a650a6bfd929a575941396d888424e4ee702e33f5ea2275d4d9e8c80c6c503a07c1f471f501e89abd4c6fd169b4c32460e1fd35ff2bbdb3febaa4c28a5b549b20017caea2652761b2a7edb22cb765921e18f1fe9315a8ade66625d11d0203010001
      

      按照ASN.1規范的BER編碼規則對每個字節進行解析如下:

      字節序號 密鑰原文 對應的二進制 TLV組
      (“····”縮進)
      語義
      0 30 0011 0000 T Tag值 Universal類型 結構體——類型SEQUENCE
      1 81 1000 0001 L Length值 定長 長類型——長度1個字節
      2 9F 1001 1111 ——實際長度159個字節(0x9F轉十進制)
      V{ Value值{
      3 30 0011 0000 ····T Tag值 Universal類型 結構體 類型SEQUENCE
      4 0D 0000 1101 ····L Length值 定長 短類型——實際長度13個字節
      ····V{ Value值{
      5 06 0000 0110 ········T Tag值 Universal類型 原數據——類型OBJECT IDENTIFIER
      6 09 0000 1001 ········L Length值 定長 短類型——實際長度9個字節
      ········V{ Value值{
      7~11 2A 86 48 86 F7 ············
      12~15 0D 01 01 01 ············
      ········} }
      16 05 0000 0101 ········T Tag值 Universal類型 原數據——類型NULL
      17 00 0000 0000 ········T Tag值 Universal類型 原數據——類型BER保留
      ····} }
      18 03 0000 0011 ····T Tag值 Universal類型 原數據——類型BIT STRING
      19 81 1000 0001 ····L Length值 定長 長類型——長度1個字節
      20 8D 1000 1101 ····· ——實際長度141個字節
      ····V{ Value值{
      21 00 ········ unused bit
      22 30 ········T Tag值 Universal類型 結構體 類型SEQUENCE
      23 81 1000 0001 ········L Length值 定長 長類型——長度1個字節
      24 89 1000 1001 ········ ——實際長度137個字節
      ········V{ Value值{
      25 02 0000 0010 ············T Tag值 Universal類型 原數據——類型INTEGER
      26 81 1000 0001 ············L Length值 定長 長類型——長度1個字節
      27 81 1000 0001 ············ ——實際長度129個字節
      ············V{ Value值{
      28 00 ················ 首字節的MSB=1時,補0
      29~156 89 0F 1A 96 BB ················
      29 67 40 99 06 ················
      74 21 7C 96 AF ················
      E8 BB BC 63 BA ················
      69 12 3A 55 C8 ················
      7F 03 AF E3 6F ················
      10 65 22 C2 93 ················
      5A 65 0A 6B FD ················
      92 9A 57 59 41 ················
      39 6D 88 84 24 ················ 128字節的公鑰值modulus
      E4 EE 70 2E 33 ················
      F5 EA 22 75 D4 ················
      D9 E8 C8 0C 6C ················
      50 3A 07 C1 F4 ················
      71 F5 01 E8 9A ················
      BD 4C 6F D1 69 ················
      B4 C3 24 60 E1 ················
      FD 35 FF 2B BD ················
      B3 FE BA A4 C2 ················
      8A 5B 54 9B 20 ················
      01 7C AE A2 65 ················
      27 61 B2 A7 ED ················
      B2 2C B7 65 92 ················
      1E 18 F1 FE 93 ················
      15 A8 AD E6 66 ················
      25 D1 1D ················
      ············} }
      157 02 0000 0010 ············T Tag值 Universal類型 原數據——類型INTEGER
      158 03 0000 0011 ············L Length值 定長 短類型——實際長度3個字節
      ············V{ Value值{
      159 01 ················
      160 00 ················ 3字節的公有冪值exponent
      161 01 ················
      ············} }
      ········} }
      ····} }
      } }

      解析完的公鑰對象的結構形式如下:

      SEQUENCE{ // 159個字節
          SEQUENCE{ // 13個字節
              OBJECT IDENTIFIER{ // 9個字節
              }
          }
          BIT STRING{ // 141個字節
              SEQUENCE{ // 137個字節
                  INTEGER{ // 129個字節
                      補0 // 1個字節
                      modulus // 128個字節,模數,即N
                  }
                  INTEGER{
                      exponent // 3個字節,公有冪值,即E
                  }
              }
          }
      }
      

      至此我們已經從DER密鑰格式的十六進制串中解析出了模數modulus和公有冪exponent,即公鑰內容。

      解出了公鑰的內容,要向其它幾種密鑰格式轉換,只需要按照對應的格式規則進行處理即可,很多語言都有現呈的類可以進行構造(C#中提供RSACryptoServiceProvider類)。例子中是公鑰的解析,在RSA算法中,私鑰對象的結構要比公鑰多幾個字段,但解析方式是一樣的,同樣遵循ASN.1規范。

      4 源代碼

      最后貼上客戶端的C#源碼,從DER格式的公鑰中解出模數和公有冪:

      
      /**
       * 從DER格式的公鑰字符串解析出modulus和exponent
       */
      internal static KeyValuePair<byte[], byte[]> DecodeHexPublicKey(string hexPublicKey)
      {
          try
          {
              byte[] keyBytes = HexStringToByte(hexPublicKey); // 字符串轉成十六進制字節數組
              if (keyBytes[0] != 0x30) // 第一個字節的文件頭必須解析成SEQUENCE結構
              {
                  throw new Exception("head of file cannot decode to SEQUENCE");
              }
      
              // 解析主結構體(SEQUENCE結構體)
              int mainSequenceLength = 0;
              int currentIndex = 1; // 從第二個字節開始解析
              mainSequenceLength = DecodeTLV_Length(keyBytes, ref currentIndex); // 獲取主SEQUENCE結構體的長度
      
              // 解析inner(SEQUENCE結構體)
              int innerSequenceLength = 15; // inner的固定長度15
              currentIndex += innerSequenceLength; // 跳過即可
      
              // 解析bitString(Bit String類型)
              currentIndex++; // 下標跳過Bit String數據的Tag字段,移向Length字段
              int bitStringLength = DecodeTLV_Length(keyBytes, ref currentIndex); // 獲取Bit String數據的長度
              currentIndex++; // Bit String的Value字段帶一個unused bit,跳過
      
              // 解析參數結構體(SEQUENCE結構體)
              currentIndex++; // 下標跳過參數結構體的Tag字段,移向Length字段
              int paramSequenceLength = DecodeTLV_Length(keyBytes, ref currentIndex); // 獲取參數SEQUENCE結構體的長度
      
              // 解析modulus模數N
              byte[] modulus = DecodeInteger(keyBytes, ref currentIndex);
      
              // 解析exponent公開冪E
              byte[] exponent = DecodeInteger(keyBytes, ref currentIndex);
      
              // 公鑰(E,N)
              KeyValuePair<byte[], byte[]> result = new KeyValuePair<byte[], byte[]>(modulus, exponent);
      
              return result;
          }
          catch (Exception ex)
          {
              Console.WriteLine(ex.Message);
              return new KeyValuePair<byte[], byte[]>();
          }
      }
      
      /**
       * 字符串以十六進制的規則轉為字節數組
       */
      private static byte[] HexStringToByte(string hexString)
      {
          hexString = hexString.Replace(" ", "");
          if ((hexString.Length % 2) != 0)
          {
              hexString += " ";
          }
      
          byte[] returnBytes = new byte[hexString.Length / 2];
          for (int i = 0; i < returnBytes.Length; i++)
          {
              returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
          }
      
          return returnBytes;
      }
      
      /**
       * 解碼ASN.1規則中的INTEGER類型,返回解碼后的數據,處理完成后currentIndex的位置會移到該類型數據末尾的下一個位置
       * @param data 密鑰字節數組(十六進制)
       * @param currentIndex 表示當前需要解碼的Integer的Tag字段的位置
       */
      private static byte[] DecodeInteger(byte[] dataInteger, ref int currentIndex)
      {
          if (dataInteger[currentIndex] != 0x02)
          {
              throw new Exception("head of file cannot decode to INTEGER");
          }
          currentIndex += 1;
          var len = DecodeTLV_Length(dataInteger, ref currentIndex);
          int integerStart = currentIndex;
          int integerEnd = currentIndex + len - 1;
      
          // 跳過補零位,>0x7f時補0
          while (dataInteger[currentIndex] == 0)
          {
              currentIndex++;
          }
          if (dataInteger[currentIndex] > 0x7f)
          {
              integerStart = currentIndex;
          }
      
          byte[] result = dataInteger.Where((item, index) => index >= integerStart & index <= integerEnd).ToArray();
      
          currentIndex = integerEnd + 1; // 處理完成后currentIndex移到末尾的下一個位置
          return result;
      }
      
      /**
       * 解碼TLV元組中的Length字節,返回實際長度,返回0表示錯誤,處理完成后currentIndex的位置會移到Length字段末尾的下一個位置
       * @param data 密鑰字節數組(十六進制)
       * @param index 表示當前需要解碼的TLV元組中的Length字段的位置
       */
      private static int DecodeTLV_Length(byte[] data, ref int currentIndex)
      {
          try
          {
              int length = data[currentIndex]; // 獲取當前字節,十進制表示
              if (length == 0) // 解碼不能為:0000 0000,拋出異常
              {
                  throw new Exception("parameter length in TLV cannot be 0x00");
              }
              else if(length < 0x80) // 解碼為:0xxx xxxx,定長式、短形式:最高位為0,后7位表示長度的實際值
              {
                  ++currentIndex; // 處理完成后currentIndex移到末尾的下一個位置
                  return length;
              }
              else if(length == 0x80) // 解碼為:1000 0000,不定長式:以兩個連續的0x00結尾
              {
                  int result = 0;
                  int count = 0; // 連續遇到0x00的次數
                  while (count != 2)
                  {
                      ++currentIndex; // 獲取下一個字節
                      int temp = data[currentIndex];
                      if (temp == 0x00) // 遇到0x00
                      {
                          ++count;
                      }
                      else
                      {
                          count = 0;
                      }
                      ++result;
                  }
      
                  ++currentIndex; // 處理完成后currentIndex移到末尾的下一個位置
                  return result;
              }
              else // 解碼為:1xxx xxxx,定長式、長形式:最高位為1,后7位表示長度的實際值會占用接下的多少個字節數
              {
                  length &= 0x7F; // 當前值和“0111 1111”進行按位與,提取出length后7位的1,后7位的十進制表示長度的實際值占用的字節數
                  int result = 0;
                  for (int i = length - 1; i >= 0; i--)
                  {
                      ++currentIndex; // 獲取下一個字節
                      int temp = data[currentIndex] << (i * 8); // 計算當前字節所在的位置表示的值,每多一個字節需要左移八位
                      result += temp;
                  }
      
                  ++currentIndex; // 處理完成后currentIndex移到末尾的下一個位置
                  return result;
              }
          }
          catch (Exception ex)
          {
              Console.WriteLine(ex.Message);
              ++currentIndex; // 處理完成后currentIndex移到末尾的下一個位置
              return 0;
          }
      }
      

      特別注意:如果場景涉及大字節序和小字節序(Big-Endian與Little-Endian)的轉換,需要額外處理。

      總結

      ??RSA算法本身的原理并不復雜,其中的密鑰解析依賴于密鑰的格式規范,各種密鑰格式的互轉都可以先轉換成原始的密鑰內容結構體對象,再按照不同的密鑰格式規范進行組裝,期間可能會涉及到base64字符串的轉換、不同進制的轉換、大小字節序的轉換等問題,本文提供了對十六進制DER格式公鑰解析的具體解決方案。

      posted @ 2021-06-16 01:21  seedoubleu  閱讀(1576)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 窝窝午夜色视频国产精品破 | 99精品国产一区二区三区不卡 | 国产午夜三级一区二区三| 特级做a爰片毛片免费看无码| 精品嫩模福利一区二区蜜臀| 又粗又大又黄又硬又爽免费看| 又粗又硬又黄a级毛片| 亚洲精品国产精品乱码不| 国产地址二永久伊甸园| 激情综合色综合啪啪五月| 乱码精品一区二区三区| 久久亚洲精品情侣| 亚洲熟女少妇乱色一区二区| 亚洲欭美日韩颜射在线二| 国产精品夫妇激情啪发布| 久久人人97超碰精品| 99久久激情国产精品| 亚洲国产一区二区三区久| 中文字幕av一区二区三区| 国产亚洲精品综合99久久| 日本阿v片在线播放免费| 在线亚洲人成电影网站色www| 亚洲AV成人无码久久精品四虎| 亚洲中文字幕精品久久久久久动漫 | 亚洲综合成人一区二区三区| WWW丫丫国产成人精品| 激情综合网一区二区三区| 国产丰满乱子伦无码专区| 固原市| 久久精品av国产一区二区| 国产精品一区二区三区蜜臀| 无码免费大香伊蕉在人线国产| 国产精品一码二码三码| 国产精品中文字幕av| 精品视频一区二区三区不卡| 好吊妞视频这里有精品| 国产精品亚洲а∨天堂2021| 亚洲色无码播放亚洲成av| 97精品人妻系列无码人妻| 国产精品中文字幕在线| 黄骅市|