1 2 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="\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 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 } 119 catch(Exception 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 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(comment.getBytes()); 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("---- 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 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 { 202 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 } 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 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 } 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 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]; 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'){ type=UNKNOWN; 446 vendor=VENDOR_FSECURE; 447 } 448 else{ 449 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]==':'){inheader=true; break;} 478 } 479 if(!inheader){ 480 i++; 481 encrypted=false; 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 && 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(); _buf.getInt(); 519 byte[]_type=_buf.getString(); 520 byte[] _cipher=_buf.getString(); 522 String cipher=new String (_cipher); 523 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 && 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 } 666 | Popular Tags |