RSA 알고리즘에 흥미가 있어서 시작했지만 만들 때 꽤나 고생한 프로그램. 나는 타입별로 변수명을 몇개 정해놓고 그 안에서 쓰는 경향이 있는데 소수인지 판별하는 메소드를 다른 파일에서 가져왔다가 클래스변수와 지역변수명이 겹친게 문제였다(심지어 뭔 생각이었는지 가져올 때 파라미터 명만 수정해서 더 찾기 힘들엇음). 아예 안되면 모르겠는데 소수가 얻어걸리면 정상적으로 작동하니(당시엔 왜그런지 몰랐음) 갈아 엎을수도 없고...그러다 오기가 생겨 새로운 파일에 코드를 전부 다시 짠 후 코드 비교 프로그램에 넣어 겨우 원인을 찾아냈다.
※ 구현 기능
○ 할당된 공개 키와 개인 키 확인 기능
○ 암호화 버튼 클릭 시 문자열을 유니코드로 변환 후 암호화
○ 해독 버튼 클릭 시 암호를 해독하여 유니코드를 얻은 후 유니코드를 문자열로 재변환
○ 리셋 버튼 클릭 시 공개 키와 개인 키를 새로 할당
코드
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace RSA
{
public partial class Form1 : Form
{
RSA rsa;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
rsa = new RSA();
rsa.makeArgs();
}
private void btnPubK_Click(object sender, EventArgs e)
{
btnPubK.Text = rsa.getPubK.ToString();
}
private void btnPriK_Click(object sender, EventArgs e)
{
btnPriK.Text = rsa.getPriK.ToString();
}
private void btnEncode_Click(object sender, EventArgs e) //암호화 버튼
{
richTextBox3.Clear();
richTextBox6.Clear();
int[] bit = toBit(richTextBox1.Text);
rsa.encode(bit);
int[] tmp = rsa.getCode();
foreach (int j in bit)
richTextBox6.AppendText(j.ToString() + " ");
foreach (int i in tmp)
richTextBox3.AppendText(i.ToString() + " ");
}
private void btnPass_Click(object sender, EventArgs e) //전달 버튼
{
richTextBox2.Clear();
int[] tmp = rsa.getCode();
foreach (int i in tmp)
richTextBox2.AppendText(i.ToString() + " ");
}
private void btnDecode_Click(object sender, EventArgs e) //해독 버튼
{
richTextBox4.Clear();
richTextBox5.Clear();
int[] m = rsa.getCode();
int[] tmp = rsa.decode(m);
foreach (int i in tmp)
{
richTextBox4.AppendText(i.ToString() + " ");
}
richTextBox5.Text = toKor(tmp);
}
private void btnClean_Click(object sender, EventArgs e) //모두 지우기 버튼
{
richTextBox1.Clear();
richTextBox2.Clear();
richTextBox3.Clear();
richTextBox4.Clear();
richTextBox5.Clear();
richTextBox6.Clear();
}
private void btnReset_Click(object sender, EventArgs e) //리셋 버튼
{
rsa.makeArgs();
btnPubK.Text = "공개 키 확인";
btnPriK.Text = "개인 키 확인";
richTextBox1.Clear();
richTextBox2.Clear();
richTextBox3.Clear();
richTextBox4.Clear();
richTextBox5.Clear();
richTextBox6.Clear();
}
public int[] toBit(string kor) //문자열을 유니코드로 변환
{
Encoding unicode = Encoding.Unicode;
byte[] unicodeBytes = unicode.GetBytes(kor);
int[] bits = new int[unicodeBytes.Length];
for (int i = 0; i < unicodeBytes.Length; i++)
bits[i] = unicodeBytes[i];
return bits;
}
private string toKor(int[] kor) //유니코드를 문자열로 변환
{
Encoding unicode = Encoding.Unicode;
byte[] unibyte = new byte[kor.Length];
for (int i = 0; i < unibyte.Length; i++)
unibyte[i] = (byte)kor[i];
char[] uniChars = new char[unicode.GetCharCount(unibyte, 0, unibyte.Length)];
unicode.GetChars(unibyte, 0, unibyte.Length, uniChars, 0);
return new string(uniChars);
}
}
public class RSA
{
private int p = 7, q = 11, n, phiN, pubK, priK;
private static int SEED = 127;
private List<int> pubKList = new List<int>(); //공개 키 후보 리스트
private List<int> priKList = new List<int>(); //개인 키 후보 리스트
private int[] code;
public int getPubK { get { return pubK; } } //공개 키 얻기
public int getPriK { get { return priK; } } //개인 키 얻기
public int[] getCode()
{
int[] copy = new int[code.Length];
Array.Copy(code, copy, copy.Length);
return copy;
}
public void encode(int[] bits) //문자열 암호화
{
code = new int[bits.Length];
for (int i = 0; i < bits.Length; i++)
code[i] = singleEncode(bits[i]);
}
private int singleEncode(int c) //한글자 암호화
{
int tmp = 1;
for (int i = 0; i < pubK; i++)
{
tmp = (tmp * c) % n;
}
return tmp;
}
public int[] decode(int[] ps) //문자열 해독
{
int[] ims = new int[ps.Length];
for (int i = 0; i < ps.Length; i++)
ims[i] = singleDecode(ps[i]);
return ims;
}
private int singleDecode(int k) //한글자 해독
{
int tmp = 1;
for (int i = 0; i < priK; i++)
tmp = (tmp * k) % n;
return tmp;
}
public void makeArgs() //키 계산용 아규먼트와 키 생성
{
int distance = SEED / 2;
Random r = new Random(SEED + System.DateTime.Now.Millisecond);
this.p = r.Next(SEED - distance, SEED);
while (!isPrime(p)) //소수 p 생성
this.p = r.Next(SEED++);
this.q = r.Next(SEED, SEED + distance);
while (!isPrime(q) || q == p) //p와 다른소수 q 생성
this.q = r.Next(SEED, SEED + distance);
this.n = p * q; //N=p*q
this.phiN = (p - 1) * (q - 1); //phiN(파이엔)=(p-1)*(q-1)
makePubK();
selectPubk();
makePriK();
selectPriK();
}
private void makePubK() //공개 키 생성
{
pubKList.Clear();
for (int i = 2; i < phiN; i++)
{
if (i != p && i != q && (i < phiN) && (GCD(i, phiN) == 1)) //p와q가 아니고 파이엔과 서로소인 수
pubKList.Add(i);
}
}
private void selectPubk() //공개 키 후보 리스트에서 선택
{
Random r = new Random(SEED + System.DateTime.Now.Millisecond);
pubK = pubKList[r.Next(pubKList.Count)];
}
private void makePriK() //개인 키 후보 생성
{
priKList.Clear();
for (int i = 2; i < phiN; i++)
if (pubK != i && (pubK * i) % phiN == 1) //
priKList.Add(i);
}
private void selectPriK() //개인 키 후보 리스트에서 선택
{
int iters = 0;
int count = priKList.Count;
while (count == 0) //공개 키에 만족하는 개인 키가 없을 시 공개 키 변경
{
iters++;
makePriK();
count = priKList.Count;
if (iters == 10)
{
selectPubk();
iters = 0;
}
}
priK = priKList[0];
}
private bool isPrime(int n) //소수인지 확인
{
for (int i = 2; i <= n / 2; i++)
{
if (n % i == 0)
return false;
}
return true;
}
private int GCD(int a, int b) //최대공약수 구하기
{
if (b == 0)
return a;
else
return GCD(b, a % b);
}
}
}
'C# > 개발' 카테고리의 다른 글
[C#] 바이오리듬 (0) | 2022.08.05 |
---|---|
[C#] 하노이의 탑 (0) | 2022.08.05 |
[C#] 로또 (0) | 2022.08.05 |
[C#] 웹 이미지 다운로더 (0) | 2022.08.05 |
[C#] 시리얼 통신과 장비조작 (0) | 2022.08.05 |