C#을 이용한 자동화 보안을 위한 간단한 암호화 기법(Enhancing Automation Security with Simple Encryption Techniques)

2024. 6. 12. 11:58Development👩🏻‍🦳/C#

암호화의 단계
[예제 3] 1차원 배열을 이용한 N글자 전치암호 프로그.zip
0.04MB
암호화리팩토링.zip
0.04MB

암호화의 필요성 및 용도 ( Necessity and purpose of encryption )

 

공정 자동화에서 암호화 및 데이터 섞기 기술은 데이터 보호, 안전한 통신, 프로세스 보안 등 다양한 목적으로 사용됩니다.

1. 데이터 보호 및 기밀성 유지

  • 생산 데이터 보호: 생산량, 공정 시간, 품질 검사 결과 등의 데이터를 암호화하여 보호.
  • 기계 간 안전한 통신: 기계 간 통신 데이터를 암호화하여 도청 방지.

2. 데이터 무결성 및 인증

  • 데이터 무결성 검증: 데이터 전송 중 변조 여부 확인.
  • 사용자 인증 및 접근 제어: 사용자 인증 시 암호화된 토큰 사용.

3. 프로세스 보안 및 이상 감지

  • 프로세스 보안 강화: 암호화된 명령어로 특정 프로세스 보호.
  • 이상 감지 시스템: 암호화된 데이터를 통해 비정상 활동 탐지.

4. 공급망 보안

  • 공급망 내 데이터 보호: 공급망 전체에서 데이터 암호화.

5. 원격 모니터링 및 제어

  • 원격 제어 시스템 보호: 원격 모니터링 및 제어 시 데이터 암호화.

암호화 방식

현재 구현된 암호화 방식은 기본적인 단순 치환 암호화입니다. 이는 각 문자를 특정 규칙에 따라 다른 문자로 대체하는 방식입니다. 향후 다중 치환 암호화, 블록 암호화, 대칭 키 암호화, 비대칭 키 암호화, 양자 암호화 등의 고급 암호화 기법 도입이 필요합니다.

ASCII 코드를 이용한 간단한 암호화 예제

ASCII 코드를 활용한 암호화는 문자 데이터를 숫자로 변환하는 기본적인 과정을 통해 암호화의 기초를 제공합니다.

예제 코드: C#

csharp코드 복사
char c = 'A';         // 문자 'A'
int asciiValue = (int)c;  // ASCII 코드 값 65
char newChar = (char)asciiValue; // 다시 문자 'A'로 변환

Console.WriteLine(asciiValue); // 출력: 65
Console.WriteLine(newChar);    // 출력: A

핵심 구현 과정 요약

1. 입력 유효성 검사

csharp코드 복사
if (textBox1.Text == "") { /* 경고 1 */ }
if (textBox4.Text == "") { /* 경고 2 */ }
if (textBox5.Text == "") { /* 경고 3 */ }
if (int.Parse(textBox4.Text) < 2 || int.Parse(textBox4.Text) > 7) { /* 경고 4 */ }
if (int.Parse(textBox4.Text) != textBox5.Text.Length) { /* 경고 5 */ }

2. 섞을 순서 유효성 검사

csharp코드 복사
for (int j = 0; j < iCutting_Giri; j++) {
    int iTemp = Convert.ToInt16(sSunceo[j]);
    if (iTemp < 1 || iTemp > iCutting_Giri) { /* 경고 6 */ }
    for (int k = j + 1; k < iCutting_Giri; k++) {
        if (sSunceo[j] == sSunceo[k]) { /* 경고 7 */ }
    }
}
// Check for three consecutive numbers in sequence
bool consecutiveFound = false;
for (int i = 0; i < iCutting_Giri - 2; i++) {
    int a = Convert.ToInt16(sSunceo[i]);
    int b = Convert.ToInt16(sSunceo[i + 1]);
    int c = Convert.ToInt16(sSunceo[i + 2]);

    if ((b == a + 1 && c == b + 1) || (b == a - 1 && c == b - 1)) {
        consecutiveFound = true;
        break;
    }
}

