.NETプログラミング研究 第17号 †
.NET Tips †
秘密鍵暗号方式による暗号化 †
ここでは共有キー暗号方式 (対称暗号化方式、秘密鍵暗号方式)による暗号化、復号化について説明します。共有キー暗号方式では、単一の共有キーを使用してデータの暗号化と復号化が行われます。「秘密鍵暗号システム」に関して「アスキー デジタル用語辞典」(http://yougo.ascii24.com/)では、
「暗号システムにおいて、暗号化に使用する鍵と復号化に使用する鍵が同じもののこと。従来の暗号システムはほとんどすべてがこれに相当するため、慣用暗号システムとも呼ばれる。鍵が1種類しかないため、暗号化に使用した鍵を、通信路とは別の安全な手段を使って、通信相手に届けておく必要がある。」
と説明されています。
ファイルを暗号化する方法は「マイクロソフト サポート技術情報 - 307010」にて詳しく説明されています。
ただしこのサンプルには2つの欠点があります。パスワードを必ず8バイトにする必要がある点と、暗号化されるファイルがUTF-8エンコーディングである必要がある(復号化の時、元のファイルがUTF-8として読み込み、UTF-8で出力している)という点です。
これではほとんど役に立たないので、この2点を修正したものを書いてみました。共有キー(秘密キー)と初期化ベクタは、与えられたパスワードが何バイトであるかにかかわらず、適切なバイト数に調節して設定しています。また、暗号化するファイルのエンコーディングはShift JISとしています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
| | public static void EncryptFile(string fileName, string key)
{
System.IO.FileStream fsIn =
new System.IO.FileStream(fileName,
System.IO.FileMode.Open, System.IO.FileAccess.Read);
byte[] bytesIn = new byte[fsIn.Length];
fsIn.Read(bytesIn, 0, bytesIn.Length);
fsIn.Close();
System.Security.Cryptography.DESCryptoServiceProvider des =
new System.Security.Cryptography.DESCryptoServiceProvider();
byte[] bytesKey = System.Text.Encoding.UTF8.GetBytes(key);
des.Key = ResizeBytesArray(bytesKey, des.Key.Length);
des.IV = ResizeBytesArray(bytesKey, des.IV.Length);
string outFileName = fileName + ".enc";
System.IO.FileStream fsOut =
new System.IO.FileStream(outFileName,
System.IO.FileMode.Create, System.IO.FileAccess.Write);
System.Security.Cryptography.ICryptoTransform desdecrypt =
des.CreateEncryptor();
System.Security.Cryptography.CryptoStream cryptStreem =
new System.Security.Cryptography.CryptoStream(fsOut,
desdecrypt,
System.Security.Cryptography.CryptoStreamMode.Write);
cryptStreem.Write(bytesIn, 0, bytesIn.Length);
cryptStreem.Close();
fsOut.Close();
}
public static void DecryptFile(string fileName, string key)
{
System.Security.Cryptography.DESCryptoServiceProvider des =
new System.Security.Cryptography.DESCryptoServiceProvider();
byte[] bytesKey = System.Text.Encoding.UTF8.GetBytes(key);
des.Key = ResizeBytesArray(bytesKey, des.Key.Length);
des.IV = ResizeBytesArray(bytesKey, des.IV.Length);
System.IO.FileStream fsIn =
new System.IO.FileStream(fileName,
System.IO.FileMode.Open, System.IO.FileAccess.Read);
System.Security.Cryptography.ICryptoTransform desdecrypt =
des.CreateDecryptor();
System.Security.Cryptography.CryptoStream cryptStreem =
new System.Security.Cryptography.CryptoStream(fsIn,
desdecrypt,
System.Security.Cryptography.CryptoStreamMode.Read);
string outFileName;
if (fileName.ToLower().EndsWith(".enc"))
outFileName = fileName.Substring(0, fileName.Length - 4);
else
outFileName = fileName + ".dec";
System.IO.StreamWriter sw =
new System.IO.StreamWriter(outFileName, false,
System.Text.Encoding.GetEncoding("sjis"));
System.IO.StreamReader sr =
new System.IO.StreamReader(cryptStreem,
System.Text.Encoding.GetEncoding("sjis"));
sw.Write(sr.ReadToEnd());
sw.Flush();
sr.Close();
sw.Close();
cryptStreem.Close();
fsIn.Close();
}
private static byte[] ResizeBytesArray(byte[] bytes, int newSize)
{
byte[] newBytes = new byte[newSize];
if (bytes.Length < newSize)
{
for (int i = 0; i < bytes.Length; i++)
newBytes[i] = bytes[i];
}
else
{
int pos = 0;
for (int i = newSize; i < bytes.Length; i++)
{
newBytes[pos++] ^= bytes[i];
if (pos >= newBytes.Length)
pos = 0;
}
}
return newBytes;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
| | Public Shared Sub EncryptFile(ByVal fileName As String, _
ByVal key As String)
Dim fsIn As New System.IO.FileStream(fileName, _
System.IO.FileMode.Open, System.IO.FileAccess.Read)
Dim bytesIn(fsIn.Length) As Byte
fsIn.Read(bytesIn, 0, bytesIn.Length)
fsIn.Close()
Dim des As New System.Security.Cryptography.DESCryptoServiceProvider
Dim bytesKey As Byte() = System.Text.Encoding.UTF8.GetBytes(key)
des.Key = ResizeBytesArray(bytesKey, des.Key.Length)
des.IV = ResizeBytesArray(bytesKey, des.IV.Length)
Dim outFileName As String = fileName + ".enc"
Dim fsOut As New System.IO.FileStream(outFileName, _
System.IO.FileMode.Create, System.IO.FileAccess.Write)
Dim desdecrypt As System.Security.Cryptography.ICryptoTransform = _
des.CreateEncryptor()
Dim cryptStreem As New System.Security.Cryptography.CryptoStream( _
fsOut, desdecrypt, _
System.Security.Cryptography.CryptoStreamMode.Write)
cryptStreem.Write(bytesIn, 0, bytesIn.Length)
cryptStreem.Close()
fsOut.Close()
End Sub
Public Shared Sub DecryptFile(ByVal fileName As String, _
ByVal key As String)
Dim des As New System.Security.Cryptography.DESCryptoServiceProvider
Dim bytesKey As Byte() = System.Text.Encoding.UTF8.GetBytes(key)
des.Key = ResizeBytesArray(bytesKey, des.Key.Length)
des.IV = ResizeBytesArray(bytesKey, des.IV.Length)
Dim fsIn As New System.IO.FileStream(fileName, _
System.IO.FileMode.Open, System.IO.FileAccess.Read)
Dim desdecrypt As System.Security.Cryptography.ICryptoTransform = _
des.CreateDecryptor()
Dim cryptStreem As New System.Security.Cryptography.CryptoStream( _
fsIn, desdecrypt, _
System.Security.Cryptography.CryptoStreamMode.Read)
Dim outFileName As String
If fileName.ToLower().EndsWith(".enc") Then
outFileName = fileName.Substring(0, fileName.Length - 4)
Else
outFileName = fileName + ".dec"
End If
Dim sw As New System.IO.StreamWriter( _
outFileName, False, System.Text.Encoding.GetEncoding("sjis"))
Dim sr As New System.IO.StreamReader( _
cryptStreem, System.Text.Encoding.GetEncoding("sjis"))
sw.Write(sr.ReadToEnd())
sw.Flush()
sr.Close()
sw.Close()
cryptStreem.Close()
fsIn.Close()
End Sub
Private Shared Function ResizeBytesArray(ByVal bytes() As Byte, _
ByVal newSize As Integer) As Byte()
Dim newBytes(newSize - 1) As Byte
If bytes.Length < newSize Then
Dim i As Integer
For i = 0 To bytes.Length - 1
newBytes(i) = bytes(i)
Next i
Else
Dim pos As Integer = 0
Dim i As Integer
For i = newSize To bytes.Length - 1
newBytes(pos) = newBytes(pos) Xor bytes(i)
pos += 1
If pos >= newBytes.Length Then
pos = 0
End If
Next i
End If
Return newBytes
End Function
|
上記の例では、共有キーと初期化ベクタを決定するために、与えられたパスワード(パスワードをバイト型配列に変換したもの)が短ければ残りを0で埋め、長ければ切り詰めるようにしています。切り詰め方としては単にカットするのではなく(そのようにしているサンプルも多いようですが、こうしてしまうとパスワードのはじめ数文字があっていればOKとなってしまう)、切り詰める部分をXORで合成しています(果たしてこれがよい方法であるかは分かりませんが)。また、共有キーと初期化ベクタのサイズの決定は、DESCryptoServiceProviderのKeyとIVプロパティで取得されるバイト配列の大きさと同じとしていますが、DESCryptoServiceProvider.LegalKeySizesやLegalBlockSizesプロパティを調べて決めた方がよりよいでしょう。
上記の方法では暗号化されるファイルはShift JISである必要がありますが、DecryptFileを次のように変えることにより、どんなファイルでも暗号化、復号化できるようになるでしょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
| | public static void DecryptFile(string fileName, string key)
{
System.Security.Cryptography.DESCryptoServiceProvider des =
new System.Security.Cryptography.DESCryptoServiceProvider();
byte[] bytesKey = System.Text.Encoding.UTF8.GetBytes(key);
des.Key = ResizeBytesArray(bytesKey, des.Key.Length);
des.IV = ResizeBytesArray(bytesKey, des.IV.Length);
System.IO.FileStream fsIn =
new System.IO.FileStream(fileName,
System.IO.FileMode.Open, System.IO.FileAccess.Read);
System.Security.Cryptography.ICryptoTransform desdecrypt =
des.CreateDecryptor();
System.Security.Cryptography.CryptoStream cryptStreem =
new System.Security.Cryptography.CryptoStream(fsIn,
desdecrypt,
System.Security.Cryptography.CryptoStreamMode.Read);
string outFileName;
if (fileName.ToLower().EndsWith(".enc"))
outFileName = fileName.Substring(0, fileName.Length - 4);
else
outFileName = fileName + ".dec";
System.IO.FileStream fsOut =
new System.IO.FileStream(outFileName,
System.IO.FileMode.Create, System.IO.FileAccess.Write);
byte[] bs = new byte[256];
int readLen;
while ((readLen = cryptStreem.Read(bs, 0, bs.Length)) > 0)
fsOut.Write(bs, 0, readLen);
cryptStreem.Close();
fsIn.Close();
fsOut.Close();
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
| | Public Shared Sub DecryptFile(ByVal fileName As String, _
ByVal key As String)
Dim des As New System.Security.Cryptography.DESCryptoServiceProvider
Dim bytesKey As Byte() = System.Text.Encoding.UTF8.GetBytes(key)
des.Key = ResizeBytesArray(bytesKey, des.Key.Length)
des.IV = ResizeBytesArray(bytesKey, des.IV.Length)
Dim fsIn As New System.IO.FileStream(fileName, _
System.IO.FileMode.Open, System.IO.FileAccess.Read)
Dim desdecrypt As System.Security.Cryptography.ICryptoTransform = _
des.CreateDecryptor()
Dim cryptStreem As New System.Security.Cryptography.CryptoStream( _
fsIn, desdecrypt, _
System.Security.Cryptography.CryptoStreamMode.Read)
Dim outFileName As String
If fileName.ToLower().EndsWith(".enc") Then
outFileName = fileName.Substring(0, fileName.Length - 4)
Else
outFileName = fileName + ".dec"
End If Dim fsOut As New System.IO.FileStream(outFileName, _
System.IO.FileMode.Create, System.IO.FileAccess.Write)
Dim bs(255) As Byte
Dim readLen As Integer
Do
readLen = cryptStreem.Read(bs, 0, bs.Length)
If readLen > 0 Then
fsOut.Write(bs, 0, readLen)
End If
Loop While (readLen > 0)
cryptStreem.Close()
fsIn.Close()
fsOut.Close()
End Sub
|
ここでは、DESCryptoServiceProvider クラスを使用し、DES (Data Encryption Standard) アルゴリズムによる暗号化、復号化の例を示しましたが、.NET Framework では、共有キー暗号化アルゴリズムを実装するクラスとして、他にも3つ用意されています(下の表参照)。使い方はDESCryptoServiceProviderと全く同じですので、上記の例の"DESCryptoServiceProvider"を適当なクラス名で置き換えるだけでも動くでしょう。
次にファイルではなく、文字列を暗号化する方法を紹介します。
上記の方法では、ファイルから読み込んだデータを暗号化し、ファイルに書き込んでいるため、CryptoStreamオブジェクトを作成する際に、暗号化の対象とするストリームとしてFileStreamオブジェクトを使用しましたが、ここではMemoryStreamオブジェクトを使用することにします。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
| | public static string EncryptString(string str, string key)
{
byte[] bytesIn = System.Text.Encoding.UTF8.GetBytes(str);
System.Security.Cryptography.DESCryptoServiceProvider des =
new System.Security.Cryptography.DESCryptoServiceProvider();
byte[] bytesKey = System.Text.Encoding.UTF8.GetBytes(key);
des.Key = ResizeBytesArray(bytesKey, des.Key.Length);
des.IV = ResizeBytesArray(bytesKey, des.IV.Length);
System.IO.MemoryStream msOut = new System.IO.MemoryStream();
System.Security.Cryptography.ICryptoTransform desdecrypt =
des.CreateEncryptor();
System.Security.Cryptography.CryptoStream cryptStreem =
new System.Security.Cryptography.CryptoStream(msOut,
desdecrypt,
System.Security.Cryptography.CryptoStreamMode.Write);
cryptStreem.Write(bytesIn, 0, bytesIn.Length);
cryptStreem.FlushFinalBlock();
byte[] bytesOut = msOut.ToArray();
cryptStreem.Close();
msOut.Close();
return System.Convert.ToBase64String(bytesOut);
}
public static string DecryptString(string str, string key)
{
System.Security.Cryptography.DESCryptoServiceProvider des =
new System.Security.Cryptography.DESCryptoServiceProvider();
byte[] bytesKey = System.Text.Encoding.UTF8.GetBytes(key);
des.Key = ResizeBytesArray(bytesKey, des.Key.Length);
des.IV = ResizeBytesArray(bytesKey, des.IV.Length);
byte[] bytesIn = System.Convert.FromBase64String(str);
System.IO.MemoryStream msIn =
new System.IO.MemoryStream(bytesIn);
System.Security.Cryptography.ICryptoTransform desdecrypt =
des.CreateDecryptor();
System.Security.Cryptography.CryptoStream cryptStreem =
new System.Security.Cryptography.CryptoStream(msIn,
desdecrypt,
System.Security.Cryptography.CryptoStreamMode.Read);
System.IO.StreamReader srOut =
new System.IO.StreamReader(cryptStreem,
System.Text.Encoding.UTF8);
string result = srOut.ReadToEnd();
srOut.Close();
cryptStreem.Close();
msIn.Close();
return result;
}
private static byte[] ResizeBytesArray(byte[] bytes, int newSize)
{
byte[] newBytes = new byte[newSize];
if (bytes.Length < newSize)
{
for (int i = 0; i < bytes.Length; i++)
newBytes[i] = bytes[i];
}
else
{
int pos = 0;
for (int i = newSize; i < bytes.Length; i++)
{
newBytes[pos++] ^= bytes[i];
if (pos >= newBytes.Length)
pos = 0;
}
}
return newBytes;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
| | Public Shared Function EncryptString(ByVal str As String, _
ByVal key As String) As String
Dim bytesIn As Byte() = System.Text.Encoding.UTF8.GetBytes(str)
Dim des As New System.Security.Cryptography.DESCryptoServiceProvider
Dim bytesKey As Byte() = System.Text.Encoding.UTF8.GetBytes(key)
des.Key = ResizeBytesArray(bytesKey, des.Key.Length)
des.IV = ResizeBytesArray(bytesKey, des.IV.Length)
Dim msOut As New System.IO.MemoryStream
Dim desdecrypt As System.Security.Cryptography.ICryptoTransform = _
des.CreateEncryptor()
Dim cryptStreem As New System.Security.Cryptography.CryptoStream( _
msOut, desdecrypt, _
System.Security.Cryptography.CryptoStreamMode.Write)
cryptStreem.Write(bytesIn, 0, bytesIn.Length)
cryptStreem.FlushFinalBlock()
Dim bytesOut As Byte() = msOut.ToArray()
cryptStreem.Close()
msOut.Close()
Return System.Convert.ToBase64String(bytesOut)
End Function
Public Shared Function DecryptString(ByVal str As String, _
ByVal key As String) As String
Dim des As New System.Security.Cryptography.DESCryptoServiceProvider
Dim bytesKey As Byte() = System.Text.Encoding.UTF8.GetBytes(key)
des.Key = ResizeBytesArray(bytesKey, des.Key.Length)
des.IV = ResizeBytesArray(bytesKey, des.IV.Length)
Dim bytesIn As Byte() = System.Convert.FromBase64String(str)
Dim msIn As New System.IO.MemoryStream(bytesIn)
Dim desdecrypt As System.Security.Cryptography.ICryptoTransform = _
des.CreateDecryptor()
Dim cryptStreem As New System.Security.Cryptography.CryptoStream( _
msIn, desdecrypt, _
System.Security.Cryptography.CryptoStreamMode.Read)
Dim srOut As New System.IO.StreamReader( _
cryptStreem, System.Text.Encoding.UTF8)
Dim result As String = srOut.ReadToEnd()
srOut.Close()
cryptStreem.Close()
msIn.Close()
Return result
End Function
Private Shared Function ResizeBytesArray(ByVal bytes() As Byte, _
ByVal newSize As Integer) As Byte()
Dim newBytes(newSize - 1) As Byte
If bytes.Length < newSize Then
Dim i As Integer
For i = 0 To bytes.Length - 1
newBytes(i) = bytes(i)
Next i
Else
Dim pos As Integer = 0
Dim i As Integer
For i = newSize To bytes.Length - 1
newBytes(pos) = newBytes(pos) Xor bytes(i)
pos += 1
If pos >= newBytes.Length Then
pos = 0
End If
Next i
End If
Return newBytes
End Function
|
コメント †
|