본문 바로가기

others

AES, SHA 암호화 4, T-SQL

MS-SQL  내부에서 AES, SHA 암호화를 진행하겠습니다.

C# 코드로 DLL만들고 이걸 시퀄서버에 등록해서 펑션화 하겠습니다.

DLL을 만들기 위해 클래스 라이브러리 프로젝트를 생성하고.. 다음 코드로 빌드하여 DLL을 생성합니다.


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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
using System;
using System.Data.SqlTypes;
using System.IO;
using System.Security.Cryptography;
using System.Text;
 
///<summary>
///Crypto, 암복호화 유틸 클래스, MS-SQL에 ASSEMBLY로 등록해서 사용
///</summary>
///<remarks>
///author Francis Lee
///since 2015. 8. 21.
///version 1.0
///</remarks>
public partial class Crypto
{
    // 키
    private static readonly string KEY = "01234567890123456789012345678901";
 
    //128bit (16자리)
    private static readonly string KEY_128 = KEY.Substring(0128 / 8);
 
    //256bit (32자리)
    private static readonly string KEY_256 = KEY.Substring(0256 / 8);
 
    //AES 128 암호화.., CBC, PKCS7, 예외발생하면 null
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlString encryptAES128(SqlString plain)
    {
        try
        {
            string plainString = plain.ToString();
            //바이트로 변환 
            byte[] plainBytes = Encoding.UTF8.GetBytes(plainString);
 
            //레인달 알고리듬
            RijndaelManaged rm = new RijndaelManaged();
            //자바에서 사용한 운용모드와 패딩방법 일치시킴(AES/CBC/PKCS5Padding)
            rm.Mode = CipherMode.CBC;
            rm.Padding = PaddingMode.PKCS7;
            rm.KeySize = 128;
 
            //메모리스트림 생성
            MemoryStream memoryStream = new MemoryStream();
 
            //key, iv값 정의
            ICryptoTransform encryptor = rm.CreateEncryptor(Encoding.UTF8.GetBytes(KEY_128), Encoding.UTF8.GetBytes(KEY_128));
            //크립토스트림을 키와 IV값으로 메모리스트림을 이용하여 생성
            CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
 
            //크립트스트림에 바이트배열을 쓰고 플러시..
            cryptoStream.Write(plainBytes, 0, plainBytes.Length);
            cryptoStream.FlushFinalBlock();
 
            //메모리스트림에 담겨있는 암호화된 바이트배열을 담음
            byte[] encryptBytes = memoryStream.ToArray();
 
            //베이스64로 변환
            string encryptString = Convert.ToBase64String(encryptBytes);
 
            //스트림 닫기.
            cryptoStream.Close();
            memoryStream.Close();
 
            return new SqlString(encryptString);
        }
        catch (Exception)
        {
            return null;
        }
    }
 
    //AES128 복호화.., CBC, PKCS7, 예외발생하면 null
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlString decryptAES128(SqlString encrypt)
    {
        try
        {
            //SqlString을 string으로
            string encryptString = encrypt.ToString();
            //base64를 바이트로 변환 
            byte[] encryptBytes = Convert.FromBase64String(encryptString);
            //byte[] encryptBytes = Encoding.UTF8.GetBytes(encryptString);
 
            //레인달 알고리듬
            RijndaelManaged rm = new RijndaelManaged();
            //자바에서 사용한 운용모드와 패딩방법 일치시킴(AES/CBC/PKCS5Padding)
            rm.Mode = CipherMode.CBC;
            rm.Padding = PaddingMode.PKCS7;
            rm.KeySize = 128;
 
            //메모리스트림 생성
            MemoryStream memoryStream = new MemoryStream(encryptBytes);
 
            //key, iv값 정의
            ICryptoTransform decryptor = rm.CreateDecryptor(Encoding.UTF8.GetBytes(KEY_128), Encoding.UTF8.GetBytes(KEY_128));
            //크립토스트림을 키와 IV값으로 메모리스트림을 이용하여 생성
            CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
 
            //복호화된 데이터를 담을 바이트 배열을 선언한다. 
            byte[] plainBytes = new byte[encryptBytes.Length];
 
            int plainCount = cryptoStream.Read(plainBytes, 0, plainBytes.Length);
 
            //복호화된 바이트 배열을 string으로 변환
            string plainString = Encoding.UTF8.GetString(plainBytes, 0, plainCount);
 
            //스트림 닫기.
            cryptoStream.Close();
            memoryStream.Close();
 
            return new SqlString(plainString);
        }
        catch (Exception)
        {
            return null;
        }
    }
 