if (consecutiveFound) {
    /* 경고 9 */
}

3. 문자열을 정수로 변환 및 유효성 검사

csharp코드 복사
for (int iCount = 0; iCount < iMunjang_Giri; iCount++) {
    sTemp = textBox1.Text.Substring(iCount, 1);
    char cTemp = Convert.ToChar(sTemp);
    int iTemp = Convert.ToInt32(cTemp);
    if (iTemp < 97 || (iTemp > 122 && iTemp < 40000)) { /* 경고 10 */ }
}

4. 패딩 작업

csharp코드 복사
if (iMunjang_Giri % iCutting_Giri != 0) {
    int iPadding_Giri = iCutting_Giri - (iMunjang_Giri % iCutting_Giri);
    sTemp = textBox1.Text.Substring(iMunjang_Giri - 1, 1);
    char cTemp = Convert.ToChar(sTemp);
    int iTemp = Convert.ToInt32(cTemp);
    if (iTemp > 96 && iTemp < 123)
        textBox1.Text += sEng_Pad.Substring(0, iPadding_Giri);
    else
        textBox1.Text += sKor_Pad.Substring(0, iPadding_Giri);
}

5. 문자열 섞기 및 암호문 생성

csharp코드 복사
for (int j = 0; j < iMunjang_Giri; j += iCutting_Giri) {
    for (int k = 0; k < iCutting_Giri; k++)
        sTempArray[k] = sMunjang[j + k];
    for (int y = 0; y < iCutting_Giri; y++)
        sMunjang[j + y] = sTempArray[Convert.ToInt16(sSunceo[y]) - 1];
}
textBox2.Text = "";
for (int iCount = 0; iCount < iMunjang_Giri; iCount++) {
    char cTemp = Convert.ToChar(sMunjang[iCount]);
    int iTemp = Convert.ToInt32(cTemp);
    if (iTemp > 96 && iTemp < 123)
        textBox2.Text += Convert.ToChar(iTemp - (97 - 65));
    else
        textBox2.Text += sMunjang[iCount];
}

암호화 예제 코드

csharp코드 복사
using System;
using System.Text;

public class SimpleEncryption {
    public static string Encrypt(string plainText, string key) {
        char[] chars = plainText.ToCharArray();
        int[] order = Array.ConvertAll(key.ToCharArray(), c => (int)char.GetNumericValue(c) - 1);
        StringBuilder encrypted = new StringBuilder();

        for (int i = 0; i < chars.Length; i += order.Length) {
            char[] segment = new char[order.Length];
            for (int j = 0; j < order.Length; j++) {
                if (i + j < chars.Length) {
                    segment[order[j]] = chars[i + j];
                } else {
                    segment[order[j]] = ' '; // Padding with spaces
                }
            }
            encrypted.Append(segment);
        }
        return encrypted.ToString();
    }

    public static void Main() {
        string plainText = "hellothere";
        string key = "312";

        string encryptedText = Encrypt(plainText, key);
        Console.WriteLine("Encrypted: " + encryptedText);
    }
}

해킹(복호화) 코드 예제

csharp코드 복사
using System;
using System.Text;

public class SimpleDecryption {
    public static string Decrypt(string encryptedText, string key) {
        char[] chars = encryptedText.ToCharArray();
        int[] order = Array.ConvertAll(key.ToCharArray(), c => (int)char.GetNumericValue(c) - 1);
        int[] reverseOrder = new int[order.Length];

        // Creating reverse order
        for (int i = 0; i < order.Length; i++) {
            reverseOrder[order[i]] = i;
        }

        StringBuilder decrypted = new StringBuilder();

        for (int i = 0; i < chars.Length; i += order.Length) {
            char[] segment = new char[order.Length];
            for (int j = 0; j < order.Length; j++) {
                if (i + j < chars.Length) {
                    segment[j] = chars[i + reverseOrder[j]];
                }
            }
            decrypted.Append(segment);
        }
        return decrypted.ToString();
    }

