2013-10-14 51 views
3

有沒有辦法以編程方式將Jsch生成的SSH RSA密鑰轉換爲可用於加密的格式javax.crypto.Cipher?我大多看到了類似的答案this將openSSH rsa密鑰轉換爲javax.crypto.Cipher兼容格式

openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key_file -nocrypt > pkcs8_key 

但是我沒有存取權限OpenSSL的或shell命令。順便說一句:我只使用JDK6。

感謝@erickson對他的幫助,我可以從轉換RFC4716的公共密鑰使用的指數和模量BigInteger類型傳遞給KeyFactoryRSAPublicKeySpec一個Java公鑰。他的解決方案如下。

現在我試圖轉換私鑰。下面是產生使用writePrivateKey(str filename)示例privateJsch.key:使用openssl asn1parse -in privateJsch.key -out privateJsch.der一代產量

-----BEGIN RSA PRIVATE KEY----- 
MIICXAIBAAKBgQC0ouLgTjmKjHU6UjNSL8HyTIdFM1UdVpgU81paWKreN8L36YoT 
goZQHeyyUCCHmq3r3cKaySyu93mHBY0l76qSAIRZgE1IAFkBhNWBdlJ9UYA9HXm/ 
MqTQHbpqz0EYGE9TsFHS8dn1/utsJxKSWZ4xPNYjfS4Ps6G84iRwfdrIbQIDAQAB 
AoGAKv3xnY1AqLcRV5Yk3NS9Blwsfc3f3iG0BJh+0q3zzPvcjYCp+kbAjOTyZuYn 
N98asd6P6KMk3WfNJtOtanAGWl46bmtzNsQtSr5rVQEgs2w8i2yJcwVAYf2Td4qX 
m3dH+roJA/CEFRSDat4sUfjOVmsYQXIBa0W2XTpp+7T1U4ECQQD1wSR6iTz7Bja0 
MPcizDbRTRQHALBf7E8j8YOLpN/IGSox9pT+ktjsI2vMaD+b3SM4s0FD8quBlppE 
o5FAguHxAkEAvCrCK7eZU3H+Ul1iw9Kd3WPHjDvQcdT5rEL+NSYEZyHgU7ipXEih 
UHvK47Bkte/PVIu3jBFBnMujA0XiT0gSPQJBAI3+8j/nChgU6AjHfhRaIJZgzeCZ 
8k8KcFPZWWOXeUHZ4HqL+lz5pmMSuFecKJy7cn1xfZVwIs62oR5l0CiRN1ECQCui 
CqaSi3ZjH6M/znA0PbEhuxsUn7BVv5OncUUnzKuRmnAviO5CVU3Rdum3dJMPydcE 
Ewri0YEnY2SV5vWVc80CQH43uBbshz7ju3DdVykHFrRElQB+f0YMK3Ad7eu+us0w 
dLrOOoXP0T60B/bMTo8rdMa6XU/0w/w8FsOqoxNY23U= 
-----END RSA PRIVATE KEY----- 

