00001 using System;
00002 using System.IO;
00003 using System.Runtime.CompilerServices;
00004
00005
00006 namespace Tamir.SharpSsh.jsch
00007 {
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037 public abstract class KeyPair
00038 {
00039 public const int ERROR=0;
00040 public const int DSA=1;
00041 public const int RSA=2;
00042 public const int UNKNOWN=3;
00043
00044 internal const int VENDOR_OPENSSH=0;
00045 internal const int VENDOR_FSECURE=1;
00046 internal int vendor=VENDOR_OPENSSH;
00047
00048 private static byte[] cr=Util.getBytes("\n");
00049
00050 public static KeyPair genKeyPair(JSch jsch, int type)
00051 {
00052 return genKeyPair(jsch, type, 1024);
00053 }
00054 public static KeyPair genKeyPair(JSch jsch, int type, int key_size)
00055 {
00056 KeyPair kpair=null;
00057 if(type==DSA){ kpair=new KeyPairDSA(jsch); }
00058 else if(type==RSA){ kpair=new KeyPairRSA(jsch); }
00059 if(kpair!=null)
00060 {
00061 kpair.generate(key_size);
00062 }
00063 return kpair;
00064 }
00065
00066 internal abstract void generate(int key_size);
00067
00068 internal abstract byte[] getBegin();
00069 internal abstract byte[] getEnd();
00070 public abstract int getKeySize();
00071
00072 internal JSch jsch=null;
00073 private Cipher cipher;
00074 private HASH hash;
00075 private Random random;
00076
00077 private byte[] passphrase;
00078
00079 public KeyPair(JSch jsch)
00080 {
00081 this.jsch=jsch;
00082 }
00083
00084 static byte[][] header={Util.getBytes( "Proc-Type: 4,ENCRYPTED"),
00085 Util.getBytes("DEK-Info: DES-EDE3-CBC,")};
00086
00087 internal abstract byte[] getPrivateKey();
00088
00089 private void Write(Stream s, byte[] arr)
00090 {
00091 s.Write(arr, 0, arr.Length);
00092 }
00093
00094 public void writePrivateKey(Stream outs)
00095 {
00096 byte[] plain=getPrivateKey();
00097 byte[][] _iv=new byte[1][];
00098 byte[] encoded=encrypt(plain, _iv);
00099 byte[] iv=_iv[0];
00100 byte[] prv=Util.toBase64(encoded, 0, encoded.Length);
00101
00102 try
00103 {
00104 Write(outs, getBegin()); Write(outs,cr);
00105 if(passphrase!=null)
00106 {
00107 Write(outs, header[0]); Write(outs,cr);
00108 Write(outs, header[1]);
00109 for(int j=0; j<iv.Length; j++)
00110 {
00111 outs.WriteByte(b2a((byte)((iv[j]>>4)&0x0f)));
00112 outs.WriteByte(b2a((byte)(iv[j]&0x0f)));
00113 }
00114 Write(outs,cr);
00115 Write(outs,cr);
00116 }
00117 int i=0;
00118 while(i<prv.Length)
00119 {
00120 if(i+64<prv.Length)
00121 {
00122 outs.Write(prv, i, 64);
00123 Write(outs,cr);
00124 i+=64;
00125 continue;
00126 }
00127 outs.Write(prv, i, prv.Length-i);
00128 Write(outs,cr);
00129 break;
00130 }
00131 Write(outs, getEnd()); Write(outs,cr);
00132
00133 }
00134 catch(Exception e)
00135 {
00136 Console.WriteLine(e);
00137 }
00138 }
00139
00140 private static byte[] space=Util.getBytes(" ");
00141
00142 internal abstract byte[] getKeyTypeName();
00143 public abstract int getKeyType();
00144
00145 public virtual byte[] getPublicKeyBlob(){ return publickeyblob; }
00146
00147 public void writePublicKey(Stream outs, String comment)
00148 {
00149 byte[] pubblob=getPublicKeyBlob();
00150 byte[] pub=Util.toBase64(pubblob, 0, pubblob.Length);
00151 try
00152 {
00153 Write(outs, getKeyTypeName()); Write(outs, space);
00154 outs.Write(pub, 0, pub.Length); Write(outs, space);
00155 Write(outs, Util.getBytes( comment));
00156 Write(outs,cr);
00157 }
00158 catch(Exception e)
00159 {
00160 Console.WriteLine(e);
00161 }
00162 }
00163
00164 public void writePublicKey(String name, String comment)
00165 {
00166 FileStream fos=new FileStream(name,FileMode.OpenOrCreate);
00167 writePublicKey(fos, comment);
00168 fos.Close();
00169 }
00170
00171 public void writeSECSHPublicKey(Stream outs, String comment)
00172 {
00173 byte[] pubblob=getPublicKeyBlob();
00174 byte[] pub=Util.toBase64(pubblob, 0, pubblob.Length);
00175 try
00176 {
00177 Write(outs, Util.getBytes( "---- BEGIN SSH2 PUBLIC KEY ----")); Write(outs, cr);
00178 Write(outs, Util.getBytes("Comment: \""+comment+"\"")); Write(outs,cr);
00179 int index=0;
00180 while(index<pub.Length)
00181 {
00182 int len=70;
00183 if((pub.Length-index)<len)len=pub.Length-index;
00184 outs.Write(pub, index, len); Write(outs, cr);
00185 index+=len;
00186 }
00187 Write(outs, Util.getBytes("---- END SSH2 PUBLIC KEY ----")); Write(outs,cr);
00188 }
00189 catch(Exception e)
00190 {
00191 Console.WriteLine(e);
00192 }
00193 }
00194
00195 public void writeSECSHPublicKey(String name, String comment)
00196 {
00197 FileStream fos=new FileStream(name, FileMode.OpenOrCreate);
00198 writeSECSHPublicKey(fos, comment);
00199 fos.Close();
00200 }
00201
00202
00203 public void writePrivateKey(String name)
00204 {
00205 FileStream fos=new FileStream(name, FileMode.OpenOrCreate);
00206 writePrivateKey(fos);
00207 fos.Close();
00208 }
00209
00210 public String getFingerPrint()
00211 {
00212 if(hash==null) hash=genHash();
00213 byte[] kblob=getPublicKeyBlob();
00214 if(kblob==null) return null;
00215 return getKeySize()+" "+Util.getFingerPrint(hash, kblob);
00216 }
00217
00218 private byte[] encrypt(byte[] plain, byte[][] _iv)
00219 {
00220 if(passphrase==null) return plain;
00221
00222 if(cipher==null) cipher=genCipher();
00223 byte[] iv=_iv[0]=new byte[cipher.getIVSize()];
00224
00225 if(random==null) random=genRandom();
00226 random.fill(iv, 0, iv.Length);
00227
00228 byte[] key=genKey(passphrase, iv);
00229 byte[] encoded=plain;
00230 int bsize=cipher.getBlockSize();
00231 if(encoded.Length%bsize!=0)
00232 {
00233 byte[] foo=new byte[(encoded.Length/bsize+1)*bsize];
00234 Array.Copy(encoded, 0, foo, 0, encoded.Length);
00235 encoded=foo;
00236 }
00237
00238 try
00239 {
00240 cipher.init(Cipher.ENCRYPT_MODE, key, iv);
00241 cipher.update(encoded, 0, encoded.Length, encoded, 0);
00242 }
00243 catch(Exception e)
00244 {
00245 Console.WriteLine(e);
00246 }
00247 return encoded;
00248 }
00249
00250 internal abstract bool parse(byte[] data);
00251
00252 private byte[] decrypt(byte[] data, byte[] passphrase, byte[] iv)
00253 {
00254
00255
00256
00257
00258
00259
00260 try
00261 {
00262 byte[] key=genKey(passphrase, iv);
00263 cipher.init(Cipher.DECRYPT_MODE, key, iv);
00264 byte[] plain=new byte[data.Length];
00265 cipher.update(data, 0, data.Length, plain, 0);
00266 return plain;
00267 }
00268 catch(Exception e)
00269 {
00270 Console.WriteLine(e);
00271 }
00272 return null;
00273 }
00274
00275 internal int writeSEQUENCE(byte[] buf, int index, int len)
00276 {
00277 buf[index++]=0x30;
00278 index=writeLength(buf, index, len);
00279 return index;
00280 }
00281 internal int writeINTEGER(byte[] buf, int index, byte[] data)
00282 {
00283 buf[index++]=0x02;
00284 index=writeLength(buf, index, data.Length);
00285 Array.Copy(data, 0, buf, index, data.Length);
00286 index+=data.Length;
00287 return index;
00288 }
00289
00290 internal int countLength(int len)
00291 {
00292 int i=1;
00293 if(len<=0x7f) return i;
00294 while(len>0)
00295 {
00296 len>>=8;
00297 i++;
00298 }
00299 return i;
00300 }
00301
00302 internal int writeLength(byte[] data, int index, int len)
00303 {
00304 int i=countLength(len)-1;
00305 if(i==0)
00306 {
00307 data[index++]=(byte)len;
00308 return index;
00309 }
00310 data[index++]=(byte)(0x80|i);
00311 int j=index+i;
00312 while(i>0)
00313 {
00314 data[index+i-1]=(byte)(len&0xff);
00315 len>>=8;
00316 i--;
00317 }
00318 return j;
00319 }
00320
00321 private Random genRandom()
00322 {
00323 if(random==null)
00324 {
00325 try
00326 {
00327 Type t=Type.GetType(jsch.getConfig("random"));
00328 random=(Random)Activator.CreateInstance(t);
00329 }
00330 catch(Exception e){ Console.Error.WriteLine("connect: random "+e); }
00331 }
00332 return random;
00333 }
00334
00335 private HASH genHash()
00336 {
00337 try
00338 {
00339 Type t=Type.GetType(jsch.getConfig("md5"));
00340 hash=(HASH)Activator.CreateInstance(t);
00341 hash.init();
00342 }
00343 catch
00344 {
00345 }
00346 return hash;
00347 }
00348 private Cipher genCipher()
00349 {
00350 try
00351 {
00352 Type t;
00353 t=Type.GetType(jsch.getConfig("3des-cbc"));
00354 cipher=(Cipher)(Activator.CreateInstance(t));
00355 }
00356 catch
00357 {
00358 }
00359 return cipher;
00360 }
00361
00362
00363
00364
00365
00366
00367
00368 [MethodImpl(MethodImplOptions.Synchronized)]
00369 internal byte[] genKey(byte[] passphrase, byte[] iv)
00370 {
00371 if(cipher==null) cipher=genCipher();
00372 if(hash==null) hash=genHash();
00373
00374 byte[] key=new byte[cipher.getBlockSize()];
00375 int hsize=hash.getBlockSize();
00376 byte[] hn=new byte[key.Length/hsize*hsize+
00377 (key.Length%hsize==0?0:hsize)];
00378 try
00379 {
00380 byte[] tmp=null;
00381 if(vendor==VENDOR_OPENSSH)
00382 {
00383 for(int index=0; index+hsize<=hn.Length;)
00384 {
00385 if(tmp!=null){ hash.update(tmp, 0, tmp.Length); }
00386 hash.update(passphrase, 0, passphrase.Length);
00387 hash.update(iv, 0, iv.Length);
00388 tmp=hash.digest();
00389 Array.Copy(tmp, 0, hn, index, tmp.Length);
00390 index+=tmp.Length;
00391 }
00392 Array.Copy(hn, 0, key, 0, key.Length);
00393 }
00394 else if(vendor==VENDOR_FSECURE)
00395 {
00396 for(int index=0; index+hsize<=hn.Length;)
00397 {
00398 if(tmp!=null){ hash.update(tmp, 0, tmp.Length); }
00399 hash.update(passphrase, 0, passphrase.Length);
00400 tmp=hash.digest();
00401 Array.Copy(tmp, 0, hn, index, tmp.Length);
00402 index+=tmp.Length;
00403 }
00404 Array.Copy(hn, 0, key, 0, key.Length);
00405 }
00406 }
00407 catch(Exception e)
00408 {
00409 Console.WriteLine(e);
00410 }
00411 return key;
00412 }
00413
00414 public void setPassphrase(String passphrase)
00415 {
00416 if(passphrase==null || passphrase.Length==0)
00417 {
00418 setPassphrase((byte[])null);
00419 }
00420 else
00421 {
00422 setPassphrase(Util.getBytes( passphrase ));
00423 }
00424 }
00425 public void setPassphrase(byte[] passphrase)
00426 {
00427 if(passphrase!=null && passphrase.Length==0)
00428 passphrase=null;
00429 this.passphrase=passphrase;
00430 }
00431
00432 private bool encrypted=false;
00433 private byte[] data=null;
00434 private byte[] iv=null;
00435 private byte[] publickeyblob=null;
00436
00437 public bool isEncrypted(){ return encrypted; }
00438 public bool decrypt(String _passphrase)
00439 {
00440 byte[] passphrase= Util.getBytes( _passphrase );
00441 byte[] foo=decrypt(data, passphrase, iv);
00442 if(parse(foo))
00443 {
00444 encrypted=false;
00445 }
00446 return !encrypted;
00447 }
00448
00449 public static KeyPair load(JSch jsch, String prvkey)
00450 {
00451 String pubkey=prvkey+".pub";
00452
00453 if(!File.Exists(pubkey))
00454 {
00455 pubkey=null;
00456 }
00457 return load(jsch, prvkey, pubkey);
00458 }
00459 public static KeyPair load(JSch jsch, String prvkey, String pubkey)
00460 {
00461
00462 byte[] iv=new byte[8];
00463 bool encrypted=true;
00464 byte[] data=null;
00465
00466 byte[] publickeyblob=null;
00467
00468 int type=ERROR;
00469 int vendor=VENDOR_OPENSSH;
00470
00471 try
00472 {
00473
00474 FileStream fis=File.OpenRead(prvkey);
00475 byte[] buf=new byte[(int)(fis.Length)];
00476 int len=fis.Read(buf, 0, buf.Length);
00477 fis.Close();
00478
00479 int i=0;
00480
00481 while(i<len)
00482 {
00483 if(buf[i]=='B'&& buf[i+1]=='E'&& buf[i+2]=='G'&& buf[i+3]=='I')
00484 {
00485 i+=6;
00486 if(buf[i]=='D'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=DSA; }
00487 else if(buf[i]=='R'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=RSA; }
00488 else if(buf[i]=='S'&& buf[i+1]=='S'&& buf[i+2]=='H')
00489 {
00490 type=UNKNOWN;
00491 vendor=VENDOR_FSECURE;
00492 }
00493 else
00494 {
00495
00496 throw new JSchException("invaid privatekey: "+prvkey);
00497 }
00498 i+=3;
00499 continue;
00500 }
00501 if(buf[i]=='C'&& buf[i+1]=='B'&& buf[i+2]=='C'&& buf[i+3]==',')
00502 {
00503 i+=4;
00504 for(int ii=0; ii<iv.Length; ii++)
00505 {
00506 iv[ii]=(byte)(((a2b(buf[i++])<<4)&0xf0)+(a2b(buf[i++])&0xf));
00507 }
00508 continue;
00509 }
00510 if(buf[i]==0x0d &&
00511 i+1<buf.Length && buf[i+1]==0x0a)
00512 {
00513 i++;
00514 continue;
00515 }
00516 if(buf[i]==0x0a && i+1<buf.Length)
00517 {
00518 if(buf[i+1]==0x0a){ i+=2; break; }
00519 if(buf[i+1]==0x0d &&
00520 i+2<buf.Length && buf[i+2]==0x0a)
00521 {
00522 i+=3; break;
00523 }
00524 bool inheader=false;
00525 for(int j=i+1; j<buf.Length; j++)
00526 {
00527 if(buf[j]==0x0a) break;
00528
00529 if(buf[j]==':'){inheader=true; break;}
00530 }
00531 if(!inheader)
00532 {
00533 i++;
00534 encrypted=false;
00535 break;
00536 }
00537 }
00538 i++;
00539 }
00540
00541 if(type==ERROR)
00542 {
00543 throw new JSchException("invaid privatekey: "+prvkey);
00544 }
00545
00546 int start=i;
00547 while(i<len)
00548 {
00549 if(buf[i]==0x0a)
00550 {
00551 bool xd=(buf[i-1]==0x0d);
00552 Array.Copy(buf, i+1,
00553 buf,
00554 i-(xd ? 1 : 0),
00555 len-i-1-(xd ? 1 : 0)
00556 );
00557 if(xd)len--;
00558 len--;
00559 continue;
00560 }
00561 if(buf[i]=='-'){ break; }
00562 i++;
00563 }
00564 data=Util.fromBase64(buf, start, i-start);
00565
00566 if(data.Length>4 &&
00567 data[0]==(byte)0x3f &&
00568 data[1]==(byte)0x6f &&
00569 data[2]==(byte)0xf9 &&
00570 data[3]==(byte)0xeb)
00571 {
00572
00573 Buffer _buf=new Buffer(data);
00574 _buf.getInt();
00575 _buf.getInt();
00576 byte[]_type=_buf.getString();
00577
00578 byte[] _cipher=_buf.getString();
00579 String cipher=Util.getString(_cipher);
00580
00581 if(cipher.Equals("3des-cbc"))
00582 {
00583 _buf.getInt();
00584 byte[] foo=new byte[data.Length-_buf.getOffSet()];
00585 _buf.getByte(foo);
00586 data=foo;
00587 encrypted=true;
00588 throw new JSchException("unknown privatekey format: "+prvkey);
00589 }
00590 else if(cipher.Equals("none"))
00591 {
00592 _buf.getInt();
00593 _buf.getInt();
00594
00595 encrypted=false;
00596
00597 byte[] foo=new byte[data.Length-_buf.getOffSet()];
00598 _buf.getByte(foo);
00599 data=foo;
00600 }
00601 }
00602
00603 if(pubkey!=null)
00604 {
00605 try
00606 {
00607
00608 fis=File.OpenRead(pubkey);
00609 buf=new byte[(int)(fis.Length)];
00610 len=fis.Read(buf, 0, buf.Length);
00611 fis.Close();
00612
00613 if(buf.Length>4 &&
00614 buf[0]=='-' && buf[1]=='-' && buf[2]=='-' && buf[3]=='-')
00615 {
00616
00617 bool valid=true;
00618 i=0;
00619 do{i++;}while(buf.Length>i && buf[i]!=0x0a);
00620 if(buf.Length<=i) {valid=false;}
00621
00622 while(valid)
00623 {
00624 if(buf[i]==0x0a)
00625 {
00626 bool inheader=false;
00627 for(int j=i+1; j<buf.Length; j++)
00628 {
00629 if(buf[j]==0x0a) break;
00630 if(buf[j]==':'){inheader=true; break;}
00631 }
00632 if(!inheader)
00633 {
00634 i++;
00635 break;
00636 }
00637 }
00638 i++;
00639 }
00640 if(buf.Length<=i){valid=false;}
00641
00642 start=i;
00643 while(valid && i<len)
00644 {
00645 if(buf[i]==0x0a)
00646 {
00647 Array.Copy(buf, i+1, buf, i, len-i-1);
00648 len--;
00649 continue;
00650 }
00651 if(buf[i]=='-'){ break; }
00652 i++;
00653 }
00654 if(valid)
00655 {
00656 publickeyblob=Util.fromBase64(buf, start, i-start);
00657 if(type==UNKNOWN)
00658 {
00659 if(publickeyblob[8]=='d'){ type=DSA; }
00660 else if(publickeyblob[8]=='r'){ type=RSA; }
00661 }
00662 }
00663 }
00664 else
00665 {
00666 if(buf[0]=='s'&& buf[1]=='s'&& buf[2]=='h' && buf[3]=='-')
00667 {
00668 i=0;
00669 while(i<len){ if(buf[i]==' ')break; i++;} i++;
00670 if(i<len)
00671 {
00672 start=i;
00673 while(i<len){ if(buf[i]==' ')break; i++;}
00674 publickeyblob=Util.fromBase64(buf, start, i-start);
00675 }
00676 }
00677 }
00678 }
00679 catch
00680 {
00681 }
00682 }
00683 }
00684 catch(Exception e)
00685 {
00686 if(e is JSchException) throw (JSchException)e;
00687 throw new JSchException(e.ToString());
00688 }
00689
00690 KeyPair kpair=null;
00691 if(type==DSA){ kpair=new KeyPairDSA(jsch); }
00692 else if(type==RSA){ kpair=new KeyPairRSA(jsch); }
00693
00694 if(kpair!=null)
00695 {
00696 kpair.encrypted=encrypted;
00697 kpair.publickeyblob=publickeyblob;
00698 kpair.vendor=vendor;
00699
00700 if(encrypted)
00701 {
00702 kpair.iv=iv;
00703 kpair.data=data;
00704 }
00705 else
00706 {
00707 if(kpair.parse(data))
00708 {
00709 return kpair;
00710 }
00711 else
00712 {
00713 throw new JSchException("invaid privatekey: "+prvkey);
00714 }
00715 }
00716 }
00717
00718 return kpair;
00719 }
00720
00721 static private byte a2b(byte c)
00722 {
00723 if('0'<=c&&c<='9') return (byte)(c-'0');
00724 return (byte)(c-'a'+10);
00725 }
00726 static private byte b2a(byte c)
00727 {
00728 if(0<=c&&c<=9) return (byte)(c+'0');
00729 return (byte)(c-10+'A');
00730 }
00731
00732 public virtual void dispose()
00733 {
00734 passphrase=null;
00735 }
00736 }
00737
00738 }