인터넷상에 널리고 널린게 AES 코드지만, 저도 하나 올려봅니다 ...
C/C++ 언어로 작성된 AES 코드는 많지만 C#은 많지 않기에 ... 그냥 한번 만들어 보았습니다.
일단 C# 언어로 작성되어있지만,
C/C++코드가 필요하신 분들이 가급적 보기 쉽고 바꾸기 좋도록(?) 만들었습니다.
(그래봤자 foreach를 for 로 만든 것 밖에 없지만 ...)
저의 프로그래밍 철학은 무조건 가독성이 높게 가 모토이므로,
코드가 좀 단순 무식해 보일지도 모르지만 (사실 실력이 좋지 않습니다 ... 게다가 가독성도 .. (응?))
무릇 고급언어(C, JAVA 등)라는 것은 기계어(어셈블리어 등)에 비해 인간이 보기 쉽고
논리적으로 판단하기 쉽게 만들어야 한다고 생각합니다.
(이렇게 밖에 프로그램을 못하는 자의 비겁한 변명이려나요 .. 그냥 프로그램 잘 하는 사람들이 그냥 부러울 뿐이에요 ;;)
using System;
class AES128 {
private string strPlaintext;
private string strCiphertext;
private string strKey;
private uint[] Plaintext;
private uint[] Ciphertext;
private uint[] Key;
private uint[] State;
private uint[][] RoundKey;
private uint[] RCon = {0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000};
private byte[] SBox = {
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
};
public AES128() {
Plaintext = new uint[4];
Ciphertext = new uint[4];
Key = new uint[4]; State = new uint[4];
RoundKey = new uint[10][];
for(int i=0; i<10; i++) RoundKey[i] = new uint[16];
}
public void SetKey(string k) {
uint tmp;
if(k.Length != 32) Console.WriteLine("Key is not 16-byte data");
else strKey = k;
for(int i=0; i<4; i++) {
Key[i] = 0;
for(int j=0; j<8; j++) {
if((strKey[i*8+j] >= 'a') && (strKey[i*8+j] <= 'f')) {
tmp = strKey[i*8+j] - (uint)'a' + (uint)10;
} else if((strKey[i*8+j] >= 'A') && (strKey[i*8+j] <= 'F')) {
tmp = strKey[i*8+j] - (uint)'A' + (uint)10;
} else {
tmp = strKey[i*8+j] - (uint)'0';
}
Key[i] |= tmp<<(28-j*4);
}
}
}
public void SetPlaintext(string p) {
uint tmp;
if(p.Length != 32) Console.WriteLine("Plaintext is not 16-byte data");
else strPlaintext = p;
for(int i=0; i<4; i++) {
Plaintext[i] = 0;
for(int j=0; j<8; j++) {
if((strPlaintext[i*8+j] >= 'a') && (strPlaintext[i*8+j] <= 'f')) {
tmp = strPlaintext[i*8+j] - (uint)'a' + (uint)10;
} else if((strPlaintext[i*8+j] >= 'A') && (strPlaintext[i*8+j] <= 'F')) {
tmp = strPlaintext[i*8+j] - (uint)'A' + (uint)10;
} else {
tmp = strPlaintext[i*8+j] - (uint)'0';
}
Plaintext[i] |= tmp<<(28-j*4);
}
}
}
public string GetCiphertext() {
for(int i=0; i<4; i++) strCiphertext += String.Format("{0:X8}", Ciphertext[i]);
return strCiphertext;
}
public void Encryption() {
KeyExpansion(Key);
Ciphertext = AddRoundKey(Plaintext, Key);
for(int i=0; i<9; i++) {
Ciphertext = ByteSub(Ciphertext);
Ciphertext = ShiftRows(Ciphertext);
Ciphertext = MixColumn(Ciphertext);
Ciphertext = AddRoundKey(Ciphertext, RoundKey[i]);
}
Ciphertext = ByteSub(Ciphertext);
Ciphertext = ShiftRows(Ciphertext);
Ciphertext = AddRoundKey(Ciphertext, RoundKey[9]);
}
private void KeyExpansion(uint[] MasterKey) {
uint tmp;
tmp = ByteSub((MasterKey[3]<<8) | (MasterKey[3]>>24));
RoundKey[0][0] = MasterKey[0] ^ tmp ^ RCon[0];
RoundKey[0][1] = MasterKey[1] ^ RoundKey[0][0];
RoundKey[0][2] = MasterKey[2] ^ RoundKey[0][1];
RoundKey[0][3] = MasterKey[3] ^ RoundKey[0][2];
for(int i=1; i<10; i++) {
tmp = ByteSub((RoundKey[i-1][3]<<8) | (RoundKey[i-1][3]>>24));
RoundKey[i][0] = RoundKey[i-1][0] ^ tmp ^ RCon[i];
RoundKey[i][1] = RoundKey[i-1][1] ^ RoundKey[i][0];
RoundKey[i][2] = RoundKey[i-1][2] ^ RoundKey[i][1];
RoundKey[i][3] = RoundKey[i-1][3] ^ RoundKey[i][2];
}
}
private uint[] AddRoundKey(uint[] InData, uint[] RoundKey) {
uint[] OutData = new uint[4];
for(int i=0; i<4; i++) OutData[i] = InData[i] ^ RoundKey[i];
return OutData;
}
private uint ByteSub(uint InData) {
uint OutData;
OutData = 0;
for(int i=0; i<4; i++) OutData |= (uint)SBox[(InData>>(24-i*8))&0xFF] << (24-i*8); return OutData;
}
private uint[] ByteSub(uint[] InData) {
uint[] OutData = new uint[4];
for(int i=0; i<4; i++) {
OutData[i] = 0;
for(int j=0; j<4; j++) OutData[i] |= (uint)SBox[(InData[i]>>(24-j*8))&0xFF] << (24-j*8);
}
return OutData;
}
private uint[] ShiftRows(uint[] InData) {
uint[] OutData = new uint[4];
OutData[0] = (InData[0]&0xFF000000) | (InData[1]&0x00FF0000) | (InData[2]&0x0000FF00) | (InData[3]&0x000000FF);
OutData[1] = (InData[1]&0xFF000000) | (InData[2]&0x00FF0000) | (InData[3]&0x0000FF00) | (InData[0]&0x000000FF);
OutData[2] = (InData[2]&0xFF000000) | (InData[3]&0x00FF0000) | (InData[0]&0x0000FF00) | (InData[1]&0x000000FF);
OutData[3] = (InData[3]&0xFF000000) | (InData[0]&0x00FF0000) | (InData[1]&0x0000FF00) | (InData[2]&0x000000FF);
return OutData;
}
private uint[] MixColumn(uint[] InData) {
uint[] OutData = new uint[4];
for(int i=0; i<4; i++) OutData[i] = (uint)(Multiplication((byte)(InData[i]>>24), 0x02) ^ Multiplication((byte)(InData[i]>>16), 0x03) ^ Multiplication((byte)(InData[i]>>8), 0x01) ^ Multiplication((byte)InData[i], 0x01)) << 24 | (uint)(Multiplication((byte)(InData[i]>>24), 0x01) ^ Multiplication((byte)(InData[i]>>16), 0x02) ^ Multiplication((byte)(InData[i]>>8), 0x03) ^ Multiplication((byte)InData[i], 0x01)) << 16 | (uint)(Multiplication((byte)(InData[i]>>24), 0x01) ^ Multiplication((byte)(InData[i]>>16), 0x01) ^ Multiplication((byte)(InData[i]>>8), 0x02) ^ Multiplication((byte)InData[i], 0x03)) << 8 | (uint)(Multiplication((byte)(InData[i]>>24), 0x03) ^ Multiplication((byte)(InData[i]>>16), 0x01) ^ Multiplication((byte)(InData[i]>>8), 0x01) ^ Multiplication((byte)InData[i], 0x02));
return OutData;
}
private byte Multiplication(byte Fx, byte Gx) {
byte OutData;
byte Carry;
OutData = 0;
for(int i=0 ;i<8; i++) {
if((Gx & 1) == 1) OutData ^= Fx;
Carry = (byte)(Fx & 0x80);
Fx <<= 1;
if(Carry == 0x80) Fx ^= 0x1B; Gx >>= 1;
}
return OutData;
}
}
class Cipher_AES_Cs {
public static void Main() {
AES128 aes = new AES128();
aes.SetKey("2B7E151628AED2A6ABF7158809CF4F3C");
aes.SetPlaintext("3243F6A8885A308D313198A2E0370734");
aes.Encryption();
Console.WriteLine(aes.GetCiphertext());
}
}
언제나 오픈 마인드입니다 ... 'ㅂ'/