    //AES 256 암호화.., CBC, PKCS7, 예외발생하면 null
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlString encryptAES256(SqlString plain)
    {
        try
        {
            string plainString = plain.ToString();
            //바이트로 변환 
            byte[] plainBytes = Encoding.UTF8.GetBytes(plainString);
 
            //레인달 알고리듬
            RijndaelManaged rm = new RijndaelManaged();
            //자바에서 사용한 운용모드와 패딩방법 일치시킴(AES/CBC/PKCS5Padding)
            rm.Mode = CipherMode.CBC;
            rm.Padding = PaddingMode.PKCS7;
            rm.KeySize = 256;
 
            //메모리스트림 생성
            MemoryStream memoryStream = new MemoryStream();
 
            //key, iv값 정의
            ICryptoTransform encryptor = rm.CreateEncryptor(Encoding.UTF8.GetBytes(KEY_256), Encoding.UTF8.GetBytes(KEY_128));
            //크립토스트림을 키와 IV값으로 메모리스트림을 이용하여 생성
            CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
 
            //크립트스트림에 바이트배열을 쓰고 플러시..
            cryptoStream.Write(plainBytes, 0, plainBytes.Length);
            cryptoStream.FlushFinalBlock();
 
            //메모리스트림에 담겨있는 암호화된 바이트배열을 담음
            byte[] encryptBytes = memoryStream.ToArray();
 
            //베이스64로 변환
            string encryptString = Convert.ToBase64String(encryptBytes);
 
            //스트림 닫기.
            cryptoStream.Close();
            memoryStream.Close();
 
            return new SqlString(encryptString);
        }
        catch (Exception)
        {
            return null;
        }
    }
 
    //AES256 복호화.., CBC, PKCS7, 예외발생하면 null
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlString decryptAES256(SqlString encrypt)
    {
        try
        {
            //SqlString을 string으로
            string encryptString = encrypt.ToString();
            //base64를 바이트로 변환 
            byte[] encryptBytes = Convert.FromBase64String(encryptString);
            //byte[] encryptBytes = Encoding.UTF8.GetBytes(encryptString);
 
            //레인달 알고리듬
            RijndaelManaged rm = new RijndaelManaged();
            //자바에서 사용한 운용모드와 패딩방법 일치시킴(AES/CBC/PKCS5Padding)
            rm.Mode = CipherMode.CBC;
            rm.Padding = PaddingMode.PKCS7;
            rm.KeySize = 256;
 
            //메모리스트림 생성
            MemoryStream memoryStream = new MemoryStream(encryptBytes);
 
            //key, iv값 정의
            ICryptoTransform decryptor = rm.CreateDecryptor(Encoding.UTF8.GetBytes(KEY_256), Encoding.UTF8.GetBytes(KEY_128));
            //크립토스트림을 키와 IV값으로 메모리스트림을 이용하여 생성
            CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
 
            //복호화된 데이터를 담을 바이트 배열을 선언한다. 
            byte[] plainBytes = new byte[encryptBytes.Length];
 
            int plainCount = cryptoStream.Read(plainBytes, 0, plainBytes.Length);
 
            //복호화된 바이트 배열을 string으로 변환
            string plainString = Encoding.UTF8.GetString(plainBytes, 0, plainCount);
 
            //스트림 닫기.
            cryptoStream.Close();
            memoryStream.Close();
 
            return new SqlString(plainString);
        }
        catch (Exception)
        {
            return null;
        }
    }
 
    //SHA256 해쉬 함수 암호화.., 예외발생하면 null
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlString encryptSHA256(SqlString plain)
    {
        try
        {
            string plainString = plain.ToString();
            //바이트로 변환 
            byte[] plainBytes = Encoding.UTF8.GetBytes(plainString);
 
            SHA256Managed sm = new SHA256Managed();
 
            byte[] encryptBytes = sm.ComputeHash(plainBytes);
 
            //hex.. 16진수
            //string encryptString = BitConverter.ToString(encryptBytes).Replace("-", "").ToLower();
 
            //base64
            string encryptString = Convert.ToBase64String(encryptBytes);
 
            return new SqlString(encryptString);
        }
        catch (Exception)
        {
            return null;
        }
    }
}
cs


