概要
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アドレス または ホスト名>:<コピー先のファイルパス>
処理の流れ
- 認証方法のインスタンスを生成する。
- 公開鍵認証またはパスワード認証を選択する。
- 公開鍵認証の場合は、ppk形式は指定できない。(例外が発生する)
- 接続情報のインスタンスを生成する。
- 生成した認証方法のインスタンスをコンストラクタの引数で渡す。
- sftpClientのインスタンスを生成する。
- 接続情報のインスタンスをコンストラクタの引数に渡す。
- 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;
}
}