티스토리 뷰

======================================================

안녕하세요 Doridori 입니다. 


이번강의는 지난 강의에 이어서 본격적으로 암호화, 복호화에 대해서 이야기 해 보도록 하겠습니다.


진행 하다가 보니 기존에 16강부터 진행 했던 내용들을 포괄해서 진행하게 되었습니다. 


복습겸해서 한번 보시는것도 좋을 것 같습니다. 


이번에는 강의를 2개로 나누어서 올렸는데 첫번째 강의는 지난 강의에 만들었던 내용을 xml로 저장하고 불러오는 부분을 진행 하였으며 두번째 강의에 Rijndael (라인달) 클래스를 이용해서 암호화, 복호화를 진행 하였습니다.

======================================================


암호화, 복호화


Source UI) (지난 강의 참고)

Soruce 전체) Study_19_ENC_DEC (Clicker Game 2).zip

교재) 19강 암호화,복호화 (Clicker Game 2).pdf




지난 강의에서 Timer에 대해서 진행해 보았는데 이번에는 지난번에 만들었던 내용을 가지고 게임 Data를 Save, Load 하는 부분과 


게임 Data를 외부에서 수정해서 불러올 경우 게임 난이도에 영향을 줄수 있기 때문에 치트를 방지하기 위해서 암호화 시키는 부분과 암호화 된 내용을 프로그램에서 정상적으로 읽어오기 위해서 복호화 시키는 부분을 진행 하였습니다. 


암호화 시키는 부분은 Rijndael (라인달) 이라는 암호화, 복호화 방식을 이용하였으며 관심이 있으신 분들은 암호화, 복호화에 대해서 검색을 해보시면 많은 정보들을 찾아보실수 있습니다. 

(저는 정보처리기사 공부 할 때 내용 정보 밖에 모르지만 (기술사 준비도 하긴 했는데 아이가 나오면서 아쉽게 쉬고 있습니다. ㅜㅠ) 구글에는 능력자 분들이 많으 시니까요 ^^


XML Read, Write 하는 부분과 Rijndael (라인달) 클래스를 이용해서 암호화, 복호화 하는 부분의 경우 최대한 클래스 File을 가져다 몇가지만 수정 하면 바로 사용할 수 있게 만들었습니다. 


한번 사용해보시면 소스를 가지고 있으시다가 필요할 때 그냥 가져다 쓰시면 편할 것 같습니다.


※ 암호화 Key의 경우 32 byte로 구성되어 있으므로 영문이나 숫자기준으로 32자리를 맞춰서 만들어주시면 됩니다.



강의) 


2개의 강의로 나누었는데 게임 Data를 xml 형태로 저장하고 불러오는 부분하나 암호화, 복호화 시키는 부분 하나 이런식으로 나누었습니다. ^^;;



1. XML 형태로 저장하고 불러오기



2. Rijndael (라인달) 클래스를 이용해서 암호화, 복호화를 구현




Source)


Main Form 외 XML을 사용하는 Class, Rijndael을 구현한 Class로 나누어져 있습니다.


1. Main


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
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
 
 
namespace _19_Simple_Clicker
{
    public partial class Form1 : Form
    {
        Dictionary<stringstring> _dData = new Dictionary<stringstring>();
        CXMLControl _xml = new CXMLControl();
        string strPath = Application.StartupPath + "\\Save.txt";
 
        private double iTick = 0;  // 한 Tick 당 더할 값
        private double iTotal = 0;  // 전체 값
 
        private int i1Add = 1;  // 1 * LEVEL 값
        private int i1Level = 1;
 
        private int i3Add = 3;  // 3 * LEVEL 값
        private int i3Level = 0;
 
        private int i50Add = 0;   // 50 * LEVEL 값
        private int i50Level = 0;
 
 
        /// <summary>
        /// 진입점
        /// </summary>
        public Form1()
        {
            InitializeComponent();
        }
 
 
        /// <summary>
        /// Form이 Loading 될때 발생 하는 Event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {
            if (File.Exists(strPath))
            {
                // File이 있을 경우 File Loading
                _dData = _xml.fXML_Reader(strPath);
 
                iTick = double.Parse(_dData[CXMLControl._TICK]);
                iTotal = double.Parse(_dData[CXMLControl._TOTAL]);
                i1Level = int.Parse(_dData[CXMLControl._LEVEL_1]);
                i3Level = int.Parse(_dData[CXMLControl._LEVEL_3]);
                i50Level = int.Parse(_dData[CXMLControl._LEVEL_50]);
            }
 