프로젝트를 생성할때 다음 쿼리를 통해 .net framework의 버전을 확인후 해당 버전을 선택해야 됩니다.

1
SELECT * FROM sys.dm_clr_properties;
cs


빌드 후 생성된 DLL파일(bin\Debug경로에 존재)을 MS-SQL이 설치되어 있는 서버에 복사하고.. 다음 쿼리를 실행합니다.

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
USE master;
EXEC sp_configure
     'clr enabled',
     1;
GO
RECONFIGURE;
 
GO
USE 해당데이터베이스;
 
--삭제, dll 갱신시 사용
DROP FUNCTION FN_ENCRYPT_AES128;
DROP FUNCTION FN_DECRYPT_AES128;
DROP FUNCTION FN_ENCRYPT_AES256;
DROP FUNCTION FN_DECRYPT_AES256;
DROP FUNCTION FN_ENCRYPT_SHA256;
DROP ASSEMBLY Crypto;
GO
 
--dll 등록
CREATE ASSEMBLY Crypto FROM 'D:\dll\Crypto.dll' WITH PERMISSION_SET = SAFE;
GO 
 
 
--함수 생성 [dll명].[클래스명].[함수명]
CREATE FUNCTION FN_ENCRYPT_AES128( @plain NVARCHAR(100))
RETURNS NVARCHAR(100)
AS
     EXTERNAL NAME
    Crypto.Crypto.encryptAES128;
GO
CREATE FUNCTION FN_DECRYPT_AES128( @encrypted NVARCHAR(100))
RETURNS NVARCHAR(100)
AS
     EXTERNAL NAME
    Crypto.Crypto.decryptAES128;
GO
CREATE FUNCTION FN_ENCRYPT_AES256( @plain NVARCHAR(100))
RETURNS NVARCHAR(100)
AS
     EXTERNAL NAME
    Crypto.Crypto.encryptAES256;
GO
CREATE FUNCTION FN_DECRYPT_AES256( @encrypted NVARCHAR(100))
RETURNS NVARCHAR(100)
AS
     EXTERNAL NAME
    Crypto.Crypto.decryptAES256;
GO
CREATE FUNCTION FN_ENCRYPT_SHA256( @plain NVARCHAR(100))
RETURNS NVARCHAR(100)
AS
     EXTERNAL NAME
    Crypto.Crypto.encryptSHA256;
GO
cs