    public static void Main() {
        string encryptedText = "elho htleer"; // Encrypted form of "hellothere" with key "312"
        string key = "312";

        string decryptedText = Decrypt(encryptedText, key);
        Console.WriteLine("Decrypted: " + decryptedText);
    }
}

코드 설명

  1. Encrypt 함수:
    • 평문(plainText)과 키(key)를 입력으로 받아 암호화된 텍스트를 반환합니다.
    • 키를 통해 문자의 순서를 결정하고, 해당 순서에 맞춰 문자를 재배열합니다.
    • 문자 길이보다 짧은 경우 공백으로 패딩합니다.
  2. Decrypt 함수:
    • 암호화된 텍스트(encryptedText)와 키(key)를 입력으로 받아 복호화된 텍스트를 반환합니다.
    • 키를 통해 문자의 원래 순서를 결정하는 reverseOrder 배열을 생성합니다.
    • 원래 순서에 따라 문자를 재배열하여 복호화된 텍스트를 생성합니다.

ASCII 코드 활용

  • Convert.ToInt16 및 char.GetNumericValue를 사용하여 문자 데이터를 숫자로 변환합니다.
  • StringBuilder를 사용하여 결과 문자열을 효율적으로 조립합니다.

장단점

장점

  • 간단한 이해: 기본적인 암호화 및 복호화 개념을 쉽게 이해할 수 있습니다.
  • ASCII 코드 활용: 문자 데이터를 처리하는 방법을 이해할 수 있습니다.

단점

  • 보안 수준 낮음: 매우 단순한 암호화 방법으로, 현대적인 보안 요구사항을 충족하지 못합니다.
  • 실제 적용의 한계: 학습 목적 외에 실무에서는 사용되지 않습니다.

비효율성 및 개선점

  1. goto 문 사용: 제어 구조를 통해 예외 처리 개선.
  2. 순서 검사: 중복 검사를 최소화.
  3. 사용자 입력 검사: 함수를 통한 코드 간결화.
  4. 하드코딩된 패딩 문자: 유연성 증대.

개선된 예시: Strategy 패턴 적용

인터페이스 정의

csharp코드 복사
public interface IEncryptionStrategy {
    string Encrypt(string plainText, int segmentLength, string shuffleOrder);
}

단순 전치 암호화 전략 구현

csharp코드 복사
public class SimpleTranspositionEncryption : IEncryptionStrategy {
    public string Encrypt(string plainText, int segmentLength, string shuffleOrder) {
        // 패딩 처리
        plainText = PadPlainText(plainText, segmentLength);
        // 평문을 세그먼트로 나눔
        string[] segments = SplitTextIntoSegments(plainText, segmentLength);
        // 세그먼트를 섞음
        string[] shuffledSegments = ShuffleSegments(segments, shuffleOrder);
        // 암호문 생성
        return CreateCipherText(shuffledSegments);
    }

    private string PadPlainText(string plainText, int segmentLength) {
        int paddingLength = segmentLength - (plainText.Length % segmentLength);
        if (paddingLength < segmentLength) {
            char paddingChar = plainText[plainText.Length - 1] < 128 ? 'x' : '하';
            plainText += new string(paddingChar, paddingLength);
        }
        return plainText;
    }

    private string[] SplitTextIntoSegments(string text, int segmentLength) {
        int segmentCount = text.Length / segmentLength;
        string[] segments = new string[segmentCount];
        for (int i = 0; i < segmentCount; i++) {
            segments[i] = text.Substring(i * segmentLength, segmentLength);
        }
        return segments;
    }