            System.Windows.Forms.Timer oTimer = new System.Windows.Forms.Timer();
            oTimer.Enabled = true;
            oTimer.Interval = 100;
            oTimer.Tick += OTimer_Tick;
            oTimer.Start();
        }
 
        private void Form1_FormClosed(object sender, System.Windows.Forms.FormClosedEventArgs e)
        {
            _dData.Clear();
 
            _dData.Add(CXMLControl._TICK, iTick.ToString());
            _dData.Add(CXMLControl._TOTAL, iTotal.ToString());
            _dData.Add(CXMLControl._LEVEL_1, i1Level.ToString());
            _dData.Add(CXMLControl._LEVEL_3, i3Level.ToString());
            _dData.Add(CXMLControl._LEVEL_50, i50Level.ToString());
 
            _xml.fXML_Writer(strPath, _dData);
        }
 
 
        // 타이머에서 호출 할 Event (Interval 간격 기준)
        private void OTimer_Tick(object sender, EventArgs e)
        {
            iTick = i1Add + i3Add + i50Add;
            iTotal = iTotal + iTick;
 
            lblTickPoint.Text = string.Format("{0} (1:{1}), (3:{2}), (50:{3})", iTick.ToString(), i1Level.ToString(), i3Level.ToString(), i50Level.ToString());
            lblTotal.Text = iTotal.ToString();
        }
 
 
        /// <summary>
        /// Button을 Click 했을 경우 발생 하는 Event (Form Designer에서 Button 종류를 묶어서 하나의 Event로 받음)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAdd_Click(object sender, EventArgs e)
        {
            Button obtn = sender as Button;  // 받아온 Sender를 Button 형으로 변환 시킴
 
            // UI 작성 시 지정한 Name을 기준으로 Event가 발생 한 Button을 찾아서 프로그램 동작 수행
            switch (obtn.Name)
            {
                case "btn1Add":
                    if (iTotal > 100)
                    {
                        iTotal = iTotal - 100;
 
                        i1Level++;
                        i1Add = 1 * i1Level;
                    }
                    break;
                case "btn3Add":
                    if (iTotal > 300)
                    {
                        iTotal = iTotal - 300;
 
                        i3Level++;
                        i3Add = 3 * i3Level;
                    }
                    break;
                case "btn50Add":
                    if (iTotal > 5000)
                    {
                        iTotal = iTotal - 5000;
 
                        i50Level++;
                        i50Add = 50 * i50Level;
                    }
                    break;
                default:
                    break;
            }
        }
        
    }
}
 
cs


2. CXMLControl.cs ( XML Read, Write )

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
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
 