$ openssl asn1parse -in privateJsch.key -out privateJsch.der 
    0:d=0 hl=4 l= 604 cons: SEQUENCE 
    4:d=1 hl=2 l= 1 prim: INTEGER   :00 
    7:d=1 hl=3 l= 129 prim: INTEGER   :B4A2E2E04E398A8C753A5233522FC1F24C874533551D569814F35A5A58AADE37C2F7E98A138286501DECB25020879AADEBDDC29AC92CAEF77987058D25EFAA92008459804D4800590184D58176527D51803D1D79BF32A4D01DBA6ACF4118184F53B051D2F1D9F5FEEB6C271292599E313CD6237D2E0FB3A1BCE224707DDAC86D 
    139:d=1 hl=2 l= 3 prim: INTEGER   :010001 
    144:d=1 hl=3 l= 128 prim: INTEGER   :2AFDF19D8D40A8B711579624DCD4BD065C2C7DCDDFDE21B404987ED2ADF3CCFBDC8D80A9FA46C08CE4F266E62737DF1AB1DE8FE8A324DD67CD26D3AD6A70065A5E3A6E6B7336C42D4ABE6B550120B36C3C8B6C8973054061FD93778A979B7747FABA0903F0841514836ADE2C51F8CE566B184172016B45B65D3A69FBB4F55381 
    275:d=1 hl=2 l= 65 prim: INTEGER   :F5C1247A893CFB0636B430F722CC36D14D140700B05FEC4F23F1838BA4DFC8192A31F694FE92D8EC236BCC683F9BDD2338B34143F2AB81969A44A3914082E1F1 
    342:d=1 hl=2 l= 65 prim: INTEGER   :BC2AC22BB7995371FE525D62C3D29DDD63C78C3BD071D4F9AC42FE3526046721E053B8A95C48A1507BCAE3B064B5EFCF548BB78C11419CCBA30345E24F48123D 
    409:d=1 hl=2 l= 65 prim: INTEGER   :8DFEF23FE70A1814E808C77E145A209660CDE099F24F0A7053D95963977941D9E07A8BFA5CF9A66312B8579C289CBB727D717D957022CEB6A11E65D028913751 
    476:d=1 hl=2 l= 64 prim: INTEGER   :2BA20AA6928B76631FA33FCE70343DB121BB1B149FB055BF93A7714527CCAB919A702F88EE42554DD176E9B774930FC9D704130AE2D18127636495E6F59573CD 
    542:d=1 hl=2 l= 64 prim: INTEGER   :7E37B816EC873EE3BB70DD57290716B44495007E7F460C2B701DEDEBBEBACD3074BACE3A85CFD13EB407F6CC4E8F2B74C6BA5D4FF4C3FC3C16C3AAA31358DB75 

BASE64的關鍵部分進行解碼單產六角細分爲ASN.1 PKCS#下列1部件(參見RFC3447)只有,其他將遵循模式不同字節數:

30:82: x3082 == ASN.1 Sequence 
02:5C: Key Length == 604 bytes 
02:01: x02 == ASN.1 integer, Value Length == 1 byte 
00:  Version == 0 
02:81:81: x02 == ASN.1 integer, Modulus Length == 129 bytes 
00:B4:A2:E2:E0:4E:39:8A:8C:75:3A:52:33:52:2F:C1: 
F2:4C:87:45:33:55:1D:56:98:14:F3:5A:5A:58:AA:DE: 
37:C2:F7:E9:8A:13:82:86:50:1D:EC:B2:50:20:87:9A: 
AD:EB:DD:C2:9A:C9:2C:AE:F7:79:87:05:8D:25:EF:AA: 
92:00:84:59:80:4D:48:00:59:01:84:D5:81:76:52:7D: 
51:80:3D:1D:79:BF:32:A4:D0:1D:BA:6A:CF:41:18:18: 
4F:53:B0:51:D2:F1:D9:F5:FE:EB:6C:27:12:92:59:9E: 
31:3C:D6:23:7D:2E:0F:B3:A1:BC:E2:24:70:7D:DA:C8: 
6D:  Modulus 
02:03: x02 == ASN.1 integer, Value Length == 3 bytes 
01:00:01: Public Exponent 
02:81:80: x02 == ASN.1 integer, Value Length == 128 bytes 
2A:FD:F1:9D:8D:40:A8:B7:11:57:96:24:DC:D4:BD:06: 
5C:2C:7D:CD:DF:DE:21:B4:04:98:7E:D2:AD:F3:CC:FB: 
DC:8D:80:A9:FA:46:C0:8C:E4:F2:66:E6:27:37:DF:1A: 
B1:DE:8F:E8:A3:24:DD:67:CD:26:D3:AD:6A:70:06:5A: 
5E:3A:6E:6B:73:36:C4:2D:4A:BE:6B:55:01:20:B3:6C: 
3C:8B:6C:89:73:05:40:61:FD:93:77:8A:97:9B:77:47: 
FA:BA:09:03:F0:84:15:14:83:6A:DE:2C:51:F8:CE:56: 
6B:18:41:72:01:6B:45:B6:5D:3A:69:FB:B4:F5:53:81: 
      Private Exponent 
