概要

SCP (Secure Copy Protocol) は、SSHプロトコルを使用して、ファイルやディレクトリを安全にコピーするためのプロトコルである。
通常、SSHを使用してリモートサーバとのセキュアな通信を提供するため、SCPもそのセキュリティメカニズムを利用している。

SCPはセキュリティ上の理由からFTPに代わり、多くの環境で利用されている。
ただし、転送速度がFTPほど高速ではないというデメリットもある。

  • 暗号化された通信
    SSHと同様、データは転送中に暗号化されるため、第三者による盗聴を防ぐ。
  • 認証
    ユーザ名、パスワード、または公開鍵認証を使用して、接続の相手を認証する。
  • ディレクトリのコピー
    単一のファイルだけでなく、ディレクトリ構造全体をコピーすることができる。
  • 転送の再開
    転送が中断した場合、中断地点から再開することが可能である。



SCPコマンド

SCPコマンドは、SSHプロトコルを使用してファイルやディレクトリをセキュアにコピーする。
SSHの利点と同様、SCPもデータ転送中に暗号化を提供し、安全な通信を確保する。

# ローカルPCからリモートPCへファイルのコピーする
scp <ローカルPCのファイルパス> <リモートPCのユーザ名>@<リモートPCのIPアドレス または ホスト名>:<コピー先のファイルパス>

# リモートPCからローカルPCへのファイルのコピーする
scp <リモートPCのユーザ名>@<リモートPCのIPアドレス または ホスト名>:<コピー元のファイルパス> <ローカルPCのファイルパス>

# ローカルPCからリモートPCへディレクトリの再帰的なコピーをする
scp -r <ローカルPCのディレクトリパス> <リモートPCのユーザ名>@<リモートPCのIPアドレス または ホスト名>:<コピー先のファイルパス>



処理の流れ

  1. 認証方法のインスタンスを生成する。
    公開鍵認証またはパスワード認証を選択する。
    公開鍵認証の場合は、ppk形式は指定できない。(例外が発生する)

  2. 接続情報のインスタンスを生成する。
    生成した認証方法のインスタンスをコンストラクタの引数で渡す。

    sftpClientのインスタンスを生成する。
    接続情報のインスタンスをコンストラクタの引数に渡す。

  3. SCPを使用した処理を記述する。
    (ダウンロード処理やアップロード処理)


ユーザディレクトリのパスを取得するSystem.Environment.GetFolderPathメソッドは、Linuxにおいても問題なくユーザディレクトリ配下のパスを取得することができる。


サンプルコード

SCPを使用したダウンロード

 using Renci.SshNet;
 
 namespace SCPDownload;
 
 class Program
 {
    private static bool AuthPubKey { get; set; }
 
    private static async Task Main(string[] args)
    {
       var    port = <ト番号>;
       string host = "<IPアドレス または ホスト名>",
              user = "<ユーザ名>",
              pass = "<パスワード認証を行う場合は、パスワード>";
       string keypath = "<秘密鍵のファイルパス>",
              passphrase = "<秘密鍵のパスフレーズ>";
 
       AuthPubKey = true;
 
       // コネクション情報
       var info = AuthPubKey == false
          ?
          // パスワード認証
          new ConnectionInfo(
             host,
             port,
             user,
             new AuthenticationMethod[]
             {
                new PasswordAuthenticationMethod(user, pass)
             }
          )
          :
          // 公開鍵認証
          new ConnectionInfo(
             host,
             port,
             user,
             GetConnectionMethod(user, keypath,passphrase));
 
       var ret = await Task.Run(() => Recieve(ref info));
    }
 
    // 公開鍵認証の設定を取得
    private static AuthenticationMethod[] GetConnectionMethod(string user, string key, string pass)
    {
       // 秘密鍵の読み込み
       //// 秘密鍵のパスフレーズが存在しない場合
       //var privateKeyFile = new PrivateKeyFile(key);
       //// 秘密鍵のパスフレーズが存在する場合
       var privateKeyFile = new PrivateKeyFile(key, pass);
 
       // 秘密鍵を使用した認証メソッドの作成
       var privateKeyAuthMethod = new PrivateKeyAuthenticationMethod(user, privateKeyFile);
 
       // ユーザー名と秘密鍵を使用した認証メソッドを返す
       return new AuthenticationMethod[] { privateKeyAuthMethod };
    }
 
    private static int Recieve(ref ConnectionInfo info)
    {
       int ret = 0;
 
       try
       {
          using (var client = new ScpClient(info))
          {
             client.RemotePathTransformation = RemotePathTransformation.ShellQuote;
 
             client.Connect();
             if (!client.IsConnected)
             {   // 接続に失敗した場合
                Console.WriteLine("Connection failed.");
                return -1;
             }
 
             var fi = new FileInfo("<ローカルPC側のファイルパス  例: ~/hoge.png>");
             client.Download("<リモートPC側のファイルパス  例: /home/user/hoge.png>", fi);
          }
       }
       catch (Exception ex)
       {  // 例外が発生した場合
          Console.WriteLine($"Recieveメソッド内で例外が発生しました:");
          Console.WriteLine($"{ex.ToString()}");
            
          ret = -1;
       }
       finally
       {
          scp.Disconnect();
       }
 
       return ret;
    }
 }