    private string[] ShuffleSegments(string[] segments, string shuffleOrder) {
        string[] shuffledSegments = new string[segments.Length];
        for (int i = 0; i < segments.Length; i++) {
            char[] shuffledSegment = new char[segments[i].Length];
            for (int j = 0; j < segments[i].Length; j++) {
                shuffledSegment[j] = segments[i][shuffleOrder[j] - '1'];
            }
            shuffledSegments[i] = new string(shuffledSegment);
        }
        return shuffledSegments;
    }

    private string CreateCipherText(string[] segments) {
        string cipherText = string.Join("", segments);
        return cipherText.ToUpper();
    }
}

Form1 클래스의 수정

csharp코드 복사
public partial class Form1 : Form {
    private IEncryptionStrategy encryptionStrategy;

    public Form1() {
        InitializeComponent();
        encryptionStrategy = new SimpleTranspositionEncryption();
    }

    private void buttonEncrypt_Click(object sender, EventArgs e) {
        if (IsValidInput()) {
            string cipherText = encryptionStrategy.Encrypt(
                textBoxPlain.Text,
                int.Parse(textBoxSegmentLength.Text),
                textBoxShuffleOrder.Text
            );
            textBoxCipher.Text = cipherText;
        }
    }

    private bool IsValidInput() {
        if (string.IsNullOrEmpty(textBoxPlain.Text)) {
            ShowError("평문을 입력하고 버튼을 누르세요.", "[ 경고 1 ]");
            textBoxPlain.Focus();
            return false;
        }
        if (string.IsNullOrEmpty(textBoxSegmentLength.Text)) {
            ShowError("나눌 길이를 입력하고서 버튼을 누르세요.", "[ 경고 2 ]");
            textBoxSegmentLength.Focus();
            return false;
        }
        if (string.IsNullOrEmpty(textBoxShuffleOrder.Text)) {
            ShowError("섞을 순서를 입력하고서 버튼을 누르세요.", "[ 경고 3 ]");
            textBoxShuffleOrder.Focus();
            return false;
        }
        if (!int.TryParse(textBoxSegmentLength.Text, out int segmentLength) || segmentLength < 2 || segmentLength > 7) {
            ShowError("나눌 길이는 2~7 사이의 값입니다.", "[ 경고 4 ]");
            textBoxSegmentLength.Focus();
            textBoxSegmentLength.SelectAll();
            return false;
        }
        if (textBoxShuffleOrder.Text.Length != segmentLength) {
            ShowError("나눌 길이와 섞을 순서의 길이가 일치하지 않습니다.", "[ 경고 5 ]");
            textBoxShuffleOrder.Focus();
            textBoxShuffleOrder.SelectAll();
            return false;
        }
        if (!IsValidShuffleOrder(textBoxShuffleOrder.Text, segmentLength)) {
            return false;
        }
        return true;
    }

    private void ShowError(string message, string title) {
        MessageBox.Show(message, title);
    }

    private bool IsValidShuffleOrder(string shuffleOrder, int segmentLength) {
        bool[] isUsed = new bool[segmentLength];

        for (int i = 0; i < segmentLength; i++) {
            if (!int.TryParse(shuffleOrder[i].ToString(), out int order) || order < 1 || order > segmentLength) {
                ShowError("섞을 순서는 1보다 작거나 나눌 길이보다 클 수 없습니다.", "[ 경고 6 ]");
                textBoxShuffleOrder.Focus();
                textBoxShuffleOrder.SelectionStart = i;
                textBoxShuffleOrder.SelectionLength = 1;
                return false;
            }
            if (isUsed[order - 1]) {
                ShowError("동일한 순서가 두번 입력되었습니다", "[ 경고 7 ]");
                textBoxShuffleOrder.Focus();
                textBoxShuffleOrder.SelectionStart = i;
                textBoxShuffleOrder.SelectionLength = 1;
                return false;
            }
            isUsed[order - 1] = true;
        }
        return true;
    }
}

이 접근 방식은 암호화 개념의 기초를 이해하는 데 유용하지만, 실제 보안 애플리케이션에는 더 강력한 암호화 알고리즘이 필요합니다.