namespace _19_Simple_Clicker
{
    class CXMLControl
    {
        // Dictionary 및 XML의 항목을 정의 ( static(정적) 변수로 사용 : 프로그램 실행 시 메모리에 바로 할당
        public static string _TICK = "TICK";
        public static string _TOTAL = "TOTAL";
        public static string _LEVEL_1 = "LEVEL_1";
        public static string _LEVEL_3 = "LEVEL_3";
        public static string _LEVEL_50 = "LEVEL_50";
 
 
        /// <summary>
        /// XML을 저장 하기 위해 사용
        /// </summary>
        /// <param name="strXMLPath">저장 할 XML File의 경로 및 파일명</param>
        /// <param name="DXMLConfig">XML로 저장 할 항목</param>
        public void fXML_Writer(string strXMLPath, Dictionary<string,string> DXMLConfig)
        {
            StringBuilder sb = new StringBuilder();
 
            // using 범위 내에 XmlWriter를 정의 하여 using을 벗어 나게 될 경우 자동으로 Dispose 하여 메모리를 관리
            //using (XmlWriter wr = XmlWriter.Create(strXMLPath))
            using (XmlWriter wr = XmlWriter.Create(sb))
            {
                // XML 형태의 Data를 작성 (결과 및 예제 확인)
 
                wr.WriteStartDocument();
 
                // Setting#01
                wr.WriteStartElement("SETTING");
                wr.WriteAttributeString("ID""0001");  // attribute 쓰기
 
                wr.WriteElementString(_TICK, DXMLConfig[_TICK]);
                wr.WriteElementString(_TOTAL, DXMLConfig[_TOTAL]);
                wr.WriteElementString(_LEVEL_1, DXMLConfig[_LEVEL_1]);
                wr.WriteElementString(_LEVEL_3, DXMLConfig[_LEVEL_3]);
                wr.WriteElementString(_LEVEL_50, DXMLConfig[_LEVEL_50]);
 
                wr.WriteEndElement();
                wr.WriteEndDocument();
            }
 
            string strRijndaelText = CRijndael.EncryptString(sb.ToString(), CRijndael._bkey);
 
            File.WriteAllText(strXMLPath, strRijndaelText);
        }
 
 
        /// <summary>
        /// XML을 읽어 오기 위해 사용
        /// </summary>
        /// <param name="strXMLPath">읽어 올 XML File의 경로 및 파일명</param>
        /// <returns></returns>
        public Dictionary<stringstring> fXML_Reader(string strXMLPath)
        {
            string strRijndaelText = File.ReadAllText(strXMLPath);
            string strDECText = CRijndael.DecryptString(strRijndaelText, CRijndael._bkey);
 
 
            Dictionary<stringstring> DXMLConfig = new Dictionary<stringstring>();  // 읽어 온 XML Data를 Dictionary에 저장하기 위해 선언 및 초기화
 
            // using 범위 내에 XmlReader를 정의 하여 using을 벗어 나게 될 경우 자동으로 Dispose 하여 메모리를 관리
            using (XmlReader rd = XmlReader.Create(new StringReader(strDECText)))
            {
                // xml을 한줄 씩 읽으면서 필요 한 정보를 가져 옴
                while (rd.Read())
                {
                    if (rd.IsStartElement())
                    {
                        if (rd.Name.Equals("SETTING"))
                        {
                            string strID = rd["ID"];  // attribute 읽기   //여기 뭐지??  ID를 읽고 뭐하는거지??
                            rd.Read();  //다음 노드로 이동
 
                            string strTICK = rd.ReadElementContentAsString(_TICK, "");  // 키 값을 기준으로 결과 값을 가져 옴
                            DXMLConfig.Add(_TICK, strTICK);   // 키값과 가져온 결과 값을 Dictionary에 저장
 
                            string strTOTAL = rd.ReadElementContentAsString(_TOTAL, "");
                            DXMLConfig.Add(_TOTAL, strTOTAL);
 
                            string strLEVEL_1 = rd.ReadElementContentAsString(_LEVEL_1, "");
                            DXMLConfig.Add(_LEVEL_1, strLEVEL_1);
 
                            string strLEVEL_3 = rd.ReadElementContentAsString(_LEVEL_3, "");
                            DXMLConfig.Add(_LEVEL_3, strLEVEL_3);
 
                            string strLEVEL_50 = rd.ReadElementContentAsString(_LEVEL_50, "");
                            DXMLConfig.Add(_LEVEL_50, strLEVEL_50);
                        }
                    }
                }
            }
 
            return DXMLConfig;   // 작성 한 Dictionary를 반환
        }
    }
}
 
cs


3. CRijndael.cs ( 암호화, 복호화)

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
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
 
namespace _19_Simple_Clicker
{
    class CRijndael
    {
        public static byte[] _bkey = Encoding.UTF8.GetBytes("RijndaelKEY123456789012345678900");  // 32byte로 작성
 
        public static string EncryptString(string plainText, byte[] Key)
        {
            // Check arguments.
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException("plainText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            
            byte[] encrypted;
            // Create an RijndaelManaged object
            // with the specified key and IV.
            using (RijndaelManaged rijAlg = new RijndaelManaged())
            {
                rijAlg.Key = Key;
                rijAlg.IV = new byte[] { 0000000000000000 };
 
                // Create an encryptor to perform the stream transform.
                ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
 
                // Create the streams used for encryption.
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {
 
                            //Write all data to the stream.
                            swEncrypt.Write(plainText);
                        }
                        encrypted = msEncrypt.ToArray();
                    }
                }
            }
 
            string strOutPut = Convert.ToBase64String(encrypted);
 
            // Return the encrypted bytes from the memory stream.
            return strOutPut;
 
        }
 
        public static string DecryptString(string strText, byte[] Key)
        {
            // Check arguments.
            if (strText == null || strText.Length <= 0)
                throw new ArgumentNullException("strText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
 
            byte[] cipherText = Convert.FromBase64String(strText);
 
            // Declare the string used to hold
            // the decrypted text.
            string plaintext = null;
 
            // Create an RijndaelManaged object
            // with the specified key and IV.
            using (RijndaelManaged rijAlg = new RijndaelManaged())
            {
                rijAlg.Key = Key;
                rijAlg.IV = new byte[] { 0000000000000000 };
 
                // Create a decryptor to perform the stream transform.
                ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
 
                // Create the streams used for decryption.
                using (MemoryStream msDecrypt = new MemoryStream(cipherText))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                        {
                            // Read the decrypted bytes from the decrypting stream
                            // and place them in a string.
                            plaintext = srDecrypt.ReadToEnd();
                        }
                    }
                }
            }
 
            return plaintext;
        }
 
    }
}
 
cs


댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
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
글 보관함