Міністерство освіти і науки України
Національний університет «Львівська політехніка»
Кафедра ЕОМ
/
Звіт
з лабораторної роботи № 2
з дисципліни:
«Програмні технології захисту інформації»
Львів-2015
Тема: Протокол SSH. Реалізація основних функцій.
Мета: Реалізувати основні функції протоколу SSH.
Теоретичні відомості:
Secure Shell, SSH (англ. Secure SHell — «безпечна оболонка») — мережевий протокол рівня застосунків, що дозволяє проводити віддалене управління комп'ютером і тунелювання TCP-з'єднань (наприклад, для передачі файлів). Схожий за функціональністю з протоколом Telnet і rlogin, проте шифрує весь трафік, в тому числі і паролі, що передаються.
Криптографічний захист протоколу SSH не фіксований, можливий вибір різних алгоритмів шифрування. Клієнти і сервери, що підтримують цей протокол, доступні для різних платформ. Крім того, протокол дозволяє не тільки використовувати безпечний віддалений shell на машині, але і туннелювати графічний інтерфейс — X Tunnelling (тільки для Unix-подібних ОС або застосунків, що використовують графічний інтерфейс X Window System). Так само ssh здатний передавати через безпечний канал (Port Forwarding) будь-який інший мережевий протокол, забезпечуючи (при належній конфігурації) можливість безпечної пересилки не тільки X-інтерфейсу, але і, наприклад, звуку.
Основні функції для роботи з протоколом SSH
Обмін ключами:
public abstract class KeyExchange
{
internal const int PROPOSAL_KEX_ALGS=0;
internal const int PROPOSAL_SERVER_HOST_KEY_ALGS=1;
internal const int PROPOSAL_ENC_ALGS_CTOS=2;
internal const int PROPOSAL_ENC_ALGS_STOC=3;
internal const int PROPOSAL_MAC_ALGS_CTOS=4;
internal const int PROPOSAL_MAC_ALGS_STOC=5;
internal const int PROPOSAL_COMP_ALGS_CTOS=6;
internal const int PROPOSAL_COMP_ALGS_STOC=7;
internal const int PROPOSAL_LANG_CTOS=8;
internal const int PROPOSAL_LANG_STOC=9;
internal const int PROPOSAL_MAX=10;
//static String kex_algs="diffie-hellman-group-exchange-sha1"+
// ",diffie-hellman-group1-sha1";
//static String kex="diffie-hellman-group-exchange-sha1";
internal static String kex="diffie-hellman-group1-sha1";
internal static String server_host_key="ssh-rsa,ssh-dss";
internal static String enc_c2s="blowfish-cbc";
internal static String enc_s2c="blowfish-cbc";
internal static String mac_c2s="hmac-md5"; // hmac-md5,hmac-sha1,hmac-ripemd160,
// hmac-sha1-96,hmac-md5-96
internal static String mac_s2c="hmac-md5";
//static String comp_c2s="none"; // zlib
//static String comp_s2c="none";
internal static String lang_c2s="";
internal static String lang_s2c="";
public const int STATE_END=0;
public String[] guess=null;
protected Session session=null;
protected HASH sha=null;
protected byte[] K=null;
protected byte[] H=null;
protected byte[] K_S=null;
public abstract void init(Session session,
byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C);
public abstract bool next(Buffer buf);
public abstract String getKeyType();
public abstract int getState();
/*
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 String[] Guess(byte[]I_S, byte[]I_C)
{
//System.out.println("guess: ");
String[] guess=new 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 String getFingerPrint()
{
HASH hash=null;
try
{
Type t=Type.GetType(session.getConfig("md5"));
hash=(HASH)(Activator.CreateInstance(t));
}
catch(Exception e){ Console.Error.WriteLine("getFingerPrint: "+e); }
return Util.getFingerPrint(hash, getHostKey());
}
internal byte[] getK(){ return K; }
internal byte[] getH(){ return H; }
internal HASH getHash(){ return sha; }
internal byte[] getHostKey(){ return K_S; }
}
Шифрування:
public class HMACMD5 : MAC
{
private const String name="hmac-md5";
private const int bsize=16;
private Org.Mentalis.Security.Cryptography.HMAC mentalis_mac;
private System.Security.Cryptography.CryptoStream cs;
//private Mac mac;
public int getBlockSize(){return bsize;}
public void init(byte[] key)
{
if(key.Length>bsize)
{
byte[] tmp=new byte[bsize];
Array.Copy(key, 0, tmp, 0, bsize);
key=tmp;
}
// SecretKeySpec skey=new SecretKeySpec(key, "HmacMD5");
// mac=Mac.getInstance("HmacMD5");
// mac.init(skey);
mentalis_mac = new Org.Mentalis.Security.Cryptography.HMAC(new System.Security.Cryptography.MD5CryptoServiceProvider(), key);
cs = new System.Security.Cryptography.CryptoStream( System.IO.Stream.Null, mentalis_mac, System.Security.Cryptography.CryptoStreamMode.Write);
}
private byte[] tmp=new byte[4];
public void update(int i)
{
tmp[0]=(byte)(i>>24);
tmp[1]=(byte)(i>>16);
tmp[2]=(byte)(i>>8);
tmp[3]=(byte)i;
update(tmp, 0, 4);
}
public void update(byte[] foo, int s, int l)
{
//mac.update(foo, s, l);
cs.Write( foo, s, l);
}
public byte[] doFinal()
{
//return mac.doFinal();
cs.Close();
byte[] result = mentalis_mac.Hash;
byte[] key = mentalis_mac.Key;
mentalis_mac.Clear();
init(key);
return result;
}
public String getName()
{
return name;
}
}
Авторизація (PublicKey Authorization):
class UserAuthPublicKey : UserAuth
{
internal UserInfo userinfo;
internal UserAuthPublicKey(UserInfo userinfo)
{
this.userinfo=userinfo;
}
public override bool start(Session session)
{
//super.start(session);
//Vector identities=JSch.identities;
System.Collections.ArrayList identities=session.jsch.identities;
Packet packet=session.packet;
Buffer buf=session.buf;
String passphrase=null;
String username=session.username;
byte[] _username=null;
try{ _username= Util.getBytesUTF8( username); }
catch
{//(java.io.UnsupportedEncodingException e){
_username=Util.getBytes(username);
}
for(int i=0; i<identities.Count; i++)
{
Identity identity=(Identity)(identities[i]);
byte[] pubkeyblob=identity.getPublicKeyBlob();
//System.out.println("UserAuthPublicKey: "+identity+" "+pubkeyblob);
if(pubkeyblob!=null)
{
// send
// byte SSH_MSG_USERAUTH_REQUEST(50)
// string user name
// string service name ("ssh-connection")
// string "publickey"
// boolen FALSE
// string plaintext password (ISO-10646 UTF-8)
packet.reset();
buf.putByte((byte)Session.SSH_MSG_USERAUTH_REQUEST);
buf.putString(_username);
buf.putString(Util.getBytes("ssh-connection"));
buf.putString(Util.getBytes("publickey"));
buf.putByte((byte)0);
buf.putString(Util.getBytes(identity.getAlgName()));
buf.putString(pubkeyblob);
session.write(packet);
loop1:
while(true)
{
// receive
// byte SSH_MSG_USERAUTH_PK_OK(52)
// string service name
buf=session.read(buf);
//System.out.println("read: 60 ? "+ buf.buffer[5]);
if(buf.buffer[5]==Session.SSH_MSG_USERAUTH_PK_OK)
{
break;
}
else if(buf.buffer[5]==Session.SSH_MSG_USERAUTH_FAILURE)
{
// System.out.println("USERAUTH publickey "+session.getIdentity()+
// " is not acceptable.");
break;
}
else if(buf.buffer[5]==Session.SSH_MSG_USERAUTH_BANNER)
{
buf.getInt(); buf.getByte(); buf.getByte();
byte[] _message=buf.getString();
byte[] lang=buf.getString();
String message=null;
try{ message=Util.getStringUTF8(_message); }
catch
{//(java.io.UnsupportedEncodingException e){
message=Util.getString(_message);
}
if(userinfo!=null)
{
userinfo.showMessage(message);
}
goto loop1;
}
else
{
//System.out.println("USERAUTH fail ("+buf.buffer[5]+")");
//throw new JSchException("USERAUTH fail ("+buf.buffer[5]+")");
break;
}
}
if(buf.buffer[5]!=Session.SSH_MSG_USERAUTH_PK_OK)
{
continue;
}
}
//System.out.println("UserAuthPublicKey: identity.isEncrypted()="+identity.isEncrypted());
int count=5;
while(true)
{
if((identity.isEncrypted() && passphrase==null))
{
if(userinfo==null) throw new JSchException("USERAUTH fail");
if(identity.isEncrypted() &&
!userinfo.promptPassphrase("Passphrase for "+identity.getName()))
{
throw new JSchAuthCancelException("publickey");
//throw new JSchException("USERAUTH cancel");
//break;
}
passphrase=userinfo.getPassphrase();
}
if(!identity.isEncrypted() || passphrase!=null)
{
//System.out.println("UserAuthPublicKey: @1 "+passphrase);
if(identity.setPassphrase(passphrase))
break;
}
passphrase=null;
count--;
if(count==0)break;
}
//System.out.println("UserAuthPublicKey: identity.isEncrypted()="+identity.isEncrypted());
if(identity.isEncrypted()) continue;
if(pubkeyblob==null) pubkeyblob=identity.getPublicKeyBlob();
//System.out.println("UserAuthPublicKey: pubkeyblob="+pubkeyblob);
if(pubkeyblob==null) continue;
// send
// byte SSH_MSG_USERAUTH_REQUEST(50)
// string user name
// string service name ("ssh-connection")
// string "publickey"
// boolen TRUE
// string plaintext password (ISO-10646 UTF-8)
packet.reset();
buf.putByte((byte)Session.SSH_MSG_USERAUTH_REQUEST);
buf.putString(_username);
buf.putString(Util.getBytes("ssh-connection"));
buf.putString(Util.getBytes("publickey"));
buf.putByte((byte)1);
buf.putString(Util.getBytes(identity.getAlgName()));
buf.putString(pubkeyblob);
// byte[] tmp=new byte[buf.index-5];
// System.arraycopy(buf.buffer, 5, tmp, 0, tmp.length);
// buf.putString(signature);
byte[] sid=session.getSessionId();
uint sidlen=(uint)sid.Length;
byte[] tmp=new byte[4+sidlen+buf.index-5];
tmp[0]=(byte)(sidlen>>24);
tmp[1]=(byte)(sidlen>>16);
tmp[2]=(byte)(sidlen>>8);
tmp[3]=(byte)(sidlen);
Array.Copy(sid, 0, tmp, 4, sidlen);
Array.Copy(buf.buffer, 5, tmp, 4+sidlen, buf.index-5);
byte[] signature=identity.getSignature(session, tmp);
if(signature==null)
{ // for example, too long key length.
break;
}
buf.putString(signature);
session.write(packet);
loop2:
while(true)
{
// receive
// byte SSH_MSG_USERAUTH_SUCCESS(52)
// string service name
buf=session.read(buf);
//System.out.println("read: 52 ? "+ buf.buffer[5]);
if(buf.buffer[5]==Session.SSH_MSG_USERAUTH_SUCCESS)
{
return true;
}
else if(buf.buffer[5]==Session.SSH_MSG_USERAUTH_BANNER)
{
buf.getInt(); buf.getByte(); buf.getByte();
byte[] _message=buf.getString();
byte[] lang=buf.getString();
String message=null;
try{ message=Util.getStringUTF8(_message); }
catch
{//(java.io.UnsupportedEncodingException e){
message=Util.getString(_message);
}
if(userinfo!=null)
{
userinfo.showMessage(message);
}
goto loop2;
}
else if(buf.buffer[5]==Session.SSH_MSG_USERAUTH_FAILURE)
{
buf.getInt(); buf.getByte(); buf.getByte();
byte[] foo=buf.getString();
int partial_success=buf.getByte();
//System.out.println(new String(foo)+
// " partial_success:"+(partial_success!=0));
if(partial_success!=0)
{
throw new JSchPartialAuthException(Util.getString(foo));
}
break;
}
//System.out.println("USERAUTH fail ("+buf.buffer[5]+")");
//throw new JSchException("USERAUTH fail ("+buf.buffer[5]+")");
break;
}
}
return false;
}
}
}
Password Authorization:
class UserAuthPassword : UserAuth{
internal UserInfo userinfo;
internal UserAuthPassword(UserInfo userinfo){
this.userinfo=userinfo;
}
public override bool start(Session session) {
// super.start(session);
//System.out.println("UserAuthPassword: start");
Packet packet=session.packet;
Buffer buf=session.buf;
String username=session.username;
String password=session.password;
String dest=username+"@"+session.host;
if(session.port!=22){
dest+=(":"+session.port);
}
while(true){
if(password==null){
if(userinfo==null){
//throw new JSchException("USERAUTH fail");
return false;
}
if(!userinfo.promptPassword("Password for "+dest)){
throw new JSchAuthCancelException("password");
//break;
}
password=userinfo.getPassword();
if(password==null){
throw new JSchAuthCancelException("password");
//break;
}
}
byte[] _username=null;
try{ _username=Util.getBytesUTF8(username); }
catch{//(java.io.UnsupportedEncodingException e){
_username=Util.getBytes(username);
}
byte[] _password=null;
try{ _password=Util.getBytes(password); }
catch{//(java.io.UnsupportedEncodingException e){
_password=Util.getBytes(password);
}
// send
// byte SSH_MSG_USERAUTH_REQUEST(50)
// string user name
// string service name ("ssh-connection")
// string "password"
// boolen FALSE
// string plaintext password (ISO-10646 UTF-8)
packet.reset();
buf.putByte((byte)Session.SSH_MSG_USERAUTH_REQUEST);
buf.putString(_username);
buf.putString(Util.getBytes("ssh-connection"));
buf.putString(Util.getBytes("password"));
buf.putByte((byte)0);
buf.putString(_password);
session.write(packet);
loop:
while(true){
// receive
// byte SSH_MSG_USERAUTH_SUCCESS(52)
// string service name
buf=session.read(buf);
//System.out.println("read: 52 ? "+ buf.buffer[5]);
if(buf.buffer[5]==Session.SSH_MSG_USERAUTH_SUCCESS){
return true;
}
if(buf.buffer[5]==Session.SSH_MSG_USERAUTH_BANNER){
buf.getInt(); buf.getByte(); buf.getByte();
byte[] _message=buf.getString();
byte[] lang=buf.getString();
String message=null;
try{ message=Util.getStringUTF8(_message); }
catch{//(java.io.UnsupportedEncodingException e){
message=Util.getString(_message);
}
if(userinfo!=null){
userinfo.showMessage(message);
}
goto loop;
}
if(buf.buffer[5]==Session.SSH_MSG_USERAUTH_FAILURE){
buf.getInt(); buf.getByte(); buf.getByte();
byte[] foo=buf.getString();
int partial_success=buf.getByte();
//System.out.println(new String(foo)+
// " partial_success:"+(partial_success!=0));
if(partial_success!=0){
throw new JSchPartialAuthException(Util.getString(foo));
}
break;
}
else{
// System.out.println("USERAUTH fail ("+buf.buffer[5]+")");
// throw new JSchException("USERAUTH fail ("+buf.buffer[5]+")");
return false;
}
}
password=null;
}
//throw new JSchException("USERAUTH fail");
//return false;
}
}
}
Висновок: У даній лабораторній роботі було реалізовано функції для роботи з протоколом SSH. Дані функції написані на мові програмування C#.