다음 쿼리로 결과를 확인합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
DECLARE @str1 VARCHAR(1000= '암호화되지 않은 문자',
        @str2 VARCHAR(1000),
        @str3 VARCHAR(1000),
        @str4 VARCHAR(1000),
        @str5 VARCHAR(1000),
        @str6 VARCHAR(1000);
 
PRINT( 'plain : ' + @str1 );
 
SET @str2 = dbo.FN_ENCRYPT_AES128( @str1 );
PRINT( 'AES128 encrypted : ' + @str2 );
 
SET @str3 = dbo.FN_DECRYPT_AES128( @str2 );
PRINT( 'AES128 decrypted : ' + @str3 );
 
SET @str4 = dbo.FN_ENCRYPT_AES256( @str1 );
PRINT( 'AES256 encrypted : ' + @str4 );
 
SET @str5 = dbo.FN_deCRYPT_AES256( @str4 );
PRINT( 'AES256 decrypted : ' + @str5 );
 
SET @str6 = dbo.FN_ENCRYPT_SHA256( @str1 );
PRINT( 'SHA256 encrypted : ' + @str6 );
cs

실행결과

 


시퀄서버 내부에서 다음과 같이 암호화를 할 수 있는데 알고리즘에 대한 운용방식, 패딩, iv값등을 어떻게 제어하는지는 모르겠습니다. 데이터베이스 내부에서만 암호화를 진행한다면 간단하게 사용할 수 있습니다. iv값이 바뀌는지 암호화의 결과는 항상 틀립니다. 또한 해쉬함수의 경우 HashBytes란 펑션을 제공하나 SHA256은 2012버전 이상에서만 사용가능합니다.

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
/*
1. 서버 마스터 키 확인
서버 마스터 키는 SQL Server 암호화 계층의 루트 이다. 이는 인스턴스 생성시 생성된다. 
아래 쿼리를 사용하여 마스터키 확인이되지 않는 다면 수동으로 생성 해야 한다.
*/
 
SELECT *
  FROM master.sys.symmetric_keys
  WHERE name = '##MS_ServiceMasterKey##';
 
/*
2. 마스터 키 생성
이 키는 마스터키는 데이터를암호화 할 때 사용되는 암호를 정의 한다.
*/
 
-- 키 확인
SELECT *
  FROM sys.symmetric_keys;
-- 생성
CREATE MASTER KEY ENCRYPTION BY PASSWORD = '0123456789012345';
-- 키 확인
SELECT *
  FROM sys.symmetric_keys;
-- 마스터 키 삭제
--DROP MASTER KEY
 
/*
3. 인증서 생성 
인증서는 SQL Server의 공개키를 포함한 디지털 서명 보안 개체 이다.
*/
 
-- 인증서 확인
SELECT *
  FROM sys.certificates;
 
-- 인증서 생성
CREATE CERTIFICATE CerificateTest WITH SUBJECT = '테스트 인증서',EXPIRY_DATE = '12/31/2050';
 
-- 인증서 확인
SELECT *
  FROM sys.certificates;
 
-- 인증서 삭제
--DROP CERTIFICATE CerificateTest
 
/*
4. 대칭키를 생성
대칭키를사용하여 암호화 및 암호화 해독을 진행 한다.
*/
 
-- 키 확인
SELECT *
  FROM sys.symmetric_keys;
 
-- 키 생성
CREATE SYMMETRIC KEY SymmetricTest WITH ALGORITHM = AES_128 ENCRYPTION BY CERTIFICATE CerificateTest;
 
-- 키 확인
SELECT *
  FROM sys.symmetric_keys;
-- 키 삭제
--DROP SYMMETRIC KEY SymmetricTest
 
/*
5. 암호화
*/
 
OPEN SYMMETRIC KEY SymmetricTest DECRYPTION BY CERTIFICATE CerificateTest;
GO
DECLARE @decryptedChar VARCHAR( MAX ),
        @encryptedBinary VARBINARY( MAX ),
        @encryptedChar VARCHAR( MAX );
 
--평문
SET @decryptedChar = '암호화되지 않은 문자';
--암호화 바이너리
SET @encryptedBinary = ENCRYPTBYKEY( KEY_GUID( 'SymmetricTest' ),@decryptedChar );
--암호화 문자
SET @encryptedChar = CONVERT( VARCHAR( MAX ),@encryptedBinary,2 );
SELECT @decryptedChar AS '평문',
       @encryptedBinary AS '암호화 바이너리',
       @encryptedChar AS '암호화 문자',
       LEN( @encryptedChar ) AS '암호화 문자길이';
GO
CLOSE SYMMETRIC KEY SymmetricTest;
 
 
/*
6. 복호화
*/
 
OPEN SYMMETRIC KEY SymmetricTest DECRYPTION BY CERTIFICATE CerificateTest;
GO
DECLARE @encryptedChar VARCHAR( MAX ),
        @encryptedBinary VARBINARY( MAX ),
        @decryptedChar VARCHAR( MAX );
--암호화 문자
SET @encryptedChar = '003A89DBF6BDEE4A914F13D77806489B01000000DB4E2D7E18166FCE544AC3BD2CC0EB145DA6C9E1A9369773F23506B013814E5F0BF83AF57CFBEF80BB818C152E1525F5';
--암호화 바이너리
SET @encryptedBinary = CONVERT( VARBINARY( MAX ),@encryptedChar,2 );
--평문
SET @decryptedChar = CONVERT( VARCHAR( MAX ),DECRYPTBYKEY( @encryptedBinary ));
SELECT @encryptedChar AS '암호화 문자',
       @encryptedBinary AS '암호화 바이너리',
       @decryptedChar AS '평문';
GO
CLOSE SYMMETRIC KEY SymmetricTest;
cs


'others' 카테고리의 다른 글

AES, SHA 암호화 6, PHP  (1) 2018.04.26
AES, SHA 암호화 5, Swift  (0) 2016.04.21
AES, SHA 암호화 3, C#  (6) 2015.08.26
AES, SHA 암호화 2, PL/SQL  (0) 2015.08.26
AES, SHA 암호화 1, JAVA  (1) 2015.08.26