02:41: x02 == ASN.1 integer, Value Length == 65 bytes 
00:F5:C1:24:7A:89:3C:FB:06:36:B4:30:F7:22:CC:36: 
D1:4D:14:07:00:B0:5F:EC:4F:23:F1:83:8B:A4:DF:C8: 
19:2A:31:F6:94:FE:92:D8:EC:23:6B:CC:68:3F:9B:DD: 
23:38:B3:41:43:F2:AB:81:96:9A:44:A3:91:40:82:E1: 
F1:  Prime P 
02:41: x02 == ASN.1 integer, Value Length == 65 bytes 
00:BC:2A:C2:2B:B7:99:53:71:FE:52:5D:62:C3:D2:9D: 
DD:63:C7:8C:3B:D0:71:D4:F9:AC:42:FE:35:26:04:67: 
21:E0:53:B8:A9:5C:48:A1:50:7B:CA:E3:B0:64:B5:EF: 
CF:54:8B:B7:8C:11:41:9C:CB:A3:03:45:E2:4F:48:12: 
3D:  Prime Q 
02:41: x02 == ASN.1 integer, Value Length == 65 bytes 
00:8D:FE:F2:3F:E7:0A:18:14:E8:08:C7:7E:14:5A:20: 
96:60:CD:E0:99:F2:4F:0A:70:53:D9:59:63:97:79:41: 
D9:E0:7A:8B:FA:5C:F9:A6:63:12:B8:57:9C:28:9C:BB: 
72:7D:71:7D:95:70:22:CE:B6:A1:1E:65:D0:28:91:37: 
51:  Prime P Exponent 
02:40: x02 == ASN.1 integer, Value Length == 64 bytes 
2B:A2:0A:A6:92:8B:76:63:1F:A3:3F:CE:70:34:3D:B1: 
21:BB:1B:14:9F:B0:55:BF:93:A7:71:45:27:CC:AB:91: 
9A:70:2F:88:EE:42:55:4D:D1:76:E9:B7:74:93:0F:C9: 
D7:04:13:0A:E2:D1:81:27:63:64:95:E6:F5:95:73:CD: 
      Prime Q Exponent 
02:40: x02 == ASN.1 integer, Value Length == 64 bytes 
7E:37:B8:16:EC:87:3E:E3:BB:70:DD:57:29:07:16:B4: 
44:95:00:7E:7F:46:0C:2B:70:1D:ED:EB:BE:BA:CD:30: 
74:BA:CE:3A:85:CF:D1:3E:B4:07:F6:CC:4E:8F:2B:74: 
C6:BA:5D:4F:F4:C3:FC:3C:16:C3:AA:A3:13:58:DB:75 
      CRT Coefficient 

類似的帖子:

引用:

+0

