]>
Commit | Line | Data |
---|---|---|
1 | /* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ | |
2 | /* | |
3 | Copyright (c) 2002-2010 ymnk, JCraft,Inc. All rights reserved. | |
4 | ||
5 | Redistribution and use in source and binary forms, with or without | |
6 | modification, are permitted provided that the following conditions are met: | |
7 | ||
8 | 1. Redistributions of source code must retain the above copyright notice, | |
9 | this list of conditions and the following disclaimer. | |
10 | ||
11 | 2. Redistributions in binary form must reproduce the above copyright | |
12 | notice, this list of conditions and the following disclaimer in | |
13 | the documentation and/or other materials provided with the distribution. | |
14 | ||
15 | 3. The names of the authors may not be used to endorse or promote products | |
16 | derived from this software without specific prior written permission. | |
17 | ||
18 | THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, | |
19 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | |
20 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, | |
21 | INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, | |
22 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, | |
24 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
26 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | |
27 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | ||
30 | package com.jcraft.jsch; | |
31 | ||
32 | import java.io.FileOutputStream; | |
33 | import java.io.FileInputStream; | |
34 | import java.io.File; | |
35 | ||
36 | public abstract class KeyPair{ | |
37 | public static final int ERROR=0; | |
38 | public static final int DSA=1; | |
39 | public static final int RSA=2; | |
40 | public static final int UNKNOWN=3; | |
41 | ||
42 | static final int VENDOR_OPENSSH=0; | |
43 | static final int VENDOR_FSECURE=1; | |
44 | int vendor=VENDOR_OPENSSH; | |
45 | ||
46 | private static final byte[] cr=Util.str2byte("\n"); | |
47 | ||
48 | public static KeyPair genKeyPair(JSch jsch, int type) throws JSchException{ | |
49 | return genKeyPair(jsch, type, 1024); | |
50 | } | |
51 | public static KeyPair genKeyPair(JSch jsch, int type, int key_size) throws JSchException{ | |
52 | KeyPair kpair=null; | |
53 | if(type==DSA){ kpair=new KeyPairDSA(jsch); } | |
54 | else if(type==RSA){ kpair=new KeyPairRSA(jsch); } | |
55 | if(kpair!=null){ | |
56 | kpair.generate(key_size); | |
57 | } | |
58 | return kpair; | |
59 | } | |
60 | ||
61 | abstract void generate(int key_size) throws JSchException; | |
62 | ||
63 | abstract byte[] getBegin(); | |
64 | abstract byte[] getEnd(); | |
65 | abstract int getKeySize(); | |
66 | ||
67 | JSch jsch=null; | |
68 | private Cipher cipher; | |
69 | private HASH hash; | |
70 | private Random random; | |
71 | ||
72 | private byte[] passphrase; | |
73 | ||
74 | public KeyPair(JSch jsch){ | |
75 | this.jsch=jsch; | |
76 | } | |
77 | ||
78 | static byte[][] header={Util.str2byte("Proc-Type: 4,ENCRYPTED"), | |
79 | Util.str2byte("DEK-Info: DES-EDE3-CBC,")}; | |
80 | ||
81 | abstract byte[] getPrivateKey(); | |
82 | ||
83 | public void writePrivateKey(java.io.OutputStream out){ | |
84 | byte[] plain=getPrivateKey(); | |
85 | byte[][] _iv=new byte[1][]; | |
86 | byte[] encoded=encrypt(plain, _iv); | |
87 | if(encoded!=plain) | |
88 | Util.bzero(plain); | |
89 | byte[] iv=_iv[0]; | |
90 | byte[] prv=Util.toBase64(encoded, 0, encoded.length); | |
91 | ||
92 | try{ | |
93 | out.write(getBegin()); out.write(cr); | |
94 | if(passphrase!=null){ | |
95 | out.write(header[0]); out.write(cr); | |
96 | out.write(header[1]); | |
97 | for(int i=0; i<iv.length; i++){ | |
98 | out.write(b2a((byte)((iv[i]>>>4)&0x0f))); | |
99 | out.write(b2a((byte)(iv[i]&0x0f))); | |
100 | } | |
101 | out.write(cr); | |
102 | out.write(cr); | |
103 | } | |
104 | int i=0; | |
105 | while(i<prv.length){ | |
106 | if(i+64<prv.length){ | |
107 | out.write(prv, i, 64); | |
108 | out.write(cr); | |
109 | i+=64; | |
110 | continue; | |
111 | } | |
112 | out.write(prv, i, prv.length-i); | |
113 | out.write(cr); | |
114 | break; | |
115 | } | |
116 | out.write(getEnd()); out.write(cr); | |
117 | //out.close(); | |
118 | } | |
119 | catch(Exception e){ | |
120 | } | |
121 | } | |
122 | ||
123 | private static byte[] space=Util.str2byte(" "); | |
124 | ||
125 | abstract byte[] getKeyTypeName(); | |
126 | public abstract int getKeyType(); | |
127 | ||
128 | public byte[] getPublicKeyBlob(){ return publickeyblob; } | |
129 | ||
130 | public void writePublicKey(java.io.OutputStream out, String comment){ | |
131 | byte[] pubblob=getPublicKeyBlob(); | |
132 | byte[] pub=Util.toBase64(pubblob, 0, pubblob.length); | |
133 | try{ | |
134 | out.write(getKeyTypeName()); out.write(space); | |
135 | out.write(pub, 0, pub.length); out.write(space); | |
136 | out.write(Util.str2byte(comment)); | |
137 | out.write(cr); | |
138 | } | |
139 | catch(Exception e){ | |
140 | } | |
141 | } | |
142 | ||
143 | public void writePublicKey(String name, String comment) throws java.io.FileNotFoundException, java.io.IOException{ | |
144 | FileOutputStream fos=new FileOutputStream(name); | |
145 | writePublicKey(fos, comment); | |
146 | fos.close(); | |
147 | } | |
148 | ||
149 | public void writeSECSHPublicKey(java.io.OutputStream out, String comment){ | |
150 | byte[] pubblob=getPublicKeyBlob(); | |
151 | byte[] pub=Util.toBase64(pubblob, 0, pubblob.length); | |
152 | try{ | |
153 | out.write(Util.str2byte("---- BEGIN SSH2 PUBLIC KEY ----")); out.write(cr); | |
154 | out.write(Util.str2byte("Comment: \""+comment+"\"")); out.write(cr); | |
155 | int index=0; | |
156 | while(index<pub.length){ | |
157 | int len=70; | |
158 | if((pub.length-index)<len)len=pub.length-index; | |
159 | out.write(pub, index, len); out.write(cr); | |
160 | index+=len; | |
161 | } | |
162 | out.write(Util.str2byte("---- END SSH2 PUBLIC KEY ----")); out.write(cr); | |
163 | } | |
164 | catch(Exception e){ | |
165 | } | |
166 | } | |
167 | ||
168 | public void writeSECSHPublicKey(String name, String comment) throws java.io.FileNotFoundException, java.io.IOException{ | |
169 | FileOutputStream fos=new FileOutputStream(name); | |
170 | writeSECSHPublicKey(fos, comment); | |
171 | fos.close(); | |
172 | } | |
173 | ||
174 | ||
175 | public void writePrivateKey(String name) throws java.io.FileNotFoundException, java.io.IOException{ | |
176 | FileOutputStream fos=new FileOutputStream(name); | |
177 | writePrivateKey(fos); | |
178 | fos.close(); | |
179 | } | |
180 | ||
181 | public String getFingerPrint(){ | |
182 | if(hash==null) hash=genHash(); | |
183 | byte[] kblob=getPublicKeyBlob(); | |
184 | if(kblob==null) return null; | |
185 | return getKeySize()+" "+Util.getFingerPrint(hash, kblob); | |
186 | } | |
187 | ||
188 | private byte[] encrypt(byte[] plain, byte[][] _iv){ | |
189 | if(passphrase==null) return plain; | |
190 | ||
191 | if(cipher==null) cipher=genCipher(); | |
192 | byte[] iv=_iv[0]=new byte[cipher.getIVSize()]; | |
193 | ||
194 | if(random==null) random=genRandom(); | |
195 | random.fill(iv, 0, iv.length); | |
196 | ||
197 | byte[] key=genKey(passphrase, iv); | |
198 | byte[] encoded=plain; | |
199 | ||
200 | // PKCS#5Padding | |
201 | { | |
202 | //int bsize=cipher.getBlockSize(); | |
203 | int bsize=cipher.getIVSize(); | |
204 | byte[] foo=new byte[(encoded.length/bsize+1)*bsize]; | |
205 | System.arraycopy(encoded, 0, foo, 0, encoded.length); | |
206 | int padding=bsize-encoded.length%bsize; | |
207 | for(int i=foo.length-1; (foo.length-padding)<=i; i--){ | |
208 | foo[i]=(byte)padding; | |
209 | } | |
210 | encoded=foo; | |
211 | } | |
212 | ||
213 | try{ | |
214 | cipher.init(Cipher.ENCRYPT_MODE, key, iv); | |
215 | cipher.update(encoded, 0, encoded.length, encoded, 0); | |
216 | } | |
217 | catch(Exception e){ | |
218 | //System.err.println(e); | |
219 | } | |
220 | Util.bzero(key); | |
221 | return encoded; | |
222 | } | |
223 | ||
224 | abstract boolean parse(byte[] data); | |
225 | ||
226 | private byte[] decrypt(byte[] data, byte[] passphrase, byte[] iv){ | |
227 | /* | |
228 | if(iv==null){ // FSecure | |
229 | iv=new byte[8]; | |
230 | for(int i=0; i<iv.length; i++)iv[i]=0; | |
231 | } | |
232 | */ | |
233 | try{ | |
234 | byte[] key=genKey(passphrase, iv); | |
235 | cipher.init(Cipher.DECRYPT_MODE, key, iv); | |
236 | Util.bzero(key); | |
237 | byte[] plain=new byte[data.length]; | |
238 | cipher.update(data, 0, data.length, plain, 0); | |
239 | return plain; | |
240 | } | |
241 | catch(Exception e){ | |
242 | //System.err.println(e); | |
243 | } | |
244 | return null; | |
245 | } | |
246 | ||
247 | int writeSEQUENCE(byte[] buf, int index, int len){ | |
248 | buf[index++]=0x30; | |
249 | index=writeLength(buf, index, len); | |
250 | return index; | |
251 | } | |
252 | int writeINTEGER(byte[] buf, int index, byte[] data){ | |
253 | buf[index++]=0x02; | |
254 | index=writeLength(buf, index, data.length); | |
255 | System.arraycopy(data, 0, buf, index, data.length); | |
256 | index+=data.length; | |
257 | return index; | |
258 | } | |
259 | ||
260 | int countLength(int len){ | |
261 | int i=1; | |
262 | if(len<=0x7f) return i; | |
263 | while(len>0){ | |
264 | len>>>=8; | |
265 | i++; | |
266 | } | |
267 | return i; | |
268 | } | |
269 | ||
270 | int writeLength(byte[] data, int index, int len){ | |
271 | int i=countLength(len)-1; | |
272 | if(i==0){ | |
273 | data[index++]=(byte)len; | |
274 | return index; | |
275 | } | |
276 | data[index++]=(byte)(0x80|i); | |
277 | int j=index+i; | |
278 | while(i>0){ | |
279 | data[index+i-1]=(byte)(len&0xff); | |
280 | len>>>=8; | |
281 | i--; | |
282 | } | |
283 | return j; | |
284 | } | |
285 | ||
286 | private Random genRandom(){ | |
287 | if(random==null){ | |
288 | try{ | |
289 | Class c=Class.forName(jsch.getConfig("random")); | |
290 | random=(Random)(c.newInstance()); | |
291 | } | |
292 | catch(Exception e){ System.err.println("connect: random "+e); } | |
293 | } | |
294 | return random; | |
295 | } | |
296 | ||
297 | private HASH genHash(){ | |
298 | try{ | |
299 | Class c=Class.forName(jsch.getConfig("md5")); | |
300 | hash=(HASH)(c.newInstance()); | |
301 | hash.init(); | |
302 | } | |
303 | catch(Exception e){ | |
304 | } | |
305 | return hash; | |
306 | } | |
307 | private Cipher genCipher(){ | |
308 | try{ | |
309 | Class c; | |
310 | c=Class.forName(jsch.getConfig("3des-cbc")); | |
311 | cipher=(Cipher)(c.newInstance()); | |
312 | } | |
313 | catch(Exception e){ | |
314 | } | |
315 | return cipher; | |
316 | } | |
317 | ||
318 | /* | |
319 | hash is MD5 | |
320 | h(0) <- hash(passphrase, iv); | |
321 | h(n) <- hash(h(n-1), passphrase, iv); | |
322 | key <- (h(0),...,h(n))[0,..,key.length]; | |
323 | */ | |
324 | synchronized byte[] genKey(byte[] passphrase, byte[] iv){ | |
325 | if(cipher==null) cipher=genCipher(); | |
326 | if(hash==null) hash=genHash(); | |
327 | ||
328 | byte[] key=new byte[cipher.getBlockSize()]; | |
329 | int hsize=hash.getBlockSize(); | |
330 | byte[] hn=new byte[key.length/hsize*hsize+ | |
331 | (key.length%hsize==0?0:hsize)]; | |
332 | try{ | |
333 | byte[] tmp=null; | |
334 | if(vendor==VENDOR_OPENSSH){ | |
335 | for(int index=0; index+hsize<=hn.length;){ | |
336 | if(tmp!=null){ hash.update(tmp, 0, tmp.length); } | |
337 | hash.update(passphrase, 0, passphrase.length); | |
338 | hash.update(iv, 0, iv.length); | |
339 | tmp=hash.digest(); | |
340 | System.arraycopy(tmp, 0, hn, index, tmp.length); | |
341 | index+=tmp.length; | |
342 | } | |
343 | System.arraycopy(hn, 0, key, 0, key.length); | |
344 | } | |
345 | else if(vendor==VENDOR_FSECURE){ | |
346 | for(int index=0; index+hsize<=hn.length;){ | |
347 | if(tmp!=null){ hash.update(tmp, 0, tmp.length); } | |
348 | hash.update(passphrase, 0, passphrase.length); | |
349 | tmp=hash.digest(); | |
350 | System.arraycopy(tmp, 0, hn, index, tmp.length); | |
351 | index+=tmp.length; | |
352 | } | |
353 | System.arraycopy(hn, 0, key, 0, key.length); | |
354 | } | |
355 | } | |
356 | catch(Exception e){ | |
357 | System.err.println(e); | |
358 | } | |
359 | return key; | |
360 | } | |
361 | ||
362 | public void setPassphrase(String passphrase){ | |
363 | if(passphrase==null || passphrase.length()==0){ | |
364 | setPassphrase((byte[])null); | |
365 | } | |
366 | else{ | |
367 | setPassphrase(Util.str2byte(passphrase)); | |
368 | } | |
369 | } | |
370 | public void setPassphrase(byte[] passphrase){ | |
371 | if(passphrase!=null && passphrase.length==0) | |
372 | passphrase=null; | |
373 | this.passphrase=passphrase; | |
374 | } | |
375 | ||
376 | private boolean encrypted=false; | |
377 | private byte[] data=null; | |
378 | private byte[] iv=null; | |
379 | private byte[] publickeyblob=null; | |
380 | ||
381 | public boolean isEncrypted(){ return encrypted; } | |
382 | public boolean decrypt(String _passphrase){ | |
383 | if(_passphrase==null || _passphrase.length()==0){ | |
384 | return !encrypted; | |
385 | } | |
386 | return decrypt(Util.str2byte(_passphrase)); | |
387 | } | |
388 | public boolean decrypt(byte[] _passphrase){ | |
389 | if(!encrypted){ | |
390 | return true; | |
391 | } | |
392 | if(_passphrase==null){ | |
393 | return !encrypted; | |
394 | } | |
395 | byte[] bar=new byte[_passphrase.length]; | |
396 | System.arraycopy(_passphrase, 0, bar, 0, bar.length); | |
397 | _passphrase=bar; | |
398 | byte[] foo=decrypt(data, _passphrase, iv); | |
399 | Util.bzero(_passphrase); | |
400 | if(parse(foo)){ | |
401 | encrypted=false; | |
402 | } | |
403 | return !encrypted; | |
404 | } | |
405 | ||
406 | public static KeyPair load(JSch jsch, String prvkey) throws JSchException{ | |
407 | String pubkey=prvkey+".pub"; | |
408 | if(!new File(pubkey).exists()){ | |
409 | pubkey=null; | |
410 | } | |
411 | return load(jsch, prvkey, pubkey); | |
412 | } | |
413 | public static KeyPair load(JSch jsch, String prvkey, String pubkey) throws JSchException{ | |
414 | ||
415 | byte[] iv=new byte[8]; // 8 | |
416 | boolean encrypted=true; | |
417 | byte[] data=null; | |
418 | ||
419 | byte[] publickeyblob=null; | |
420 | ||
421 | int type=ERROR; | |
422 | int vendor=VENDOR_OPENSSH; | |
423 | ||
424 | try{ | |
425 | File file=new File(prvkey); | |
426 | FileInputStream fis=new FileInputStream(prvkey); | |
427 | byte[] buf=new byte[(int)(file.length())]; | |
428 | int len=0; | |
429 | while(true){ | |
430 | int i=fis.read(buf, len, buf.length-len); | |
431 | if(i<=0) | |
432 | break; | |
433 | len+=i; | |
434 | } | |
435 | fis.close(); | |
436 | ||
437 | int i=0; | |
438 | ||
439 | while(i<len){ | |
440 | if(buf[i]=='B'&& buf[i+1]=='E'&& buf[i+2]=='G'&& buf[i+3]=='I'){ | |
441 | i+=6; | |
442 | if(buf[i]=='D'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=DSA; } | |
443 | else if(buf[i]=='R'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=RSA; } | |
444 | else if(buf[i]=='S'&& buf[i+1]=='S'&& buf[i+2]=='H'){ // FSecure | |
445 | type=UNKNOWN; | |
446 | vendor=VENDOR_FSECURE; | |
447 | } | |
448 | else{ | |
449 | //System.err.println("invalid format: "+identity); | |
450 | throw new JSchException("invalid privatekey: "+prvkey); | |
451 | } | |
452 | i+=3; | |
453 | continue; | |
454 | } | |
455 | if(buf[i]=='C'&& buf[i+1]=='B'&& buf[i+2]=='C'&& buf[i+3]==','){ | |
456 | i+=4; | |
457 | for(int ii=0; ii<iv.length; ii++){ | |
458 | iv[ii]=(byte)(((a2b(buf[i++])<<4)&0xf0)+(a2b(buf[i++])&0xf)); | |
459 | } | |
460 | continue; | |
461 | } | |
462 | if(buf[i]==0x0d && | |
463 | i+1<buf.length && buf[i+1]==0x0a){ | |
464 | i++; | |
465 | continue; | |
466 | } | |
467 | if(buf[i]==0x0a && i+1<buf.length){ | |
468 | if(buf[i+1]==0x0a){ i+=2; break; } | |
469 | if(buf[i+1]==0x0d && | |
470 | i+2<buf.length && buf[i+2]==0x0a){ | |
471 | i+=3; break; | |
472 | } | |
473 | boolean inheader=false; | |
474 | for(int j=i+1; j<buf.length; j++){ | |
475 | if(buf[j]==0x0a) break; | |
476 | //if(buf[j]==0x0d) break; | |
477 | if(buf[j]==':'){inheader=true; break;} | |
478 | } | |
479 | if(!inheader){ | |
480 | i++; | |
481 | encrypted=false; // no passphrase | |
482 | break; | |
483 | } | |
484 | } | |
485 | i++; | |
486 | } | |
487 | ||
488 | if(type==ERROR){ | |
489 | throw new JSchException("invalid privatekey: "+prvkey); | |
490 | } | |
491 | ||
492 | int start=i; | |
493 | while(i<len){ | |
494 | if(buf[i]==0x0a){ | |
495 | boolean xd=(buf[i-1]==0x0d); | |
496 | System.arraycopy(buf, i+1, | |
497 | buf, | |
498 | i-(xd ? 1 : 0), | |
499 | len-i-1-(xd ? 1 : 0) | |
500 | ); | |
501 | if(xd)len--; | |
502 | len--; | |
503 | continue; | |
504 | } | |
505 | if(buf[i]=='-'){ break; } | |
506 | i++; | |
507 | } | |
508 | data=Util.fromBase64(buf, start, i-start); | |
509 | ||
510 | if(data.length>4 && // FSecure | |
511 | data[0]==(byte)0x3f && | |
512 | data[1]==(byte)0x6f && | |
513 | data[2]==(byte)0xf9 && | |
514 | data[3]==(byte)0xeb){ | |
515 | ||
516 | Buffer _buf=new Buffer(data); | |
517 | _buf.getInt(); // 0x3f6ff9be | |
518 | _buf.getInt(); | |
519 | byte[]_type=_buf.getString(); | |
520 | //System.err.println("type: "+new String(_type)); | |
521 | byte[] _cipher=_buf.getString(); | |
522 | String cipher=Util.byte2str(_cipher); | |
523 | //System.err.println("cipher: "+cipher); | |
524 | if(cipher.equals("3des-cbc")){ | |
525 | _buf.getInt(); | |
526 | byte[] foo=new byte[data.length-_buf.getOffSet()]; | |
527 | _buf.getByte(foo); | |
528 | data=foo; | |
529 | encrypted=true; | |
530 | throw new JSchException("unknown privatekey format: "+prvkey); | |
531 | } | |
532 | else if(cipher.equals("none")){ | |
533 | _buf.getInt(); | |
534 | _buf.getInt(); | |
535 | ||
536 | encrypted=false; | |
537 | ||
538 | byte[] foo=new byte[data.length-_buf.getOffSet()]; | |
539 | _buf.getByte(foo); | |
540 | data=foo; | |
541 | } | |
542 | } | |
543 | ||
544 | if(pubkey!=null){ | |
545 | try{ | |
546 | file=new File(pubkey); | |
547 | fis=new FileInputStream(pubkey); | |
548 | buf=new byte[(int)(file.length())]; | |
549 | len=0; | |
550 | while(true){ | |
551 | i=fis.read(buf, len, buf.length-len); | |
552 | if(i<=0) | |
553 | break; | |
554 | len+=i; | |
555 | } | |
556 | fis.close(); | |
557 | ||
558 | if(buf.length>4 && // FSecure's public key | |
559 | buf[0]=='-' && buf[1]=='-' && buf[2]=='-' && buf[3]=='-'){ | |
560 | ||
561 | boolean valid=true; | |
562 | i=0; | |
563 | do{i++;}while(buf.length>i && buf[i]!=0x0a); | |
564 | if(buf.length<=i) {valid=false;} | |
565 | ||
566 | while(valid){ | |
567 | if(buf[i]==0x0a){ | |
568 | boolean inheader=false; | |
569 | for(int j=i+1; j<buf.length; j++){ | |
570 | if(buf[j]==0x0a) break; | |
571 | if(buf[j]==':'){inheader=true; break;} | |
572 | } | |
573 | if(!inheader){ | |
574 | i++; | |
575 | break; | |
576 | } | |
577 | } | |
578 | i++; | |
579 | } | |
580 | if(buf.length<=i){valid=false;} | |
581 | ||
582 | start=i; | |
583 | while(valid && i<len){ | |
584 | if(buf[i]==0x0a){ | |
585 | System.arraycopy(buf, i+1, buf, i, len-i-1); | |
586 | len--; | |
587 | continue; | |
588 | } | |
589 | if(buf[i]=='-'){ break; } | |
590 | i++; | |
591 | } | |
592 | if(valid){ | |
593 | publickeyblob=Util.fromBase64(buf, start, i-start); | |
594 | if(type==UNKNOWN){ | |
595 | if(publickeyblob[8]=='d'){ type=DSA; } | |
596 | else if(publickeyblob[8]=='r'){ type=RSA; } | |
597 | } | |
598 | } | |
599 | } | |
600 | else{ | |
601 | if(buf[0]=='s'&& buf[1]=='s'&& buf[2]=='h' && buf[3]=='-'){ | |
602 | i=0; | |
603 | while(i<len){ if(buf[i]==' ')break; i++;} i++; | |
604 | if(i<len){ | |
605 | start=i; | |
606 | while(i<len){ if(buf[i]==' ')break; i++;} | |
607 | publickeyblob=Util.fromBase64(buf, start, i-start); | |
608 | } | |
609 | } | |
610 | } | |
611 | } | |
612 | catch(Exception ee){ | |
613 | } | |
614 | } | |
615 | } | |
616 | catch(Exception e){ | |
617 | if(e instanceof JSchException) throw (JSchException)e; | |
618 | if(e instanceof Throwable) | |
619 | throw new JSchException(e.toString(), (Throwable)e); | |
620 | throw new JSchException(e.toString()); | |
621 | } | |
622 | ||
623 | KeyPair kpair=null; | |
624 | if(type==DSA){ kpair=new KeyPairDSA(jsch); } | |
625 | else if(type==RSA){ kpair=new KeyPairRSA(jsch); } | |
626 | ||
627 | if(kpair!=null){ | |
628 | kpair.encrypted=encrypted; | |
629 | kpair.publickeyblob=publickeyblob; | |
630 | kpair.vendor=vendor; | |
631 | ||
632 | if(encrypted){ | |
633 | kpair.iv=iv; | |
634 | kpair.data=data; | |
635 | } | |
636 | else{ | |
637 | if(kpair.parse(data)){ | |
638 | return kpair; | |
639 | } | |
640 | else{ | |
641 | throw new JSchException("invalid privatekey: "+prvkey); | |
642 | } | |
643 | } | |
644 | } | |
645 | ||
646 | return kpair; | |
647 | } | |
648 | ||
649 | static private byte a2b(byte c){ | |
650 | if('0'<=c&&c<='9') return (byte)(c-'0'); | |
651 | return (byte)(c-'a'+10); | |
652 | } | |
653 | static private byte b2a(byte c){ | |
654 | if(0<=c&&c<=9) return (byte)(c+'0'); | |
655 | return (byte)(c-10+'A'); | |
656 | } | |
657 | ||
658 | public void dispose(){ | |
659 | Util.bzero(passphrase); | |
660 | } | |
661 | ||
662 | public void finalize (){ | |
663 | dispose(); | |
664 | } | |
665 | } |