/*
        void dump(byte[] foo){
          for(int i=0; i<foo.length; i++){
            if((foo[i]&0xf0)==0)System.out.print("0");
            System.out.print(Integer.toHexString(foo[i]&0xff));
            if(i%16==15){System.out.println(""); continue;}
            if(i%2==1)System.out.print(" ");
          }
        }
        */
        internal static SharpSSH.SharpSsh.java.String[] guess(byte[]I_S, byte[]I_C)
        {
            //System.out.println("guess: ");
            SharpSSH.SharpSsh.java.String[] guess=new SharpSSH.SharpSsh.java.String[PROPOSAL_MAX];
            Buffer sb=new Buffer(I_S); sb.setOffSet(17);
            Buffer cb=new Buffer(I_C); cb.setOffSet(17);

            for(int i=0; i<PROPOSAL_MAX; i++)
            {
                byte[] sp=sb.getString();  // server proposal
                byte[] cp=cb.getString();  // client proposal

                //System.out.println("server-proposal: |"+new String(sp)+"|");
                //System.out.println("client-proposal: |"+new String(cp)+"|");

                int j=0;
                int k=0;
                //System.out.println(new String(cp));
            //loop(using BREAK instead):
                while(j<cp.Length)
                {
                    while(j<cp.Length && cp[j]!=',')j++;
                    if(k==j) return null;
                    String algorithm=Util.getString(cp, k, j-k);
                    //System.out.println("algorithm: "+algorithm);
                    int l=0;
                    int m=0;
                    while(l<sp.Length)
                    {
                        while(l<sp.Length && sp[l]!=',')l++;
                        if(m==l) return null;
                        //System.out.println("  "+new String(sp, m, l-m));
                        if(algorithm.Equals(Util.getString(sp, m, l-m)))
                        {
                            guess[i]=algorithm;
                            //System.out.println("  "+algorithm);
                            goto BREAK;
                        }
                        l++;
                        m=l;
                    }
                    j++;
                    k=j;
                }
            BREAK:
                if(j==0)
                {
                    guess[i]="";
                }
                else if(guess[i]==null)
                {
                    //System.out.println("  fail");
                    return null;
                }
            }

            //    for(int i=0; i<PROPOSAL_MAX; i++){
            //      System.out.println("guess: ["+guess[i]+"]");
            //    }

            return guess;
        }
        public static KeyPair load(JSch jsch, String prvkey, String pubkey)
        {
            byte[] iv=new byte[8];       // 8
            bool encrypted=true;
            byte[] data=null;

            byte[] publickeyblob=null;

            int type=ERROR;
            int vendor=VENDOR_OPENSSH;

            try
            {
                //File file=new File(prvkey);
                FileStream fis=File.OpenRead(prvkey);
                byte[] buf=new byte[(int)(fis.Length)];
                int len=fis.Read(buf, 0, buf.Length);
                fis.Close();

                int i=0;

                while(i<len)
                {
                    if(buf[i]=='B'&& buf[i+1]=='E'&& buf[i+2]=='G'&& buf[i+3]=='I')
                    {
                        i+=6;
                        if(buf[i]=='D'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=DSA; }
                        else if(buf[i]=='R'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=RSA; }
                        else if(buf[i]=='S'&& buf[i+1]=='S'&& buf[i+2]=='H')
                        { // FSecure
                            type=UNKNOWN;
                            vendor=VENDOR_FSECURE;
                        }
                        else
                        {
                            //System.outs.println("invalid format: "+identity);
                            throw new JSchException("invaid privatekey: "+prvkey);
                        }
                        i+=3;
                        continue;
                    }
                    if(buf[i]=='C'&& buf[i+1]=='B'&& buf[i+2]=='C'&& buf[i+3]==',')
                    {
                        i+=4;
                        for(int ii=0; ii<iv.Length; ii++)
                        {
                            iv[ii]=(byte)(((a2b(buf[i++])<<4)&0xf0)+(a2b(buf[i++])&0xf));
                        }
                        continue;
                    }
                    if(buf[i]==0x0d &&
                        i+1<buf.Length && buf[i+1]==0x0a)
                    {
                        i++;
                        continue;
                    }
                    if(buf[i]==0x0a && i+1<buf.Length)
                    {
                        if(buf[i+1]==0x0a){ i+=2; break; }
                        if(buf[i+1]==0x0d &&
                            i+2<buf.Length && buf[i+2]==0x0a)
                        {
                            i+=3; break;
                        }
                        bool inheader=false;
                        for(int j=i+1; j<buf.Length; j++)
                        {
                            if(buf[j]==0x0a) break;
                            //if(buf[j]==0x0d) break;
                            if(buf[j]==':'){inheader=true; break;}
                        }
                        if(!inheader)
                        {
                            i++;
                            encrypted=false;    // no passphrase
                            break;
                        }
                    }
                    i++;
                }

                if(type==ERROR)
                {
                    throw new JSchException("invaid privatekey: "+prvkey);
                }

                int start=i;
                while(i<len)
                {
                    if(buf[i]==0x0a)
                    {
                        bool xd=(buf[i-1]==0x0d);
                        Array.Copy(buf, i+1,
                            buf,
                            i-(xd ? 1 : 0),
                            len-i-1-(xd ? 1 : 0)
                            );
                        if(xd)len--;
                        len--;
                        continue;
                    }
                    if(buf[i]=='-'){  break; }
                    i++;
                }
                data=Util.fromBase64(buf, start, i-start);

                if(data.Length>4 &&            // FSecure
                    data[0]==(byte)0x3f &&
                    data[1]==(byte)0x6f &&
                    data[2]==(byte)0xf9 &&
                    data[3]==(byte)0xeb)
                {

                    Buffer _buf=new Buffer(data);
                    _buf.getInt();  // 0x3f6ff9be
                    _buf.getInt();
                    byte[]_type=_buf.getString();
                    //System.outs.println("type: "+new String(_type));
                    byte[] _cipher=_buf.getString();
                    String cipher=Util.getString(_cipher);
                    //System.outs.println("cipher: "+cipher);
                    if(cipher.Equals("3des-cbc"))
                    {
                        _buf.getInt();
                        byte[] foo=new byte[data.Length-_buf.getOffSet()];
                        _buf.getByte(foo);
                        data=foo;
                        encrypted=true;
                        throw new JSchException("unknown privatekey format: "+prvkey);
                    }
                    else if(cipher.Equals("none"))
                    {
                        _buf.getInt();
                        _buf.getInt();

                        encrypted=false;

                        byte[] foo=new byte[data.Length-_buf.getOffSet()];
                        _buf.getByte(foo);
                        data=foo;
                    }
                }

                if(pubkey!=null)
                {
                    try
                    {
                        //file=new File(pubkey);
                        fis=File.OpenRead(pubkey);
                        buf=new byte[(int)(fis.Length)];
                        len=fis.Read(buf, 0, buf.Length);
                        fis.Close();

                        if(buf.Length>4 &&             // FSecure's public key
                            buf[0]=='-' && buf[1]=='-' && buf[2]=='-' && buf[3]=='-')
                        {

                            bool valid=true;
                            i=0;
                            do{i++;}while(buf.Length>i && buf[i]!=0x0a);
                            if(buf.Length<=i) {valid=false;}

                            while(valid)
                            {
                                if(buf[i]==0x0a)
                                {
                                    bool inheader=false;
                                    for(int j=i+1; j<buf.Length; j++)
                                    {
                                        if(buf[j]==0x0a) break;
                                        if(buf[j]==':'){inheader=true; break;}
                                    }
                                    if(!inheader)
                                    {
                                        i++;
                                        break;
                                    }
                                }
                                i++;
                            }
                            if(buf.Length<=i){valid=false;}

                            start=i;
                            while(valid && i<len)
                            {
                                if(buf[i]==0x0a)
                                {
                                    Array.Copy(buf, i+1, buf, i, len-i-1);
                                    len--;
                                    continue;
                                }
                                if(buf[i]=='-'){  break; }
                                i++;
                            }
                            if(valid)
                            {
                                publickeyblob=Util.fromBase64(buf, start, i-start);
                                if(type==UNKNOWN)
                                {
                                    if(publickeyblob[8]=='d'){ type=DSA; }
                                    else if(publickeyblob[8]=='r'){ type=RSA; }
                                }
                            }
                        }
                        else
                        {
                            if(buf[0]=='s'&& buf[1]=='s'&& buf[2]=='h' && buf[3]=='-')
                            {
                                i=0;
                                while(i<len){ if(buf[i]==' ')break; i++;} i++;
                                if(i<len)
                                {
                                    start=i;
                                    while(i<len){ if(buf[i]==' ')break; i++;}
                                    publickeyblob=Util.fromBase64(buf, start, i-start);
                                }
                            }
                        }
                    }
                    catch//(Exception ee)
                    {
                    }
                }
            }
            catch(Exception e)
            {
                if(e is JSchException) throw (JSchException)e;
                throw new JSchException(e.ToString());
            }

            KeyPair kpair=null;
            if(type==DSA){ kpair=new KeyPairDSA(jsch); }
            else if(type==RSA){ kpair=new KeyPairRSA(jsch); }

            if(kpair!=null)
            {
                kpair.encrypted=encrypted;
                kpair.publickeyblob=publickeyblob;
                kpair.vendor=vendor;

                if(encrypted)
                {
                    kpair.iv=iv;
                    kpair.data=data;
                }
                else
                {
                    if(kpair.parse(data))
                    {
                        return kpair;
                    }
                    else
                    {
                        throw new JSchException("invaid privatekey: "+prvkey);
                    }
                }
            }

            return kpair;
        }
        public override bool next(Buffer _buf)
        {
            int i,j;
            bool result = false;
            switch(state)
            {
                case SSH_MSG_KEXDH_REPLY:
                    // The server responds with:
                    // byte      SSH_MSG_KEXDH_REPLY(31)
                    // string    server public host key and certificates (K_S)
                    // mpint     f
                    // string    signature of H
                    j=_buf.getInt();
                    j=_buf.getByte();
                    j=_buf.getByte();
                    if(j!=31)
                    {
                        Console.WriteLine("type: must be 31 "+j);
                        result = false;
                        break;
                    }

                    K_S=_buf.getString();
                    // K_S is server_key_blob, which includes ....
                    // string ssh-dss
                    // impint p of dsa
                    // impint q of dsa
                    // impint g of dsa
                    // impint pub_key of dsa
                    //System.out.print("K_S: "); //dump(K_S, 0, K_S.length);
                    byte[] f=_buf.getMPInt();
                    byte[] sig_of_H=_buf.getString();
                    /*
              for(int ii=0; ii<sig_of_H.length;ii++){
                System.out.print(Integer.toHexString(sig_of_H[ii]&0xff));
                System.out.print(": ");
              }
              Console.WriteLine("");
                    */

                    dh.setF(f);
                    K=dh.getK();

                    //The hash H is computed as the HASH hash of the concatenation of the
                    //following:
                    // string    V_C, the client's version string (CR and NL excluded)
                    // string    V_S, the server's version string (CR and NL excluded)
                    // string    I_C, the payload of the client's SSH_MSG_KEXINIT
                    // string    I_S, the payload of the server's SSH_MSG_KEXINIT
                    // string    K_S, the host key
                    // mpint     e, exchange value sent by the client
                    // mpint     f, exchange value sent by the server
                    // mpint     K, the shared secret
                    // This value is called the exchange hash, and it is used to authenti-
                    // cate the key exchange.
                    buf.reset();
                    buf.putString(V_C); buf.putString(V_S);
                    buf.putString(I_C); buf.putString(I_S);
                    buf.putString(K_S);
                    buf.putMPInt(e); buf.putMPInt(f);
                    buf.putMPInt(K);
                    byte[] foo=new byte[buf.getLength()];
                    buf.getByte(foo);
                    sha.update(foo, 0, foo.Length);
                    H=sha.digest();
                    //System.out.print("H -> "); //dump(H, 0, H.length);

                    i=0;
                    j=0;
                    j=(int)((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
                        ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
                    String alg=Util.getString(K_S, i, j);
                    i+=j;

                    result=false;

                    if(alg.Equals("ssh-rsa"))
                    {
                        byte[] tmp;
                        byte[] ee;
                        byte[] n;

                        type=RSA;

                        j=(int)((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
                            ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
                        tmp=new byte[j]; Array.Copy(K_S, i, tmp, 0, j); i+=j;
                        ee=tmp;
                        j=(int)((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
                            ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
                        tmp=new byte[j]; Array.Copy(K_S, i, tmp, 0, j); i+=j;
                        n=tmp;

                        //	SignatureRSA sig=new SignatureRSA();
                        //	sig.init();

                        SignatureRSA sig=null;
                        try
                        {
                            Type t=Type.GetType(session.getConfig("signature.rsa"));
                            sig=(SignatureRSA)(Activator.CreateInstance(t));
                            sig.init();
                        }
                        catch(Exception eee)
                        {
                            Console.WriteLine(eee);
                        }

                        sig.setPubKey(ee, n);
                        sig.update(H);
                        result=sig.verify(sig_of_H);
                        //MainClass.dump(ee, n, sig_of_H, H);
                    }
                    else if(alg.Equals("ssh-dss"))
                    {
                        byte[] q=null;
                        byte[] tmp;
                        byte[] p;
                        byte[] g;

                        type=DSS;

                        j=(int)((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
                            ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
                        tmp=new byte[j]; Array.Copy(K_S, i, tmp, 0, j); i+=j;
                        p=tmp;
                        j=(int)((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
                            ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
                        tmp=new byte[j]; Array.Copy(K_S, i, tmp, 0, j); i+=j;
                        q=tmp;
                        j=(int)((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
                            ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
                        tmp=new byte[j]; Array.Copy(K_S, i, tmp, 0, j); i+=j;
                        g=tmp;
                        j=(int)((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
                            ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
                        tmp=new byte[j]; Array.Copy(K_S, i, tmp, 0, j); i+=j;
                        f=tmp;
                        //	SignatureDSA sig=new SignatureDSA();
                        //	sig.init();
                        SignatureDSA sig=null;
                        try
                        {
                            Type t=Type.GetType(session.getConfig("signature.dss"));
                            sig=(SignatureDSA)(Activator.CreateInstance(t));
                            sig.init();
                        }
                        catch(Exception ee)
                        {
                            Console.WriteLine(ee);
                        }
                        sig.setPubKey(f, p, q, g);
                        sig.update(H);
                        result=sig.verify(sig_of_H);
                    }
                    else
                    {
                        Console.WriteLine("unknow alg");
                    }
                    state=STATE_END;
                    break;
            }
            return result;
        }
        public override bool next(Buffer _buf)
        {
            int i,j;
            bool result=false;
            switch(state)
            {
                case SSH_MSG_KEX_DH_GEX_GROUP:
                    // byte  SSH_MSG_KEX_DH_GEX_GROUP(31)
                    // mpint p, safe prime
                    // mpint g, generator for subgroup in GF (p)
                    _buf.getInt();
                    _buf.getByte();
                    j=_buf.getByte();
                    if(j!=31)
                    {
                        Console.WriteLine("type: must be 31 "+j);
                        result = false;
                    }

                    p=_buf.getMPInt();
                    g=_buf.getMPInt();
                    /*
              for(int iii=0; iii<p.length; iii++){
              System.out.println("0x"+Integer.toHexString(p[iii]&0xff)+",");
              }
              System.out.println("");
              for(int iii=0; iii<g.length; iii++){
              System.out.println("0x"+Integer.toHexString(g[iii]&0xff)+",");
              }
                    */
                    dh.setP(p);
                    dh.setG(g);

                    // The client responds with:
                    // byte  SSH_MSG_KEX_DH_GEX_INIT(32)
                    // mpint e <- g^x mod p
                    //         x is a random number (1 < x < (p-1)/2)

                    e=dh.getE();

                    packet.reset();
                    buf.putByte((byte)0x20);
                    buf.putMPInt(e);
                    session.write(packet);

                    state=SSH_MSG_KEX_DH_GEX_REPLY;
                    result = true;
                    break;

                case SSH_MSG_KEX_DH_GEX_REPLY:
                    // The server responds with:
                    // byte      SSH_MSG_KEX_DH_GEX_REPLY(33)
                    // string    server public host key and certificates (K_S)
                    // mpint     f
                    // string    signature of H
                    j=_buf.getInt();
                    j=_buf.getByte();
                    j=_buf.getByte();
                    if(j!=33)
                    {
                        Console.WriteLine("type: must be 33 "+j);
                        result = false;
                    }

                    K_S=_buf.getString();
                    // K_S is server_key_blob, which includes ....
                    // string ssh-dss
                    // impint p of dsa
                    // impint q of dsa
                    // impint g of dsa
                    // impint pub_key of dsa
                    //System.out.print("K_S: "); dump(K_S, 0, K_S.length);

                    byte[] f=_buf.getMPInt();
                    byte[] sig_of_H=_buf.getString();

                    dh.setF(f);
                    K=dh.getK();

                    //The hash H is computed as the HASH hash of the concatenation of the
                    //following:
                    // string    V_C, the client's version string (CR and NL excluded)
                    // string    V_S, the server's version string (CR and NL excluded)
                    // string    I_C, the payload of the client's SSH_MSG_KEXINIT
                    // string    I_S, the payload of the server's SSH_MSG_KEXINIT
                    // string    K_S, the host key
                    // uint32    min, minimal size in bits of an acceptable group
                    // uint32   n, preferred size in bits of the group the server should send
                    // uint32    max, maximal size in bits of an acceptable group
                    // mpint     p, safe prime
                    // mpint     g, generator for subgroup
                    // mpint     e, exchange value sent by the client
                    // mpint     f, exchange value sent by the server
                    // mpint     K, the shared secret
                    // This value is called the exchange hash, and it is used to authenti-
                    // cate the key exchange.

                    buf.reset();
                    buf.putString(V_C); buf.putString(V_S);
                    buf.putString(I_C); buf.putString(I_S);
                    buf.putString(K_S);
                    buf.putInt(min); buf.putInt(preferred); buf.putInt(max);
                    buf.putMPInt(p); buf.putMPInt(g); buf.putMPInt(e); buf.putMPInt(f);
                    buf.putMPInt(K);

                    byte[] foo=new byte[buf.getLength()];
                    buf.getByte(foo);
                    sha.update(foo, 0, foo.Length);

                    H=sha.digest();

                    // System.out.print("H -> "); dump(H, 0, H.length);

                    i=0;
                    j=0;
                    j=(int)((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
                        ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
                    String alg=Util.getString(K_S, i, j);
                    i+=j;

                    if(alg.Equals("ssh-rsa"))
                    {
                        byte[] tmp;
                        byte[] ee;
                        byte[] n;

                        type=RSA;

                        j=(int)((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
                            ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
                        tmp=new byte[j]; Array.Copy(K_S, i, tmp, 0, j); i+=j;
                        ee=tmp;
                        j=(int)((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
                            ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
                        tmp=new byte[j]; Array.Copy(K_S, i, tmp, 0, j); i+=j;
                        n=tmp;

                        //	SignatureRSA sig=new SignatureRSA();
                        //	sig.init();

                        SignatureRSA sig=null;
                        try
                        {
                            Type t=Type.GetType(session.getConfig("signature.rsa"));
                            sig=(SignatureRSA)(Activator.CreateInstance(t));
                            sig.init();
                        }
                        catch(Exception eee)
                        {
                            Console.WriteLine(eee);
                        }

                        sig.setPubKey(ee, n);
                        sig.update(H);
                        result=sig.verify(sig_of_H);
                    }
                    else if(alg.Equals("ssh-dss"))
                    {
                        byte[] q=null;
                        byte[] tmp;

                        type=DSS;

                        j=(int)((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
                            ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
                        tmp=new byte[j]; Array.Copy(K_S, i, tmp, 0, j); i+=j;
                        p=tmp;
                        j=(int)((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
                            ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
                        tmp=new byte[j]; Array.Copy(K_S, i, tmp, 0, j); i+=j;
                        q=tmp;
                        j=(int)((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
                            ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
                        tmp=new byte[j]; Array.Copy(K_S, i, tmp, 0, j); i+=j;
                        g=tmp;
                        j=(int)((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
                            ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
                        tmp=new byte[j]; Array.Copy(K_S, i, tmp, 0, j); i+=j;
                        f=tmp;

                        //	SignatureDSA sig=new SignatureDSA();
                        //	sig.init();

                        SignatureDSA sig=null;
                        try
                        {
                            Type t=Type.GetType(session.getConfig("signature.dss"));
                            sig=(SignatureDSA)(Activator.CreateInstance(t));
                            sig.init();
                        }
                        catch(Exception ee)
                        {
                            Console.WriteLine(ee);
                        }

                        sig.setPubKey(f, p, q, g);
                        sig.update(H);
                        result=sig.verify(sig_of_H);
                    }
                    else
                    {
                        Console.WriteLine("unknow alg");
                    }
                    state=STATE_END;
                    break;
            }
            return result;
        }
 internal static SftpATTRS getATTR(Buffer buf)
 {
     SftpATTRS attr=new SftpATTRS();
     attr.flags=buf.getInt();
     if((attr.flags&SSH_FILEXFER_ATTR_SIZE)!=0){ attr.size=buf.getLong(); }
     if((attr.flags&SSH_FILEXFER_ATTR_UIDGID)!=0)
     {
         attr.uid=buf.getInt(); attr.gid=buf.getInt();
     }
     if((attr.flags&SSH_FILEXFER_ATTR_PERMISSIONS)!=0)
     {
         attr.permissions=buf.getInt();
     }
     if((attr.flags&SSH_FILEXFER_ATTR_ACMODTIME)!=0)
     {
         attr.atime=buf.getInt();
     }
     if((attr.flags&SSH_FILEXFER_ATTR_ACMODTIME)!=0)
     {
         attr.mtime=buf.getInt();
     }
     if((attr.flags&SSH_FILEXFER_ATTR_EXTENDED)!=0)
     {
         int count=buf.getInt();
         if(count>0)
         {
             attr.extended=new String[count*2];
             for(int i=0; i<count; i++)
             {
                 attr.extended[i*2]=Util.getString(buf.getString());
                 attr.extended[i*2+1]=Util.getString(buf.getString());
             }
         }
     }
     return attr;
 }
        internal IdentityFile(String identity, JSch jsch)
        {
            this.identity=identity;
            this.jsch=jsch;
            try
            {
                Type c=Type.GetType(jsch.getConfig("3des-cbc"));
                cipher=(Cipher)Activator.CreateInstance(c);
                key=new byte[cipher.getBlockSize()];   // 24
                iv=new byte[cipher.getIVSize()];       // 8
                c=Type.GetType(jsch.getConfig("md5"));
                hash=(HASH)(Activator.CreateInstance(c));
                hash.init();
                FileInfo file=new FileInfo(identity);
                FileStream fis = File.OpenRead(identity);
                byte[] buf=new byte[(int)(file.Length)];
                int len=fis.Read(buf, 0, buf.Length);
                fis.Close();

                int i=0;
                while(i<len)
                {
                    if(buf[i]=='B'&& buf[i+1]=='E'&& buf[i+2]=='G'&& buf[i+3]=='I')
                    {
                        i+=6;
                        if(buf[i]=='D'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=DSS; }
                        else if(buf[i]=='R'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=RSA; }
                        else if(buf[i]=='S'&& buf[i+1]=='S'&& buf[i+2]=='H')
                        { // FSecure
                            type=UNKNOWN;
                            keytype=FSECURE;
                        }
                        else
                        {
                            //System.out.println("invalid format: "+identity);
                            throw new JSchException("invaid privatekey: "+identity);
                        }
                        i+=3;
                        continue;
                    }
                    if(buf[i]=='C'&& buf[i+1]=='B'&& buf[i+2]=='C'&& buf[i+3]==',')
                    {
                        i+=4;
                        for(int ii=0; ii<iv.Length; ii++)
                        {
                            iv[ii]=(byte)(((a2b(buf[i++])<<4)&0xf0)+
                                (a2b(buf[i++])&0xf));
                        }
                        continue;
                    }
                    if(buf[i]==0x0d &&
                        i+1<buf.Length && buf[i+1]==0x0a)
                    {
                        i++;
                        continue;
                    }
                    if(buf[i]==0x0a && i+1<buf.Length)
                    {
                        if(buf[i+1]==0x0a){ i+=2; break; }
                        if(buf[i+1]==0x0d &&
                            i+2<buf.Length && buf[i+2]==0x0a)
                        {
                            i+=3; break;
                        }
                        bool inheader=false;
                        for(int j=i+1; j<buf.Length; j++)
                        {
                            if(buf[j]==0x0a) break;
                            //if(buf[j]==0x0d) break;
                            if(buf[j]==':'){inheader=true; break;}
                        }
                        if(!inheader)
                        {
                            i++;
                            encrypted=false;    // no passphrase
                            break;
                        }
                    }
                    i++;
                }

                if(type==ERROR)
                {
                    throw new JSchException("invaid privatekey: "+identity);
                }

                int start=i;
                while(i<len)
                {
                    if(buf[i]==0x0a)
                    {
                        bool xd=(buf[i-1]==0x0d);
                        Array.Copy(buf, i+1,
                            buf,
                            i-(xd ? 1 : 0),
                            len-i-1-(xd ? 1 : 0)
                            );
                        if(xd)len--;
                        len--;
                        continue;
                    }
                    if(buf[i]=='-'){  break; }
                    i++;
                }
                encoded_data=Util.fromBase64(buf, start, i-start);

                if(encoded_data.Length>4 &&            // FSecure
                    encoded_data[0]==(byte)0x3f &&
                    encoded_data[1]==(byte)0x6f &&
                    encoded_data[2]==(byte)0xf9 &&
                    encoded_data[3]==(byte)0xeb)
                {

                    Buffer _buf=new Buffer(encoded_data);
                    _buf.getInt();  // 0x3f6ff9be
                    _buf.getInt();
                    byte[]_type=_buf.getString();
                    //System.out.println("type: "+new String(_type));
                    byte[] _cipher=_buf.getString();
                    String s_cipher=System.Text.Encoding.Default.GetString(_cipher);
                    //System.out.println("cipher: "+cipher);
                    if(s_cipher.Equals("3des-cbc"))
                    {
                        _buf.getInt();
                        byte[] foo=new byte[encoded_data.Length-_buf.getOffSet()];
                        _buf.getByte(foo);
                        encoded_data=foo;
                        encrypted=true;
                        throw new JSchException("unknown privatekey format: "+identity);
                    }
                    else if(s_cipher.Equals("none"))
                    {
                        _buf.getInt();
                        //_buf.getInt();

                        encrypted=false;

                        byte[] foo=new byte[encoded_data.Length-_buf.getOffSet()];
                        _buf.getByte(foo);
                        encoded_data=foo;
                    }

                }

                try
                {
                    file=new FileInfo(identity+".pub");
                    fis=File.OpenRead(identity+".pub");
                    buf=new byte[(int)(file.Length)];
                    len=fis.Read(buf, 0, buf.Length);
                    fis.Close();
                }
                catch
                {
                    return;
                }

                if(buf.Length>4 &&             // FSecure's public key
                    buf[0]=='-' && buf[1]=='-' && buf[2]=='-' && buf[3]=='-')
                {

                    i=0;
                    do{i++;}while(buf.Length>i && buf[i]!=0x0a);
                    if(buf.Length<=i) return;

                    while(true)
                    {
                        if(buf[i]==0x0a)
                        {
                            bool inheader=false;
                            for(int j=i+1; j<buf.Length; j++)
                            {
                                if(buf[j]==0x0a) break;
                                if(buf[j]==':'){inheader=true; break;}
                            }
                            if(!inheader)
                            {
                                i++;
                                break;
                            }
                        }
                        i++;
                    }
                    if(buf.Length<=i) return;

                    start=i;
                    while(i<len)
                    {
                        if(buf[i]==0x0a)
                        {
                            Array.Copy(buf, i+1, buf, i, len-i-1);
                            len--;
                            continue;
                        }
                        if(buf[i]=='-'){  break; }
                        i++;
                    }
                    publickeyblob=Util.fromBase64(buf, start, i-start);

                    if(type==UNKNOWN)
                    {
                        if(publickeyblob[8]=='d')
                        {
                            type=DSS;
                        }
                        else if(publickeyblob[8]=='r')
                        {
                            type=RSA;
                        }
                    }
                }
                else
                {
                    if(buf[0]!='s'|| buf[1]!='s'|| buf[2]!='h'|| buf[3]!='-') return;
                    i=0;
                    while(i<len){ if(buf[i]==' ')break; i++;} i++;
                    if(i>=len) return;
                    start=i;
                    while(i<len){ if(buf[i]==' ')break; i++;}
                    publickeyblob=Util.fromBase64(buf, start, i-start);
                }

            }
            catch(Exception e)
            {
                Console.WriteLine("Identity: "+e);
                if(e is JSchException) throw (JSchException)e;
                throw new JSchException(e.ToString());
            }
        }
        public void run()
        {
            thread=this;

            byte[] foo;
            Buffer buf=new Buffer();
            Packet packet=new Packet(buf);
            int i=0;
            Channel channel;
            int[] start=new int[1];
            int[] length=new int[1];
            KeyExchange kex=null;

            try
            {
                while(_isConnected &&
                    thread!=null)
                {
                    buf=read(buf);
                    int msgType=buf.buffer[5]&0xff;
                    //      if(msgType!=94)
                    //System.Console.WriteLine("read: 94 ? "+msgType);

                    if(kex!=null && kex.getState()==msgType)
                    {
                        bool result=kex.next(buf);
                        if(!result)
                        {
                            throw new JSchException("verify: "+result);
                        }
                        continue;
                    }

                    switch(msgType)
                    {
                        case SSH_MSG_KEXINIT:
                            //System.Console.WriteLine("KEXINIT");
                            kex=receive_kexinit(buf);
                            break;

                        case SSH_MSG_NEWKEYS:
                            //System.Console.WriteLine("NEWKEYS");
                            send_newkeys();
                            receive_newkeys(buf, kex);
                            kex=null;
                            break;

                        case SSH_MSG_CHANNEL_DATA:
                            buf.getInt();
                            buf.getByte();
                            buf.getByte();
                            i=buf.getInt();
                            channel=Channel.getChannel(i, this);
                            foo=buf.getString(start, length);
                            if(channel==null)
                            {
                                break;
                            }
                            try
                            {
                                channel.write(foo, start[0], length[0]);
                            }
                            catch(Exception e)
                            {
                                //System.Console.WriteLine(e);
                                try{channel.disconnect();}
                                catch(Exception ee){}
                                break;
                            }
                            int len=length[0];
                            channel.setLocalWindowSize(channel.lwsize-len);
                            if(channel.lwsize<channel.lwsize_max/2)
                            {
                                packet.reset();
                                buf.putByte((byte)SSH_MSG_CHANNEL_WINDOW_ADJUST);
                                buf.putInt(channel.getRecipient());
                                buf.putInt(channel.lwsize_max-channel.lwsize);
                                write(packet);
                                channel.setLocalWindowSize(channel.lwsize_max);
                            }
                            break;

                        case SSH_MSG_CHANNEL_EXTENDED_DATA:
                            buf.getInt();
                            buf.getShort();
                            i=buf.getInt();
                            channel=Channel.getChannel(i, this);
                            buf.getInt();                   // data_type_code == 1
                            foo=buf.getString(start, length);
                            //System.Console.WriteLine("stderr: "+new String(foo,start[0],length[0]));
                            if(channel==null)
                            {
                                break;
                            }
                            //channel.write(foo, start[0], length[0]);
                            channel.write_ext(foo, start[0], length[0]);

                            len=length[0];
                            channel.setLocalWindowSize(channel.lwsize-len);
                            if(channel.lwsize<channel.lwsize_max/2)
                            {
                                packet.reset();
                                buf.putByte((byte)SSH_MSG_CHANNEL_WINDOW_ADJUST);
                                buf.putInt(channel.getRecipient());
                                buf.putInt(channel.lwsize_max-channel.lwsize);
                                write(packet);
                                channel.setLocalWindowSize(channel.lwsize_max);
                            }
                            break;

                        case SSH_MSG_CHANNEL_WINDOW_ADJUST:
                            buf.getInt();
                            buf.getShort();
                            i=buf.getInt();
                            channel=Channel.getChannel(i, this);
                            if(channel==null)
                            {
                                break;
                            }
                            channel.addRemoteWindowSize(buf.getInt());
                            break;

                        case SSH_MSG_CHANNEL_EOF:
                            buf.getInt();
                            buf.getShort();
                            i=buf.getInt();
                            channel=Channel.getChannel(i, this);
                            if(channel!=null)
                            {
                                //channel._eof_remote=true;
                                //channel.eof();
                                channel.eof_remote();
                            }
                            /*
                            packet.reset();
                            buf.putByte((byte)SSH_MSG_CHANNEL_EOF);
                            buf.putInt(channel.getRecipient());
                            write(packet);
                            */
                            break;
                        case SSH_MSG_CHANNEL_CLOSE:
                            buf.getInt();
                            buf.getShort();
                            i=buf.getInt();
                            channel=Channel.getChannel(i, this);
                            if(channel!=null)
                            {
                                //	      channel.close();
                                channel.disconnect();
                            }
                            /*
                                if(Channel.pool.size()==0){
                              thread=null;
                            }
                            */
                            break;
                        case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
                            buf.getInt();
                            buf.getShort();
                            i=buf.getInt();
                            channel=Channel.getChannel(i, this);
                            if(channel==null)
                            {
                                //break;
                            }
                            channel.setRecipient(buf.getInt());
                            channel.setRemoteWindowSize(buf.getInt());
                            channel.setRemotePacketSize(buf.getInt());
                            break;
                        case SSH_MSG_CHANNEL_OPEN_FAILURE:
                            buf.getInt();
                            buf.getShort();
                            i=buf.getInt();
                            channel=Channel.getChannel(i, this);
                            if(channel==null)
                            {
                                //break;
                            }
                            int reason_code=buf.getInt();
                            //foo=buf.getString();  // additional textual information
                            //foo=buf.getString();  // language tag
                            channel.exitstatus=reason_code;
                            channel._close=true;
                            channel._eof_remote=true;
                            channel.setRecipient(0);
                            break;
                        case SSH_MSG_CHANNEL_REQUEST:
                            buf.getInt();
                            buf.getShort();
                            i=buf.getInt();
                            foo=buf.getString();
                            bool reply=(buf.getByte()!=0);
                            channel=Channel.getChannel(i, this);
                            if(channel!=null)
                            {
                                byte reply_type=(byte)SSH_MSG_CHANNEL_FAILURE;
                                if((new String(foo)).equals("exit-status"))
                                {
                                    i=buf.getInt();             // exit-status
                                    channel.setExitStatus(i);
                                    //	    System.Console.WriteLine("exit-stauts: "+i);
                                    //          channel.close();
                                    reply_type=(byte)SSH_MSG_CHANNEL_SUCCESS;
                                }
                                if(reply)
                                {
                                    packet.reset();
                                    buf.putByte(reply_type);
                                    buf.putInt(channel.getRecipient());
                                    write(packet);
                                }
                            }
                            else
                            {
                            }
                            break;
                        case SSH_MSG_CHANNEL_OPEN:
                            buf.getInt();
                            buf.getShort();
                            foo=buf.getString();
                            String ctyp=new String(foo);
                            //System.Console.WriteLine("type="+ctyp);
                            if(!new String("forwarded-tcpip").equals(ctyp) &&
                                !(new String("x11").equals(ctyp) && x11_forwarding))
                            {
                                System.Console.WriteLine("Session.run: CHANNEL OPEN "+ctyp);
                                throw new IOException("Session.run: CHANNEL OPEN "+ctyp);
                            }
                            else
                            {
                                channel=Channel.getChannel(ctyp);
                                addChannel(channel);
                                channel.getData(buf);
                                channel.init();

                                packet.reset();
                                buf.putByte((byte)SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
                                buf.putInt(channel.getRecipient());
                                buf.putInt(channel.id);
                                buf.putInt(channel.lwsize);
                                buf.putInt(channel.lmpsize);
                                write(packet);
                                Thread tmp=new Thread(channel);
                                tmp.setName("Channel "+ctyp+" "+host);
                                tmp.start();
                                break;
                            }
                        case SSH_MSG_CHANNEL_SUCCESS:
                            buf.getInt();
                            buf.getShort();
                            i=buf.getInt();
                            channel=Channel.getChannel(i, this);
                            if(channel==null)
                            {
                                break;
                            }
                            channel.reply=1;
                            break;
                        case SSH_MSG_CHANNEL_FAILURE:
                            buf.getInt();
                            buf.getShort();
                            i=buf.getInt();
                            channel=Channel.getChannel(i, this);
                            if(channel==null)
                            {
                                break;
                            }
                            channel.reply=0;
                            break;
                        case SSH_MSG_GLOBAL_REQUEST:
                            buf.getInt();
                            buf.getShort();
                            foo=buf.getString();       // request name
                            reply=(buf.getByte()!=0);
                            if(reply)
                            {
                                packet.reset();
                                buf.putByte((byte)SSH_MSG_REQUEST_FAILURE);
                                write(packet);
                            }
                            break;
                        case SSH_MSG_REQUEST_FAILURE:
                        case SSH_MSG_REQUEST_SUCCESS:
                            Thread t=grr.getThread();
                            if(t!=null)
                            {
                                grr.setReply(msgType==SSH_MSG_REQUEST_SUCCESS? 1 : 0);
                                t.interrupt();
                            }
                            break;
                        default:
                            System.Console.WriteLine("Session.run: unsupported type "+msgType);
                            throw new IOException("Unknown SSH message type "+msgType);
                    }
                }
            }
            catch(Exception e)
            {
                //System.Console.WriteLine("# Session.run");
                //e.printStackTrace();
            }
            try
            {
                disconnect();
            }
            catch(NullReferenceException e)
            {
                //System.Console.WriteLine("@1");
                //e.printStackTrace();
            }
            catch(Exception e)
            {
                //System.Console.WriteLine("@2");
                //e.printStackTrace();
            }
            _isConnected=false;
        }
        public Buffer read(Buffer buf)
        {
            int j=0;
            while(true)
            {
                buf.reset();
                io.getByte(buf.buffer, buf.index, cipher_size); buf.index+=cipher_size;
                if(s2ccipher!=null)
                {
                    s2ccipher.update(buf.buffer, 0, cipher_size, buf.buffer, 0);
                }
            //				j=((buf.buffer[0]<<24)&0xff000000)|
            //					((buf.buffer[1]<<16)&0x00ff0000)|
            //					((buf.buffer[2]<< 8)&0x0000ff00)|
            //					((buf.buffer[3]    )&0x000000ff);
                j=Util.ToInt32( buf.buffer, 0 );
                j=j-4-cipher_size+8;
                if(j<0 || (buf.index+j)>buf.buffer.Length)
                {
                    throw new IOException("invalid data");
                }
                if(j>0)
                {
                    io.getByte(buf.buffer, buf.index, j); buf.index+=(j);
                    if(s2ccipher!=null)
                    {
                        s2ccipher.update(buf.buffer, cipher_size, j, buf.buffer, cipher_size);
                    }
                }

                if(s2cmac!=null)
                {
                    s2cmac.update(seqi);
                    s2cmac.update(buf.buffer, 0, buf.index);
                    byte[] result=s2cmac.doFinal();
                    io.getByte(mac_buf, 0, mac_buf.Length);
                    if(!Arrays.equals(result, mac_buf))
                    {
                        throw new IOException("MAC Error");
                    }
                }
                seqi++;

                if(inflater!=null)
                {
                    //inflater.uncompress(buf);
                    int pad=buf.buffer[4];
                    uncompress_len[0]=buf.index-5-pad;
                    byte[] foo=inflater.uncompress(buf.buffer, 5, uncompress_len);
                    if(foo!=null)
                    {
                        buf.buffer=foo;
                        buf.index=5+uncompress_len[0];
                    }
                    else
                    {
                        System.Console.Error.WriteLine("fail in inflater");
                        break;
                    }
                }

                int type=buf.buffer[5]&0xff;
                //System.Console.WriteLine("read: "+type);
                if(type==SSH_MSG_DISCONNECT)
                {
                    buf.rewind();
                    buf.getInt();buf.getShort();
                    int reason_code=buf.getInt();
                    byte[] description=buf.getString();
                    byte[] language_tag=buf.getString();
                    /*
                        System.Console.Error.WriteLine("SSH_MSG_DISCONNECT:"+
                                               " "+reason_code+
                                   " "+new String(description)+
                                   " "+new String(language_tag));
                    */
                    throw new JSchException("SSH_MSG_DISCONNECT:"+
                        " "+reason_code+
                        " "+new String(description)+
                        " "+new String(language_tag));
                    //break;
                }
                else if(type==SSH_MSG_IGNORE)
                {
                }
                else if(type==SSH_MSG_DEBUG)
                {
                    buf.rewind();
                    buf.getInt();buf.getShort();
                    /*
                        byte always_display=(byte)buf.getByte();
                        byte[] message=buf.getString();
                        byte[] language_tag=buf.getString();
                        System.Console.Error.WriteLine("SSH_MSG_DEBUG:"+
                                   " "+new String(message)+
                                   " "+new String(language_tag));
                    */
                }
                else if(type==SSH_MSG_CHANNEL_WINDOW_ADJUST)
                {
                    buf.rewind();
                    buf.getInt();buf.getShort();
                    Channel c=Channel.getChannel(buf.getInt(), this);
                    if(c==null)
                    {
                    }
                    else
                    {
                        c.addRemoteWindowSize(buf.getInt());
                    }
                }
                else
                {
                    break;
                }
            }
            buf.rewind();
            return buf;
        }