您可以使用[BouncyCastle的庫] (http://bouncycastle.org/java.html)? –

+0

我可以,但我正在努力減少額外的庫,包和jar文件。 –

+0

Jsch包含一個'KeyPairRSA'類,它包含RSA密鑰的寫入和解析。唯一的問題是不包含許多獲得者。否則,您可以簡單地將底層字節數組字段轉換爲* positive * BigInteger值並使用簡單的'RSAPrivateKeySpec'或'RSAPrivateCrtKeySpec'和''RSA「''KeyFactory'(抱歉,沒時間完整回答) –

回答

2
static KeyPair demo(InputStream pub, InputStream pvt) 
    throws IOException, GeneralSecurityException 
    { 
    KeyFactory f = KeyFactory.getInstance("RSA"); 

    RSAPublicKeySpec pubspec = decodeRSAPublicSSH(readAllBase64Bytes(pub)); 
    RSAPrivateCrtKeySpec pvtspec = decodeRSAPrivatePKCS1(readAllBase64Bytes(pvt)); 

    return new KeyPair(f.generatePublic(pubspec), f.generatePrivate(pvtspec)); 
    } 

    static RSAPublicKeySpec decodeRSAPublicSSH(byte[] encoded) 
    { 
    ByteBuffer input = ByteBuffer.wrap(encoded); 
    String type = string(input); 
    if (!"ssh-rsa".equals(type)) 
     throw new IllegalArgumentException("Unsupported type"); 
    BigInteger exp = sshint(input); 
    BigInteger mod = sshint(input); 
    if (input.hasRemaining()) 
     throw new IllegalArgumentException("Excess data"); 
    return new RSAPublicKeySpec(mod, exp); 
    } 

    static RSAPrivateCrtKeySpec decodeRSAPrivatePKCS1(byte[] encoded) 
    { 
    ByteBuffer input = ByteBuffer.wrap(encoded); 
    if (der(input, 0x30) != input.remaining()) 
     throw new IllegalArgumentException("Excess data"); 
    if (!BigInteger.ZERO.equals(derint(input))) 
     throw new IllegalArgumentException("Unsupported version"); 
    BigInteger n = derint(input); 
    BigInteger e = derint(input); 
    BigInteger d = derint(input); 
    BigInteger p = derint(input); 
    BigInteger q = derint(input); 
    BigInteger ep = derint(input); 
    BigInteger eq = derint(input); 
    BigInteger c = derint(input); 
    return new RSAPrivateCrtKeySpec(n, e, d, p, q, ep, eq, c); 
    } 

    private static String string(ByteBuffer buf) 
    { 
    return new String(lenval(buf), Charset.forName("US-ASCII")); 
    } 

    private static BigInteger sshint(ByteBuffer buf) 
    { 
    return new BigInteger(+1, lenval(buf)); 
    } 

    private static byte[] lenval(ByteBuffer buf) 
    { 
    int len = buf.getInt(); 
    byte[] copy = new byte[len]; 
    buf.get(copy); 
    return copy; 
    } 

    private static BigInteger derint(ByteBuffer input) 
    { 
    byte[] value = new byte[der(input, 0x02)]; 
    input.get(value); 
    return new BigInteger(+1, value); 
    } 

    private static int der(ByteBuffer input, int exp) 
    { 
    int tag = input.get() & 0xFF; 
    if (tag != exp) 
     throw new IllegalArgumentException("Unexpected tag"); 
    int n = input.get() & 0xFF; 
    if (n < 128) 
     return n; 
    n &= 0x7F; 
    if ((n < 1) || (n > 2)) 
     throw new IllegalArgumentException("Invalid length"); 
    int len = 0; 
    while (n-- > 0) { 
     len <<= 8; 
     len |= input.get() & 0xFF; 
    } 
    return len; 
    } 

    private static byte[] readAllBase64Bytes(InputStream input) 
    { 
    StringBuilder buf = new StringBuilder(); 
    Scanner scanner = new Scanner(input, "US-ASCII"); 
    while (scanner.hasNextLine()) { 
     String line = scanner.nextLine().trim(); 
     if (!line.startsWith("-----")) 
     buf.append(line); 
    } 
    return DatatypeConverter.parseBase64Binary(buf.toString()); 
    } 
+0

這似乎工作,因爲我得到「指數爲65537&1024位長模數的」1024位Sun RSA公共密鑰,但我沒有對私鑰進行檢查,必須去掉頭/頁腳(順便說一下,我不能使用jdk7,只能使用jdk6,所以我必須像java.nio.Files一樣解決一些jdk7庫問題。)**在模數之後還剩下13個字節,它們不是文本,因爲它們是base64字符串,他們讀的是「bRDQ0kh9j1ASyAihGA ==」。**你知道這些字節是用於什麼嗎?** –

+0

@MarkMikofski在模數之後沒有額外的數據時我使用你在問題中提供的示例,在你的評論中,base64編碼數據的片段不會出現在你提供的示例中,如果沒有完整的示例,我無法猜測出問題是什麼。 – erickson

+0

It works,thanks!I將它與使用openssl從SSH密鑰生成的pem公鑰進行比較,並且該十六進制與Java密鑰相同!但是,一路上的錯誤。 (1)當我讀它時,我解碼每一行。我使用的是jdk6,因此我使用'BufferedReader.readLine'來省略換行符,但是在編碼之前必須先讀取整個字符串。 (2)出於某種原因,將每行的字節存儲在一個字節數組中,然後解碼,也沒有工作,我不得不使用StringBuilder。 (3)Stoopid me,我沒有刪除最後一行RFC4716'---- END SSH2 PUBLIC KEY ----' –

相關問題