PageRenderTime 55ms CodeModel.GetById 9ms app.highlight 40ms RepoModel.GetById 1ms app.codeStats 0ms

/SharpSSH/jsch/KeyPair.cs

https://bitbucket.org/ajlennon/sharpssh
C# | 738 lines | 622 code | 68 blank | 48 comment | 96 complexity | 419ca012bac2f6b03603a94d936ea368 MD5 | raw file
  1using System;
  2using System.IO;
  3using System.Runtime.CompilerServices;
  4
  5
  6namespace Tamir.SharpSsh.jsch
  7{
  8	/* -*-mode:java; c-basic-offset:2; -*- */
  9	/*
 10	Copyright (c) 2002,2003,2004 ymnk, JCraft,Inc. All rights reserved.
 11
 12	Redistribution and use in source and binary forms, with or without
 13	modification, are permitted provided that the following conditions are met:
 14
 15	  1. Redistributions of source code must retain the above copyright notice,
 16		 this list of conditions and the following disclaimer.
 17
 18	  2. Redistributions in binary form must reproduce the above copyright 
 19		 notice, this list of conditions and the following disclaimer in 
 20		 the documentation and/or other materials provided with the distribution.
 21
 22	  3. The names of the authors may not be used to endorse or promote products
 23		 derived from this software without specific prior written permission.
 24
 25	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
 26	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 27	FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
 28	INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
 29	INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 30	LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 31	OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 32	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 33	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 34	EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 35	*/
 36
 37	public abstract class KeyPair
 38	{
 39		public const int ERROR=0;
 40		public const int DSA=1;
 41		public const int RSA=2;
 42		public const int UNKNOWN=3;
 43
 44		internal const int VENDOR_OPENSSH=0;
 45		internal const int VENDOR_FSECURE=1;
 46		internal int vendor=VENDOR_OPENSSH;
 47
 48		private static byte[] cr=Util.getBytes("\n");
 49
 50		public static KeyPair genKeyPair(JSch jsch, int type)
 51		{
 52			return genKeyPair(jsch, type, 1024);
 53		}
 54		public static KeyPair genKeyPair(JSch jsch, int type, int key_size)
 55		{
 56			KeyPair kpair=null;
 57			if(type==DSA){ kpair=new KeyPairDSA(jsch); }
 58			else if(type==RSA){ kpair=new KeyPairRSA(jsch); }
 59			if(kpair!=null)
 60			{
 61				kpair.generate(key_size);
 62			}
 63			return kpair;
 64		}
 65
 66		internal abstract void generate(int key_size);
 67
 68		internal abstract byte[] getBegin();
 69		internal abstract byte[] getEnd();
 70		public abstract int getKeySize();
 71
 72		internal JSch jsch=null;
 73		private Cipher cipher;
 74		private HASH hash;
 75		private Random random;
 76
 77		private byte[] passphrase;
 78
 79		public KeyPair(JSch jsch)
 80		{
 81			this.jsch=jsch;
 82		}
 83
 84		static byte[][] header={Util.getBytes( "Proc-Type: 4,ENCRYPTED"),
 85								   Util.getBytes("DEK-Info: DES-EDE3-CBC,")};
 86
 87		internal abstract byte[] getPrivateKey();
 88
 89		private void Write(Stream s, byte[] arr)
 90		{
 91			s.Write(arr, 0, arr.Length);
 92		}
 93
 94		public void writePrivateKey(Stream outs)
 95		{
 96			byte[] plain=getPrivateKey();
 97			byte[][] _iv=new byte[1][];
 98			byte[] encoded=encrypt(plain, _iv);
 99			byte[] iv=_iv[0];
100			byte[] prv=Util.toBase64(encoded, 0, encoded.Length);
101
102			try
103			{
104				Write(outs, getBegin()); Write(outs,cr);
105				if(passphrase!=null)
106				{
107					Write(outs, header[0]); Write(outs,cr);
108					Write(outs, header[1]); 
109					for(int j=0; j<iv.Length; j++)
110					{
111						outs.WriteByte(b2a((byte)((iv[j]>>4)&0x0f)));
112						outs.WriteByte(b2a((byte)(iv[j]&0x0f)));
113					}
114					Write(outs,cr);
115					Write(outs,cr);
116				}
117				int i=0;
118				while(i<prv.Length)
119				{
120					if(i+64<prv.Length)
121					{
122						outs.Write(prv, i, 64);
123						Write(outs,cr);
124						i+=64;
125						continue;
126					}
127					outs.Write(prv, i, prv.Length-i);
128					Write(outs,cr);
129					break;
130				}
131				Write(outs, getEnd()); Write(outs,cr);
132				//outs.close();
133			}
134			catch(System.Exception e)
135			{
136				Console.WriteLine(e);
137			}
138		}
139
140		private static byte[] space=Util.getBytes(" ");
141
142		internal abstract byte[] getKeyTypeName();
143		public abstract int getKeyType();
144
145		public virtual byte[] getPublicKeyBlob(){ return publickeyblob; }
146
147		public void writePublicKey(Stream outs, String comment)
148		{
149			byte[] pubblob=getPublicKeyBlob();
150			byte[] pub=Util.toBase64(pubblob, 0, pubblob.Length);
151			try
152			{
153				Write(outs, getKeyTypeName()); Write(outs, space);
154				outs.Write(pub, 0, pub.Length); Write(outs, space);
155				Write(outs, Util.getBytes( comment));
156				Write(outs,cr);
157			}
158			catch(System.Exception e)
159			{
160				Console.WriteLine(e);
161			}
162		}
163
164		public void writePublicKey(String name, String comment) 
165		{
166			FileStream fos=new FileStream(name,FileMode.OpenOrCreate);
167			writePublicKey(fos, comment);
168			fos.Close();
169		}
170
171		public void writeSECSHPublicKey(Stream outs, String comment)
172		{
173			byte[] pubblob=getPublicKeyBlob();
174			byte[] pub=Util.toBase64(pubblob, 0, pubblob.Length);
175			try
176			{
177				Write(outs, Util.getBytes( "---- BEGIN SSH2 PUBLIC KEY ----")); Write(outs, cr);
178				Write(outs, Util.getBytes("Comment: \""+comment+"\"")); Write(outs,cr);
179				int index=0;
180				while(index<pub.Length)
181				{
182					int len=70;
183					if((pub.Length-index)<len)len=pub.Length-index;
184					outs.Write(pub, index, len); Write(outs, cr);
185					index+=len;
186				}
187				Write(outs, Util.getBytes("---- END SSH2 PUBLIC KEY ----")); Write(outs,cr);
188			}
189			catch(System.Exception e)
190			{
191				Console.WriteLine(e);
192			}
193		}
194
195		public void writeSECSHPublicKey(String name, String comment)
196		{
197			FileStream fos=new FileStream(name, FileMode.OpenOrCreate);
198			writeSECSHPublicKey(fos, comment);
199			fos.Close();
200		}
201
202
203		public void writePrivateKey(String name) 
204		{
205			FileStream fos=new FileStream(name, FileMode.OpenOrCreate);
206			writePrivateKey(fos);
207			fos.Close();
208		}
209
210		public String getFingerPrint()
211		{
212			if(hash==null) hash=genHash();
213			byte[] kblob=getPublicKeyBlob();
214			if(kblob==null) return null;
215			return getKeySize()+" "+Util.getFingerPrint(hash, kblob);
216		}
217
218		private byte[] encrypt(byte[] plain, byte[][] _iv)
219		{
220			if(passphrase==null) return plain;
221
222			if(cipher==null) cipher=genCipher();
223			byte[] iv=_iv[0]=new byte[cipher.getIVSize()];
224
225			if(random==null) random=genRandom();
226			random.fill(iv, 0, iv.Length);
227
228			byte[] key=genKey(passphrase, iv);
229			byte[] encoded=plain;
230			int bsize=cipher.getBlockSize();
231			if(encoded.Length%bsize!=0)
232			{
233				byte[] foo=new byte[(encoded.Length/bsize+1)*bsize];
234				Array.Copy(encoded, 0, foo, 0, encoded.Length);
235				encoded=foo;
236			}
237
238			try
239			{
240				cipher.init(Cipher.ENCRYPT_MODE, key, iv);
241				cipher.update(encoded, 0, encoded.Length, encoded, 0);
242			}
243			catch(System.Exception e)
244			{
245				Console.WriteLine(e);
246			}
247			return encoded;
248		}
249
250		internal abstract bool parse(byte[] data);
251
252		private byte[] decrypt(byte[] data, byte[] passphrase, byte[] iv)
253		{
254			/*
255			if(iv==null){  // FSecure
256			  iv=new byte[8];
257			  for(int i=0; i<iv.Length; i++)iv[i]=0;
258			}
259			*/
260			try
261			{
262				byte[] key=genKey(passphrase, iv);
263				cipher.init(Cipher.DECRYPT_MODE, key, iv);
264				byte[] plain=new byte[data.Length];
265				cipher.update(data, 0, data.Length, plain, 0);
266				return plain;
267			}
268			catch(System.Exception e)
269			{
270				Console.WriteLine(e);
271			}
272			return null;
273		}
274
275		internal int writeSEQUENCE(byte[] buf, int index, int len)
276		{
277			buf[index++]=0x30;
278			index=writeLength(buf, index, len);
279			return index;
280		}
281		internal int writeINTEGER(byte[] buf, int index, byte[] data)
282		{
283			buf[index++]=0x02;
284			index=writeLength(buf, index, data.Length);
285			Array.Copy(data, 0, buf, index, data.Length);
286			index+=data.Length;
287			return index;
288		}
289
290		internal int countLength(int len)
291		{
292			int i=1;
293			if(len<=0x7f) return i;
294			while(len>0)
295			{
296				len>>=8;
297				i++;
298			}
299			return i;
300		}
301
302		internal int writeLength(byte[] data, int index, int len)
303		{
304			int i=countLength(len)-1;
305			if(i==0)
306			{
307				data[index++]=(byte)len;
308				return index;
309			}
310			data[index++]=(byte)(0x80|i);
311			int j=index+i;
312			while(i>0)
313			{
314				data[index+i-1]=(byte)(len&0xff);
315				len>>=8;
316				i--;
317			}
318			return j;
319		}
320
321		private Random genRandom()
322		{
323			if(random==null)
324			{
325				try
326				{
327					Type t=Type.GetType(jsch.getConfig("random"));
328					random=(Random)Activator.CreateInstance(t);
329				}
330				catch(System.Exception e){ Console.Error.WriteLine("connect: random "+e); }
331			}
332			return random;
333		}
334
335		private HASH genHash()
336		{
337			try
338			{
339				Type t=Type.GetType(jsch.getConfig("md5"));
340				hash=(HASH)Activator.CreateInstance(t);
341				hash.init();
342			}
343			catch//(System.Exception e)
344			{
345			}
346			return hash;
347		}
348		private Cipher genCipher()
349		{
350			try
351			{
352				Type t;
353				t=Type.GetType(jsch.getConfig("3des-cbc"));
354				cipher=(Cipher)(Activator.CreateInstance(t));
355			}
356			catch//(System.Exception e)
357			{
358			}
359			return cipher;
360		}
361
362		/*
363		  hash is MD5
364		  h(0) <- hash(passphrase, iv);
365		  h(n) <- hash(h(n-1), passphrase, iv);
366		  key <- (h(0),...,h(n))[0,..,key.Length];
367		*/
368		[MethodImpl(MethodImplOptions.Synchronized)]
369		internal byte[] genKey(byte[] passphrase, byte[] iv)
370		{
371			if(cipher==null) cipher=genCipher();
372			if(hash==null) hash=genHash();
373
374			byte[] key=new byte[cipher.getBlockSize()];
375			int hsize=hash.getBlockSize();
376			byte[] hn=new byte[key.Length/hsize*hsize+
377				(key.Length%hsize==0?0:hsize)];
378			try
379			{
380				byte[] tmp=null;
381				if(vendor==VENDOR_OPENSSH)
382				{
383					for(int index=0; index+hsize<=hn.Length;)
384					{
385						if(tmp!=null){ hash.update(tmp, 0, tmp.Length); }
386						hash.update(passphrase, 0, passphrase.Length);
387						hash.update(iv, 0, iv.Length);
388						tmp=hash.digest();
389						Array.Copy(tmp, 0, hn, index, tmp.Length);
390						index+=tmp.Length;
391					}
392					Array.Copy(hn, 0, key, 0, key.Length); 
393				}
394				else if(vendor==VENDOR_FSECURE)
395				{
396					for(int index=0; index+hsize<=hn.Length;)
397					{
398						if(tmp!=null){ hash.update(tmp, 0, tmp.Length); }
399						hash.update(passphrase, 0, passphrase.Length);
400						tmp=hash.digest();
401						Array.Copy(tmp, 0, hn, index, tmp.Length);
402						index+=tmp.Length;
403					}
404					Array.Copy(hn, 0, key, 0, key.Length); 
405				}
406			}
407			catch(System.Exception e)
408			{
409				Console.WriteLine(e);
410			}
411			return key;
412		} 
413
414		public void setPassphrase(String passphrase)
415		{
416			if(passphrase==null || passphrase.Length==0)
417			{
418				setPassphrase((byte[])null);
419			}
420			else
421			{
422				setPassphrase(Util.getBytes( passphrase ));
423			}
424		}
425		public void setPassphrase(byte[] passphrase)
426		{
427			if(passphrase!=null && passphrase.Length==0) 
428				passphrase=null;
429			this.passphrase=passphrase;
430		}
431
432		private bool encrypted=false;
433		private byte[] data=null;
434		private byte[] iv=null;
435		private byte[] publickeyblob=null;
436
437		public bool isEncrypted(){ return encrypted; }
438		public bool decrypt(String _passphrase)
439		{
440			byte[] passphrase= Util.getBytes( _passphrase );
441			byte[] foo=decrypt(data, passphrase, iv);
442			if(parse(foo))
443			{
444				encrypted=false;
445			}
446			return !encrypted;
447		}
448
449		public static KeyPair load(JSch jsch, String prvkey)
450		{
451			String pubkey=prvkey+".pub";
452//			if(!new File(pubkey).exists())
453			if(!File.Exists(pubkey))
454			{
455				pubkey=null;
456			}
457			return load(jsch, prvkey, pubkey);
458		}
459		public static KeyPair load(JSch jsch, String prvkey, String pubkey)
460		{
461
462			byte[] iv=new byte[8];       // 8
463			bool encrypted=true;
464			byte[] data=null;
465
466			byte[] publickeyblob=null;
467
468			int type=ERROR;
469			int vendor=VENDOR_OPENSSH;
470
471			try
472			{
473				//File file=new File(prvkey);
474				FileStream fis=File.OpenRead(prvkey);
475				byte[] buf=new byte[(int)(fis.Length)];
476				int len=fis.Read(buf, 0, buf.Length);
477				fis.Close();
478
479				int i=0;
480
481				while(i<len)
482				{
483					if(buf[i]=='B'&& buf[i+1]=='E'&& buf[i+2]=='G'&& buf[i+3]=='I')
484					{
485						i+=6;	    
486						if(buf[i]=='D'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=DSA; }
487						else if(buf[i]=='R'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=RSA; }
488						else if(buf[i]=='S'&& buf[i+1]=='S'&& buf[i+2]=='H')
489						{ // FSecure
490							type=UNKNOWN;
491							vendor=VENDOR_FSECURE;
492						}
493						else
494						{
495							//System.outs.println("invalid format: "+identity);
496							throw new JSchException("invaid privatekey: "+prvkey);
497						}
498						i+=3;
499						continue;
500					}
501					if(buf[i]=='C'&& buf[i+1]=='B'&& buf[i+2]=='C'&& buf[i+3]==',')
502					{
503						i+=4;
504						for(int ii=0; ii<iv.Length; ii++)
505						{
506							iv[ii]=(byte)(((a2b(buf[i++])<<4)&0xf0)+(a2b(buf[i++])&0xf));
507						}
508						continue;
509					}
510					if(buf[i]==0x0d &&
511						i+1<buf.Length && buf[i+1]==0x0a)
512					{
513						i++;
514						continue;
515					}
516					if(buf[i]==0x0a && i+1<buf.Length)
517					{
518						if(buf[i+1]==0x0a){ i+=2; break; }
519						if(buf[i+1]==0x0d &&
520							i+2<buf.Length && buf[i+2]==0x0a)
521						{
522							i+=3; break;
523						}
524						bool inheader=false;
525						for(int j=i+1; j<buf.Length; j++)
526						{
527							if(buf[j]==0x0a) break;
528							//if(buf[j]==0x0d) break;
529							if(buf[j]==':'){inheader=true; break;}
530						}
531						if(!inheader)
532						{
533							i++; 
534							encrypted=false;    // no passphrase
535							break;
536						}
537					}
538					i++;
539				}
540
541				if(type==ERROR)
542				{
543					throw new JSchException("invaid privatekey: "+prvkey);
544				}
545
546				int start=i;
547				while(i<len)
548				{
549					if(buf[i]==0x0a)
550					{
551						bool xd=(buf[i-1]==0x0d);
552						Array.Copy(buf, i+1, 
553							buf, 
554							i-(xd ? 1 : 0), 
555							len-i-1-(xd ? 1 : 0)
556							);
557						if(xd)len--;
558						len--;
559						continue;
560					}
561					if(buf[i]=='-'){  break; }
562					i++;
563				}
564				data=Util.fromBase64(buf, start, i-start);
565
566				if(data.Length>4 &&            // FSecure
567					data[0]==(byte)0x3f &&
568					data[1]==(byte)0x6f &&
569					data[2]==(byte)0xf9 &&
570					data[3]==(byte)0xeb)
571				{
572
573					Buffer _buf=new Buffer(data);
574					_buf.getInt();  // 0x3f6ff9be
575					_buf.getInt();
576					byte[]_type=_buf.getString();
577					//System.outs.println("type: "+new String(_type)); 
578					byte[] _cipher=_buf.getString();
579					String cipher=Util.getString(_cipher);
580					//System.outs.println("cipher: "+cipher); 
581					if(cipher.Equals("3des-cbc"))
582					{
583						_buf.getInt();
584						byte[] foo=new byte[data.Length-_buf.getOffSet()];
585						_buf.getByte(foo);
586						data=foo;
587						encrypted=true;
588						throw new JSchException("unknown privatekey format: "+prvkey);
589					}
590					else if(cipher.Equals("none"))
591					{
592						_buf.getInt();
593						_buf.getInt();
594
595						encrypted=false;
596
597						byte[] foo=new byte[data.Length-_buf.getOffSet()];
598						_buf.getByte(foo);
599						data=foo;
600					}
601				}
602
603				if(pubkey!=null)
604				{
605					try
606					{
607						//file=new File(pubkey);
608						fis=File.OpenRead(pubkey);
609						buf=new byte[(int)(fis.Length)];
610						len=fis.Read(buf, 0, buf.Length);
611						fis.Close();
612
613						if(buf.Length>4 &&             // FSecure's public key
614							buf[0]=='-' && buf[1]=='-' && buf[2]=='-' && buf[3]=='-')
615						{
616
617							bool valid=true;
618							i=0;
619							do{i++;}while(buf.Length>i && buf[i]!=0x0a);
620							if(buf.Length<=i) {valid=false;}
621
622							while(valid)
623							{
624								if(buf[i]==0x0a)
625								{
626									bool inheader=false;
627									for(int j=i+1; j<buf.Length; j++)
628									{
629										if(buf[j]==0x0a) break;
630										if(buf[j]==':'){inheader=true; break;}
631									}
632									if(!inheader)
633									{
634										i++; 
635										break;
636									}
637								}
638								i++;
639							}
640							if(buf.Length<=i){valid=false;}
641
642							start=i;
643							while(valid && i<len)
644							{
645								if(buf[i]==0x0a)
646								{
647									Array.Copy(buf, i+1, buf, i, len-i-1);
648									len--;
649									continue;
650								}
651								if(buf[i]=='-'){  break; }
652								i++;
653							}
654							if(valid)
655							{
656								publickeyblob=Util.fromBase64(buf, start, i-start);
657								if(type==UNKNOWN)
658								{
659									if(publickeyblob[8]=='d'){ type=DSA; }
660									else if(publickeyblob[8]=='r'){ type=RSA; }
661								}
662							}
663						}
664						else
665						{
666							if(buf[0]=='s'&& buf[1]=='s'&& buf[2]=='h' && buf[3]=='-')
667							{
668								i=0;
669								while(i<len){ if(buf[i]==' ')break; i++;} i++;
670								if(i<len)
671								{
672									start=i;
673									while(i<len){ if(buf[i]==' ')break; i++;}
674									publickeyblob=Util.fromBase64(buf, start, i-start);
675								}
676							}
677						}
678					}
679					catch//(System.Exception ee)
680					{
681					}
682				}
683			}
684			catch(System.Exception e)
685			{
686				if(e is JSchException) throw (JSchException)e;
687				throw new JSchException(e.ToString());
688			}
689
690			KeyPair kpair=null;
691			if(type==DSA){ kpair=new KeyPairDSA(jsch); }
692			else if(type==RSA){ kpair=new KeyPairRSA(jsch); }
693
694			if(kpair!=null)
695			{
696				kpair.encrypted=encrypted;
697				kpair.publickeyblob=publickeyblob;
698				kpair.vendor=vendor;
699
700				if(encrypted)
701				{
702					kpair.iv=iv;
703					kpair.data=data;
704				}
705				else
706				{
707					if(kpair.parse(data))
708					{
709						return kpair;
710					}
711					else
712					{
713						throw new JSchException("invaid privatekey: "+prvkey);
714					}
715				}
716			}
717
718			return kpair;
719		}
720
721		static private byte a2b(byte c)
722		{
723			if('0'<=c&&c<='9') return (byte)(c-'0');
724			return (byte)(c-'a'+10);
725		}
726		static private byte b2a(byte c)
727		{
728			if(0<=c&&c<=9) return (byte)(c+'0');
729			return (byte)(c-10+'A');
730		}
731
732		public virtual void dispose()
733		{
734			passphrase=null;
735		}
736	}
737
738}