SCPを使用したアップロード

 using Renci.SshNet;
 
 namespace SCPUpload;
 
 class Program
 {
    private static bool AuthPubKey { get; set; }
 
    private static async Task Main(string[] args)
    {
       var    port = <ト番号>;
       string host = "<IPアドレス または ホスト名>",
              user = "<ユーザ名>",
              pass = "<パスワード認証を行う場合は、パスワード>";
       string keypath = "<秘密鍵のファイルパス>",
              passphrase = "<秘密鍵のパスフレーズ>";
 
      AuthPubKey = true;
 
       // コネクション情報
       var info = AuthPubKey == false
          ?
          // パスワード認証
          new ConnectionInfo(
             host,
             port,
             user,
             new AuthenticationMethod[]
             {
                new PasswordAuthenticationMethod(user, pass)
             }
          )
          :
          // 公開鍵認証
          new ConnectionInfo(
             host,
             port,
             user,
             GetConnectionMethod(user, keypath,passphrase));
 
       var ret = await Task.Run(() => Send(ref info));
    }
 
    // 公開鍵認証の設定を取得
    private static AuthenticationMethod[] GetConnectionMethod(string user, string key, string pass)
    {
       // 秘密鍵の読み込み
       //// 秘密鍵のパスフレーズが存在しない場合
       //var privateKeyFile = new PrivateKeyFile(key);
       //// 秘密鍵のパスフレーズが存在する場合
       var privateKeyFile = new PrivateKeyFile(key, pass);
 
       // 秘密鍵を使用した認証メソッドの作成
       var privateKeyAuthMethod = new PrivateKeyAuthenticationMethod(user, privateKeyFile);
 
       // ユーザー名と秘密鍵を使用した認証メソッドを返す
       return new AuthenticationMethod[] { privateKeyAuthMethod };
    }
 
    private static int Send(ref ConnectionInfo info)
    {
       int ret = 0;
 
       try
       {
          using (var client = new ScpClient(info))
          {
             client.RemotePathTransformation = RemotePathTransformation.ShellQuote;
 
             client.Connect();
             if (!client.IsConnected)
             {   // 接続に失敗した場合
                Console.WriteLine("Connection failed.");
                return -1;
             }
 
             using (var fileStream = new FileStream("<ローカルPC側のファイルパス  例: ~/piyo.png>", FileMode.Open))
             {
                scp.Upload(fileStream, "<リモートPC側のファイルパス  例: /home/user/piyo.png>");
             }
          }
       }
       catch (Exception ex)
       {  // 例外が発生した場合
          Console.WriteLine($"Sendメソッド内で例外が発生しました:");
          Console.WriteLine($"{ex.ToString()}");
            
          ret = -1;
       }
       finally
       {
          scp.Disconnect();
       }
 
       return ret;
    }
 }