00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 package com.jcraft.jsch;
00031
00032 import java.io.FileOutputStream;
00033 import java.io.FileInputStream;
00034 import java.io.File;
00035
00051 public abstract class KeyPair{
00056 public static final int ERROR=0;
00060 public static final int DSA=1;
00064 public static final int RSA=2;
00069 public static final int UNKNOWN=3;
00070
00071 static final int VENDOR_OPENSSH=0;
00072 static final int VENDOR_FSECURE=1;
00073 int vendor=VENDOR_OPENSSH;
00074
00075 private static final byte[] cr=Util.str2byte("\n");
00076
00084 public static KeyPair genKeyPair(JSch jsch, int type) throws JSchException{
00085 return genKeyPair(jsch, type, 1024);
00086 }
00087
00096 public static KeyPair genKeyPair(JSch jsch, int type, int key_size) throws JSchException{
00097 KeyPair kpair=null;
00098 if(type==DSA){ kpair=new KeyPairDSA(jsch); }
00099 else if(type==RSA){ kpair=new KeyPairRSA(jsch); }
00100 if(kpair!=null){
00101 kpair.generate(key_size);
00102 }
00103 return kpair;
00104 }
00105
00106 abstract void generate(int key_size) throws JSchException;
00107
00108 abstract byte[] getBegin();
00109 abstract byte[] getEnd();
00110 abstract int getKeySize();
00111
00112 public String getPublicKeyComment(){
00113 return publicKeyComment;
00114 }
00115 private String publicKeyComment = "";
00116
00117 JSch jsch=null;
00118 private Cipher cipher;
00119 private HASH hash;
00120 private Random random;
00121
00122 private byte[] passphrase;
00123
00134 public KeyPair(JSch jsch){
00135 this.jsch=jsch;
00136 }
00137
00138 static byte[][] header={Util.str2byte("Proc-Type: 4,ENCRYPTED"),
00139 Util.str2byte("DEK-Info: DES-EDE3-CBC,")};
00140
00141 abstract byte[] getPrivateKey();
00142
00147
00148 public void writePrivateKey(java.io.OutputStream out){
00149 byte[] plain=getPrivateKey();
00150 byte[][] _iv=new byte[1][];
00151 byte[] encoded=encrypt(plain, _iv);
00152 if(encoded!=plain)
00153 Util.bzero(plain);
00154 byte[] iv=_iv[0];
00155 byte[] prv=Util.toBase64(encoded, 0, encoded.length);
00156
00157 try{
00158 out.write(getBegin()); out.write(cr);
00159 if(passphrase!=null){
00160 out.write(header[0]); out.write(cr);
00161 out.write(header[1]);
00162 for(int i=0; i<iv.length; i++){
00163 out.write(b2a((byte)((iv[i]>>>4)&0x0f)));
00164 out.write(b2a((byte)(iv[i]&0x0f)));
00165 }
00166 out.write(cr);
00167 out.write(cr);
00168 }
00169 int i=0;
00170 while(i<prv.length){
00171 if(i+64<prv.length){
00172 out.write(prv, i, 64);
00173 out.write(cr);
00174 i+=64;
00175 continue;
00176 }
00177 out.write(prv, i, prv.length-i);
00178 out.write(cr);
00179 break;
00180 }
00181 out.write(getEnd()); out.write(cr);
00182
00183 }
00184 catch(Exception e){
00185 }
00186 }
00187
00188 private static byte[] space=Util.str2byte(" ");
00189
00190 abstract byte[] getKeyTypeName();
00191
00196 public abstract int getKeyType();
00197
00201 public byte[] getPublicKeyBlob(){ return publickeyblob; }
00202
00209 public void writePublicKey(java.io.OutputStream out, String comment){
00210 byte[] pubblob=getPublicKeyBlob();
00211 byte[] pub=Util.toBase64(pubblob, 0, pubblob.length);
00212 try{
00213 out.write(getKeyTypeName()); out.write(space);
00214 out.write(pub, 0, pub.length); out.write(space);
00215 out.write(Util.str2byte(comment));
00216 out.write(cr);
00217 }
00218 catch(Exception e){
00219 }
00220 }
00221
00228 public void writePublicKey(String name, String comment) throws java.io.FileNotFoundException, java.io.IOException{
00229 FileOutputStream fos=new FileOutputStream(name);
00230 writePublicKey(fos, comment);
00231 fos.close();
00232 }
00233
00242 public void writeSECSHPublicKey(java.io.OutputStream out, String comment){
00243 byte[] pubblob=getPublicKeyBlob();
00244 byte[] pub=Util.toBase64(pubblob, 0, pubblob.length);
00245 try{
00246 out.write(Util.str2byte("---- BEGIN SSH2 PUBLIC KEY ----")); out.write(cr);
00247 out.write(Util.str2byte("Comment: \""+comment+"\"")); out.write(cr);
00248 int index=0;
00249 while(index<pub.length){
00250 int len=70;
00251 if((pub.length-index)<len)len=pub.length-index;
00252 out.write(pub, index, len); out.write(cr);
00253 index+=len;
00254 }
00255 out.write(Util.str2byte("---- END SSH2 PUBLIC KEY ----")); out.write(cr);
00256 }
00257 catch(Exception e){
00258 }
00259 }
00260
00269 public void writeSECSHPublicKey(String name, String comment) throws java.io.FileNotFoundException, java.io.IOException{
00270 FileOutputStream fos=new FileOutputStream(name);
00271 writeSECSHPublicKey(fos, comment);
00272 fos.close();
00273 }
00274
00275
00280 public void writePrivateKey(String name) throws java.io.FileNotFoundException, java.io.IOException{
00281 FileOutputStream fos=new FileOutputStream(name);
00282 writePrivateKey(fos);
00283 fos.close();
00284 }
00285
00286
00292 public String getFingerPrint(){
00293 if(hash==null) hash=genHash();
00294 byte[] kblob=getPublicKeyBlob();
00295 if(kblob==null) return null;
00296 return getKeySize()+" "+Util.getFingerPrint(hash, kblob);
00297 }
00298
00299 private byte[] encrypt(byte[] plain, byte[][] _iv){
00300 if(passphrase==null) return plain;
00301
00302 if(cipher==null) cipher=genCipher();
00303 byte[] iv=_iv[0]=new byte[cipher.getIVSize()];
00304
00305 if(random==null) random=genRandom();
00306 random.fill(iv, 0, iv.length);
00307
00308 byte[] key=genKey(passphrase, iv);
00309 byte[] encoded=plain;
00310
00311
00312 {
00313
00314 int bsize=cipher.getIVSize();
00315 byte[] foo=new byte[(encoded.length/bsize+1)*bsize];
00316 System.arraycopy(encoded, 0, foo, 0, encoded.length);
00317 int padding=bsize-encoded.length%bsize;
00318 for(int i=foo.length-1; (foo.length-padding)<=i; i--){
00319 foo[i]=(byte)padding;
00320 }
00321 encoded=foo;
00322 }
00323
00324 try{
00325 cipher.init(Cipher.ENCRYPT_MODE, key, iv);
00326 cipher.update(encoded, 0, encoded.length, encoded, 0);
00327 }
00328 catch(Exception e){
00329
00330 }
00331 Util.bzero(key);
00332 return encoded;
00333 }
00334
00335 abstract boolean parse(byte[] data);
00336
00337 private byte[] decrypt(byte[] data, byte[] passphrase, byte[] iv){
00338
00339
00340
00341
00342
00343
00344 try{
00345 byte[] key=genKey(passphrase, iv);
00346 cipher.init(Cipher.DECRYPT_MODE, key, iv);
00347 Util.bzero(key);
00348 byte[] plain=new byte[data.length];
00349 cipher.update(data, 0, data.length, plain, 0);
00350 return plain;
00351 }
00352 catch(Exception e){
00353
00354 }
00355 return null;
00356 }
00357
00358 int writeSEQUENCE(byte[] buf, int index, int len){
00359 buf[index++]=0x30;
00360 index=writeLength(buf, index, len);
00361 return index;
00362 }
00363 int writeINTEGER(byte[] buf, int index, byte[] data){
00364 buf[index++]=0x02;
00365 index=writeLength(buf, index, data.length);
00366 System.arraycopy(data, 0, buf, index, data.length);
00367 index+=data.length;
00368 return index;
00369 }
00370
00371 int countLength(int len){
00372 int i=1;
00373 if(len<=0x7f) return i;
00374 while(len>0){
00375 len>>>=8;
00376 i++;
00377 }
00378 return i;
00379 }
00380
00381 int writeLength(byte[] data, int index, int len){
00382 int i=countLength(len)-1;
00383 if(i==0){
00384 data[index++]=(byte)len;
00385 return index;
00386 }
00387 data[index++]=(byte)(0x80|i);
00388 int j=index+i;
00389 while(i>0){
00390 data[index+i-1]=(byte)(len&0xff);
00391 len>>>=8;
00392 i--;
00393 }
00394 return j;
00395 }
00396
00397 private Random genRandom(){
00398 if(random==null){
00399 try{
00400 Class c=Class.forName(jsch.getConfig("random"));
00401 random=(Random)(c.newInstance());
00402 }
00403 catch(Exception e){ System.err.println("connect: random "+e); }
00404 }
00405 return random;
00406 }
00407
00408 private HASH genHash(){
00409 try{
00410 Class c=Class.forName(jsch.getConfig("md5"));
00411 hash=(HASH)(c.newInstance());
00412 hash.init();
00413 }
00414 catch(Exception e){
00415 }
00416 return hash;
00417 }
00418 private Cipher genCipher(){
00419 try{
00420 Class c;
00421 c=Class.forName(jsch.getConfig("3des-cbc"));
00422 cipher=(Cipher)(c.newInstance());
00423 }
00424 catch(Exception e){
00425 }
00426 return cipher;
00427 }
00428
00429
00430
00431
00432
00433
00434
00435 synchronized byte[] genKey(byte[] passphrase, byte[] iv){
00436 if(cipher==null) cipher=genCipher();
00437 if(hash==null) hash=genHash();
00438
00439 byte[] key=new byte[cipher.getBlockSize()];
00440 int hsize=hash.getBlockSize();
00441 byte[] hn=new byte[key.length/hsize*hsize+
00442 (key.length%hsize==0?0:hsize)];
00443 try{
00444 byte[] tmp=null;
00445 if(vendor==VENDOR_OPENSSH){
00446 for(int index=0; index+hsize<=hn.length;){
00447 if(tmp!=null){ hash.update(tmp, 0, tmp.length); }
00448 hash.update(passphrase, 0, passphrase.length);
00449 hash.update(iv, 0, iv.length > 8 ? 8: iv.length);
00450 tmp=hash.digest();
00451 System.arraycopy(tmp, 0, hn, index, tmp.length);
00452 index+=tmp.length;
00453 }
00454 System.arraycopy(hn, 0, key, 0, key.length);
00455 }
00456 else if(vendor==VENDOR_FSECURE){
00457 for(int index=0; index+hsize<=hn.length;){
00458 if(tmp!=null){ hash.update(tmp, 0, tmp.length); }
00459 hash.update(passphrase, 0, passphrase.length);
00460 tmp=hash.digest();
00461 System.arraycopy(tmp, 0, hn, index, tmp.length);
00462 index+=tmp.length;
00463 }
00464 System.arraycopy(hn, 0, key, 0, key.length);
00465 }
00466 }
00467 catch(Exception e){
00468 System.err.println(e);
00469 }
00470 return key;
00471 }
00472
00473
00479 public void setPassphrase(String passphrase){
00480 if(passphrase==null || passphrase.length()==0){
00481 setPassphrase((byte[])null);
00482 }
00483 else{
00484 setPassphrase(Util.str2byte(passphrase));
00485 }
00486 }
00492 public void setPassphrase(byte[] passphrase){
00493 if(passphrase!=null && passphrase.length==0)
00494 passphrase=null;
00495 this.passphrase=passphrase;
00496 }
00497
00498 private boolean encrypted=false;
00499 private byte[] data=null;
00500 private byte[] iv=null;
00501 private byte[] publickeyblob=null;
00502
00508 public boolean isEncrypted(){ return encrypted; }
00509
00515 public boolean decrypt(String _passphrase){
00516 if(_passphrase==null || _passphrase.length()==0){
00517 return !encrypted;
00518 }
00519 return decrypt(Util.str2byte(_passphrase));
00520 }
00526 public boolean decrypt(byte[] _passphrase){
00527 if(!encrypted){
00528 return true;
00529 }
00530 if(_passphrase==null){
00531 return !encrypted;
00532 }
00533 byte[] bar=new byte[_passphrase.length];
00534 System.arraycopy(_passphrase, 0, bar, 0, bar.length);
00535 _passphrase=bar;
00536 byte[] foo=decrypt(data, _passphrase, iv);
00537 Util.bzero(_passphrase);
00538 if(parse(foo)){
00539 encrypted=false;
00540 }
00541 return !encrypted;
00542 }
00543
00552 public static KeyPair load(JSch jsch, String prvkey) throws JSchException{
00553 String pubkey=prvkey+".pub";
00554 if(!new File(pubkey).exists()){
00555 pubkey=null;
00556 }
00557 return load(jsch, prvkey, pubkey);
00558 }
00559
00567 public static KeyPair load(JSch jsch, String prvkey, String pubkey) throws JSchException{
00568
00569 byte[] iv=new byte[8];
00570 boolean encrypted=true;
00571 byte[] data=null;
00572
00573 byte[] publickeyblob=null;
00574
00575 int type=ERROR;
00576 int vendor=VENDOR_OPENSSH;
00577 String publicKeyComment = "";
00578 Cipher cipher=null;
00579
00580 try{
00581 File file=new File(prvkey);
00582 FileInputStream fis=new FileInputStream(prvkey);
00583 byte[] buf=new byte[(int)(file.length())];
00584 int len=0;
00585 while(true){
00586 int i=fis.read(buf, len, buf.length-len);
00587 if(i<=0)
00588 break;
00589 len+=i;
00590 }
00591 fis.close();
00592
00593 int i=0;
00594
00595 while(i<len){
00596 if(buf[i] == '-' && i+4<len &&
00597 buf[i+1] == '-' && buf[i+2] == '-' &&
00598 buf[i+3] == '-' && buf[i+4] == '-'){
00599 break;
00600 }
00601 i++;
00602 }
00603
00604 while(i<len){
00605 if(buf[i]=='B'&& i+3<len && buf[i+1]=='E'&& buf[i+2]=='G'&& buf[i+3]=='I'){
00606 i+=6;
00607 if(buf[i]=='D'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=DSA; }
00608 else if(buf[i]=='R'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=RSA; }
00609 else if(buf[i]=='S'&& buf[i+1]=='S'&& buf[i+2]=='H'){
00610 type=UNKNOWN;
00611 vendor=VENDOR_FSECURE;
00612 }
00613 else{
00614 throw new JSchException("invalid privatekey: "+prvkey);
00615 }
00616 i+=3;
00617 continue;
00618 }
00619 if(buf[i]=='A'&& i+7<len && buf[i+1]=='E'&& buf[i+2]=='S'&& buf[i+3]=='-' &&
00620 buf[i+4]=='2'&& buf[i+5]=='5'&& buf[i+6]=='6'&& buf[i+7]=='-'){
00621 i+=8;
00622 if(Session.checkCipher((String)jsch.getConfig("aes256-cbc"))){
00623 Class c=Class.forName((String)jsch.getConfig("aes256-cbc"));
00624 cipher=(Cipher)(c.newInstance());
00625
00626 iv=new byte[cipher.getIVSize()];
00627 }
00628 else{
00629 throw new JSchException("privatekey: aes256-cbc is not available "+prvkey);
00630 }
00631 continue;
00632 }
00633 if(buf[i]=='A'&& i+7<len && buf[i+1]=='E'&& buf[i+2]=='S'&& buf[i+3]=='-' &&
00634 buf[i+4]=='1'&& buf[i+5]=='9'&& buf[i+6]=='2'&& buf[i+7]=='-'){
00635 i+=8;
00636 if(Session.checkCipher((String)jsch.getConfig("aes192-cbc"))){
00637 Class c=Class.forName((String)jsch.getConfig("aes192-cbc"));
00638 cipher=(Cipher)(c.newInstance());
00639
00640 iv=new byte[cipher.getIVSize()];
00641 }
00642 else{
00643 throw new JSchException("privatekey: aes192-cbc is not available "+prvkey);
00644 }
00645 continue;
00646 }
00647 if(buf[i]=='A'&& i+7<len && buf[i+1]=='E'&& buf[i+2]=='S'&& buf[i+3]=='-' &&
00648 buf[i+4]=='1'&& buf[i+5]=='2'&& buf[i+6]=='8'&& buf[i+7]=='-'){
00649 i+=8;
00650 if(Session.checkCipher((String)jsch.getConfig("aes128-cbc"))){
00651 Class c=Class.forName((String)jsch.getConfig("aes128-cbc"));
00652 cipher=(Cipher)(c.newInstance());
00653
00654 iv=new byte[cipher.getIVSize()];
00655 }
00656 else{
00657 throw new JSchException("privatekey: aes128-cbc is not available "+prvkey);
00658 }
00659 continue;
00660 }
00661 if(buf[i]=='C'&& i+3<len && buf[i+1]=='B'&& buf[i+2]=='C'&& buf[i+3]==','){
00662 i+=4;
00663 for(int ii=0; ii<iv.length; ii++){
00664 iv[ii]=(byte)(((a2b(buf[i++])<<4)&0xf0)+(a2b(buf[i++])&0xf));
00665 }
00666 continue;
00667 }
00668 if(buf[i]==0x0d && i+1<buf.length && buf[i+1]==0x0a){
00669 i++;
00670 continue;
00671 }
00672 if(buf[i]==0x0a && i+1<buf.length){
00673 if(buf[i+1]==0x0a){ i+=2; break; }
00674 if(buf[i+1]==0x0d &&
00675 i+2<buf.length && buf[i+2]==0x0a){
00676 i+=3; break;
00677 }
00678 boolean inheader=false;
00679 for(int j=i+1; j<buf.length; j++){
00680 if(buf[j]==0x0a) break;
00681
00682 if(buf[j]==':'){inheader=true; break;}
00683 }
00684 if(!inheader){
00685 i++;
00686 encrypted=false;
00687 break;
00688 }
00689 }
00690 i++;
00691 }
00692
00693 if(type==ERROR){
00694 throw new JSchException("invalid privatekey: "+prvkey);
00695 }
00696
00697 int start=i;
00698 while(i<len){
00699 if(buf[i]==0x0a){
00700 boolean xd=(buf[i-1]==0x0d);
00701 System.arraycopy(buf, i+1,
00702 buf,
00703 i-(xd ? 1 : 0),
00704 len-i-1-(xd ? 1 : 0)
00705 );
00706 if(xd)len--;
00707 len--;
00708 continue;
00709 }
00710 if(buf[i]=='-'){ break; }
00711 i++;
00712 }
00713 data=Util.fromBase64(buf, start, i-start);
00714
00715 if(data.length>4 &&
00716 data[0]==(byte)0x3f &&
00717 data[1]==(byte)0x6f &&
00718 data[2]==(byte)0xf9 &&
00719 data[3]==(byte)0xeb){
00720
00721 Buffer _buf=new Buffer(data);
00722 _buf.getInt();
00723 _buf.getInt();
00724 byte[]_type=_buf.getString();
00725
00726 String _cipher=Util.byte2str(_buf.getString());
00727
00728 if(_cipher.equals("3des-cbc")){
00729 _buf.getInt();
00730 byte[] foo=new byte[data.length-_buf.getOffSet()];
00731 _buf.getByte(foo);
00732 data=foo;
00733 encrypted=true;
00734 throw new JSchException("unknown privatekey format: "+prvkey);
00735 }
00736 else if(_cipher.equals("none")){
00737 _buf.getInt();
00738 _buf.getInt();
00739
00740 encrypted=false;
00741
00742 byte[] foo=new byte[data.length-_buf.getOffSet()];
00743 _buf.getByte(foo);
00744 data=foo;
00745 }
00746 }
00747
00748 if(pubkey!=null){
00749 try{
00750 file=new File(pubkey);
00751 fis=new FileInputStream(pubkey);
00752 buf=new byte[(int)(file.length())];
00753 len=0;
00754 while(true){
00755 i=fis.read(buf, len, buf.length-len);
00756 if(i<=0)
00757 break;
00758 len+=i;
00759 }
00760 fis.close();
00761
00762 if(buf.length>4 &&
00763 buf[0]=='-' && buf[1]=='-' && buf[2]=='-' && buf[3]=='-'){
00764
00765 boolean valid=true;
00766 i=0;
00767 do{i++;}while(buf.length>i && buf[i]!=0x0a);
00768 if(buf.length<=i) {valid=false;}
00769
00770 while(valid){
00771 if(buf[i]==0x0a){
00772 boolean inheader=false;
00773 for(int j=i+1; j<buf.length; j++){
00774 if(buf[j]==0x0a) break;
00775 if(buf[j]==':'){inheader=true; break;}
00776 }
00777 if(!inheader){
00778 i++;
00779 break;
00780 }
00781 }
00782 i++;
00783 }
00784 if(buf.length<=i){valid=false;}
00785
00786 start=i;
00787 while(valid && i<len){
00788 if(buf[i]==0x0a){
00789 System.arraycopy(buf, i+1, buf, i, len-i-1);
00790 len--;
00791 continue;
00792 }
00793 if(buf[i]=='-'){ break; }
00794 i++;
00795 }
00796 if(valid){
00797 publickeyblob=Util.fromBase64(buf, start, i-start);
00798 if(type==UNKNOWN){
00799 if(publickeyblob[8]=='d'){ type=DSA; }
00800 else if(publickeyblob[8]=='r'){ type=RSA; }
00801 }
00802 }
00803 }
00804 else{
00805 if(buf[0]=='s'&& buf[1]=='s'&& buf[2]=='h' && buf[3]=='-'){
00806 i=0;
00807 while(i<len){ if(buf[i]==' ')break; i++;} i++;
00808 if(i<len){
00809 start=i;
00810 while(i<len){ if(buf[i]==' ')break; i++;}
00811 publickeyblob=Util.fromBase64(buf, start, i-start);
00812 }
00813 if(i++<len){
00814 int s=i;
00815 while(i<len){ if(buf[i]=='\n')break; i++;}
00816 if(i<len){
00817 publicKeyComment = new String(buf, s, i-s);
00818 }
00819 }
00820 }
00821 }
00822 }
00823 catch(Exception ee){
00824 }
00825 }
00826 }
00827 catch(Exception e){
00828 if(e instanceof JSchException) throw (JSchException)e;
00829 if(e instanceof Throwable)
00830 throw new JSchException(e.toString(), (Throwable)e);
00831 throw new JSchException(e.toString());
00832 }
00833
00834 KeyPair kpair=null;
00835 if(type==DSA){ kpair=new KeyPairDSA(jsch); }
00836 else if(type==RSA){ kpair=new KeyPairRSA(jsch); }
00837
00838 if(kpair!=null){
00839 kpair.encrypted=encrypted;
00840 kpair.publickeyblob=publickeyblob;
00841 kpair.vendor=vendor;
00842 kpair.publicKeyComment=publicKeyComment;
00843 kpair.cipher=cipher;
00844
00845 if(encrypted){
00846 kpair.iv=iv;
00847 kpair.data=data;
00848 }
00849 else{
00850 if(kpair.parse(data)){
00851 return kpair;
00852 }
00853 else{
00854 throw new JSchException("invalid privatekey: "+prvkey);
00855 }
00856 }
00857 }
00858
00859 return kpair;
00860 }
00861
00862 static private byte a2b(byte c){
00863 if('0'<=c&&c<='9') return (byte)(c-'0');
00864 return (byte)(c-'a'+10);
00865 }
00866 static private byte b2a(byte c){
00867 if(0<=c&&c<=9) return (byte)(c+'0');
00868 return (byte)(c-10+'A');
00869 }
00870
00875 public void dispose(){
00876 Util.bzero(passphrase);
00877 }
00878
00886 public void finalize (){
00887 dispose();
00888 }
00889 }