]> Joshua Wise's Git repositories - dumload.git/blame_incremental - src/com/jcraft/jsch/KeyPair.java
Add a progress bar.
[dumload.git] / src / com / jcraft / jsch / KeyPair.java
... / ...
CommitLineData
1/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
2/*
3Copyright (c) 2002-2010 ymnk, JCraft,Inc. All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
18THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
19INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
21INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
22INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
24OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*/
29
30package com.jcraft.jsch;
31
32import java.io.FileOutputStream;
33import java.io.FileInputStream;
34import java.io.File;
35
36public 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}
This page took 0.031928 seconds and 4 git commands to generate.