KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > jcraft > jsch > KeyPair


1 /* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
2 /*
3 Copyright (c) 2002,2003,2004,2005,2006 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 JavaDoc;
33 import java.io.FileInputStream JavaDoc;
34 import java.io.File JavaDoc;
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="\n".getBytes();
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={"Proc-Type: 4,ENCRYPTED".getBytes(),
79               "DEK-Info: DES-EDE3-CBC,".getBytes()};
80
81   abstract byte[] getPrivateKey();
82
83   public void writePrivateKey(java.io.OutputStream JavaDoc 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 JavaDoc e){
120     }
121   }
122
123   private static byte[] space=" ".getBytes();
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 JavaDoc out, String JavaDoc 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(comment.getBytes());
137       out.write(cr);
138     }
139     catch(Exception JavaDoc e){
140     }
141   }
142
143   public void writePublicKey(String JavaDoc name, String JavaDoc comment) throws java.io.FileNotFoundException JavaDoc, java.io.IOException JavaDoc{
144     FileOutputStream JavaDoc fos=new FileOutputStream JavaDoc(name);
145     writePublicKey(fos, comment);
146     fos.close();
147   }
148
149   public void writeSECSHPublicKey(java.io.OutputStream JavaDoc out, String JavaDoc comment){
150     byte[] pubblob=getPublicKeyBlob();
151     byte[] pub=Util.toBase64(pubblob, 0, pubblob.length);
152     try{
153       out.write("---- BEGIN SSH2 PUBLIC KEY ----".getBytes()); out.write(cr);
154       out.write(("Comment: \""+comment+"\"").getBytes()); 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("---- END SSH2 PUBLIC KEY ----".getBytes()); out.write(cr);
163     }
164     catch(Exception JavaDoc e){
165     }
166   }
167
168   public void writeSECSHPublicKey(String JavaDoc name, String JavaDoc comment) throws java.io.FileNotFoundException JavaDoc, java.io.IOException JavaDoc{
169     FileOutputStream JavaDoc fos=new FileOutputStream JavaDoc(name);
170     writeSECSHPublicKey(fos, comment);
171     fos.close();
172   }
173
174
175   public void writePrivateKey(String JavaDoc name) throws java.io.FileNotFoundException JavaDoc, java.io.IOException JavaDoc{
176     FileOutputStream JavaDoc fos=new FileOutputStream JavaDoc(name);
177     writePrivateKey(fos);
178     fos.close();
179   }
180
181   public String JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc c=Class.forName(jsch.getConfig("random"));
290         random=(Random)(c.newInstance());
291       }
292       catch(Exception JavaDoc e){ System.err.println("connect: random "+e); }
293     }
294     return random;
295   }
296
297   private HASH genHash(){
298     try{
299       Class JavaDoc c=Class.forName(jsch.getConfig("md5"));
300       hash=(HASH)(c.newInstance());
301       hash.init();
302     }
303     catch(Exception JavaDoc e){
304     }
305     return hash;
306   }
307   private Cipher genCipher(){
308     try{
309       Class JavaDoc c;
310       c=Class.forName(jsch.getConfig("3des-cbc"));
311       cipher=(Cipher)(c.newInstance());
312     }
313     catch(Exception JavaDoc 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 JavaDoc e){
357       System.err.println(e);
358     }
359     return key;
360   }
361
362   public void setPassphrase(String JavaDoc 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 JavaDoc _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 JavaDoc prvkey) throws JSchException{
407     String JavaDoc pubkey=prvkey+".pub";
408     if(!new File JavaDoc(pubkey).exists()){
409       pubkey=null;
410     }
411     return load(jsch, prvkey, pubkey);
412   }
413   public static KeyPair load(JSch jsch, String JavaDoc prvkey, String JavaDoc 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 JavaDoc file=new File JavaDoc(prvkey);
426       FileInputStream JavaDoc fis=new FileInputStream JavaDoc(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 JavaDoc cipher=new String JavaDoc(_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 JavaDoc(pubkey);
547       fis=new FileInputStream JavaDoc(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 JavaDoc ee){
613     }
614       }
615     }
616     catch(Exception JavaDoc e){
617       if(e instanceof JSchException) throw (JSchException)e;
618       if(e instanceof Throwable JavaDoc)
619         throw new JSchException(e.toString(), (Throwable JavaDoc)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 }
666
Popular Tags