본문 바로가기
Delphi Tip/+Tip

델파이 암호화

by MonoSoft 2023. 7. 13.
728x90
반응형

델파이 암호화

728x90

 

 

데이터의 암호화는 보안의 가장 기본적인 형태이다.

 

그 동안 보안은 어느 군사작 전에서나 이용되는 용어로 여겨진게 사실이지만,

갈수록 시스템이 외부에 노출되 는 (특히 인터넷등의 네트워크망에서) 상황에서

데이터의 적절한 암호화는 아주 중요한 사안이 되고 있다.

 

여러분이 주변에서 쉽게 구할 수 있는 암호화 알고리즘들이 있고, 그것은 완벽에 가깝다.

여러분이 사용하고 있는 ID의 암호 역시 그런 알고리즘에 기반을 두고 암호화된다.

 

하지만, 이런 것들은 암호화에만 치중한 것으로 해독기능이 없다.

 

즉, 암호화 된 데이터를 다시 원본 데이터로 추출해 내는 역변환은 불가능한 것이다.

시스템의 운영자도 해당 사용자 ID의 암호를 변환해 원본 암호를 뽑아내는 것이 불가능하다.

 

여러분이 ID를 입력했을 때, 그것을 암호화 하여 이미 암호화 되어

저장된 데이터와 비교하는 방법을 취하기 때문이다.

 

날고기는 해커라도 이 암호를 해독 하는 것은 불가능하다.

이들이 취하는 방법 중 하나는 터미널의 입출력을 감시하다가 사용자가

암호를 입력할 때 암호를 추출해 내는 것이다.

 

특히 TCP/IP가 일반화 되고 있는 요즘의 상황에서

이것은 아주 쉽게 이루어질 수 있다.

 

이 강좌에서 제공될 소스는 Encrypt와 Decrypt가 가능한 암호화 방법이다.

 

이것의 장점에 대해서는 다음 강좌에서 설명한다.

 

기존 우리나라 통신망의 가장 큰 장점은 해커의 침입을 원천차단할 수 있었다는데 있다.

즉, X.25라는 프로토콜과 전용라인을 이용하여 1:1로 물려진

이 네트워 크는 외부의 침입이 불가능했다.

 

대부분의 국가전산망과 은행전산망이 이에 해당 한다.

즉 라인을 따낼 수 없으므로 접근이 원천차단되는 것이다.

 

우리나라에 있 어서 만큼은 국가전산망이나 은행전산망을 비집고 들어가

이리저리 데이터를 요리하는 것은 꿈에 불과한 것이 현실이다.

 

자신이 시스템에 접근할 수 있는 공무원이거나,

혹은 은행직원이라면 이것이 가능하겠지만,

아쉽게도 그들은 해커가 될 소질이 적은 사람들이다.

 

얼마전 국내에서 인터넷을 통한 은행서비스 접근이 모두 봉쇄되었다.

즉, telnet 접속으로의 홈뱅킹 기능이 전면 봉쇄된 것이다.

 

홈 뱅킹 도중에 사용자가 계좌번호와 비밀번호를 입력한다면,

그 네트워크 집단의 다른 사용자는 그 내용을 충분히 해킹할 수 있다.

 

따라서 근래에는 TCP/IP 접속 에도 암호화 기법을 동원해 홈뱅킹등에서 보안을 처리하지만,

이것은 웹브라우져 상의 이야기이고 telnet은 그 구성상 암호화 지원이 불가능하다.

 

이것은 TCP/IP 프로토콜의 문제는 아니다.

TCP/IP 프로토콜이 적용되는 네트워크 구조의 문제이다.

 

즉, 인터넷등 누구나 억세스할 수 있는 범위이냐, 아니면 전용 라인등으로

쌍방간에만 이루어지는 데이터 전송이냐의 차이다.

 

좀 우습게 표현하 자면 빨대로 꽂아서 먹느냐 아니면

접시에 부어서 여러명이 함께 찍어 먹느냐의 차이다.

 

TCP/IP등의 프로토콜은 패킷단위로 정보를 전송하고

경유지에서는 자신의 패킷만 받아들이고 나머지는 흘려보낸다.

 

하지만 이 경유지에서 다른 시스템으로 가는 패킷,

그 중 한사용자의 패킷만 걸러낸다면 그 사람이 현재 입력하는 내용,

그 사람이 현재 보고있는 화면등이 적나라하게 드러난다.

 

물론 이 정도를 구현하는 것에는 고난도의 기법이 요구되며,

이 정도만 구현해도 소위 말하는 해커 범주에 넣을 수 있다.

 

NT나 Win95로 치자면, 여러분이 Winsock.dll등의

각종 네트워크 관련 드라이버를 스스로 만들 수 없는 한은 불가능한 이야기이기 때문이다.

 

한가지 짚고 넘어가야 할 점은, 사용자와의 전쟁에서 이길 수 있는

프로그래머는 존재하지 않는다는 것이다. 좀 다른 예일 수는 있으나,

본인이 몇해전에 제작한 머드게임이 현재까지 하이텔과 천리안등에서 서비스되고 있다.

 

어떤 새로운 기능 을 지원하다 보면 사용자는 도저히 상상할 수 없는 방법을

동원해 그 기능을 이 용한 해킹을 한다.

 

예를들어 아이템의 복사나 금액의 배수증가등 이론상으로 불가능한 것들이

사소한 헛점을 이용해 발생하는 것이다. 정말 빌다시피 하여

그 기법(?)을 알아보면 너무나 의외에다 교묘한 것에 혀를 내두를 정도이다.

결국은 시행착오만이 보안 문제를 해결해 나갈 수 있는 열쇠가 된다.

아무리 완벽에 가 까운 보안체계도 헛점이 있을 수 밖에 없다.

 

설문조사에는 오차가 있고, 기업경영에는 로스(Loss)가 있으며,

암호화 기법에도 빈틈이 있다. 우리는 그 오차와 로스, 빈틈을 없앨 수는 없다.

단지 그것을 최소 화할 수 있을 뿐이다.

 

여러분이 작성하는 어플리케이션에도 이 암호화는 필수적인 것일 수 있다.

그 동 안 우리가 간과해 왔던 것에 약간의 관심을 기울여 보자.

미래를 위한 대비일 수 있다.

 

다음 강좌에서 아주 간단한, 하지만 본인이 생각하기에 별 빈틈이 없는

암호화 알고리즘의 소스를 제공할 것이다.

 

 

본 소스 코드에 사용되는 암호화 알고리즘은 Encrypt와 Decrypt가 가능한 것이다.

 

즉, Encrypt를 통해 암호화를 걸고 Decrypt를 통해 암호를 해독하는 것이 가능하다.

 

이렇게 해독기능을 부여하는 것은 여러 종류의 어플리케이션들에서 유용 할 수 있다.

 

단지, 사용자의 암호를 암호화 하여 저장하는 것이라면 Decrypt는 필요치 않다.

 

하지만 간단한 예로, 웹서버 어플리케이션 제작에서의 쿠기(Cookie) 사용을 생각 해보자.

쿠키는 웹 브라우져에 따라 어떤 형태로던 사용자 컴퓨터의 하드디스크 어딘가에 저장된다.

이 쿠키는 아주 간단히 열어서 내용을 파악할 수 있으므로

이 내용을 암호화 할 필요가 있다.

 

다음으로, 웹서버 어플리케이션이 이 쿠키를 이용하기 위해서는

쿠기를 읽어서 암호를 해독해야 원본 데이터를 알 수 있다.

 

Encrypt와 Decrypt를 지원하는 이 소스코드는 원래 볼랜드 사이트에서 받았던 것으로

본인이 다시 수정을 가한 것이다.

 

이 암호화 소스는 3개의 키를 쓴다.

키 두 개는 소스코드에 넣어두고 Encrypt를 걸 때 다른 한키를 인자로 준다.

 

Decrypt에서도 키를 인자로 받는데, 이 때 키 값이 다르다면 해독은 불가능하다.

 

3개의 키를 이용하므로 경우의 수를 생각해 볼 때,

키 값을 모르면 해독이 99.9% 불가능하다.

C1, C2 상수로 되어있는 기본 키 값 두 개를 여러분의 필요에 따라 변경하기 바란다.

이 두 개의 키 값은 어플리케이션 마다 다르게 주는 것이 좋을 것이다.

 

이 소스코드를 통해 다음과 같이 데이터가 변환된다.

 

Encrypt: '63AA24F1B6263E8F303F77D7364C495DFA7C3E84D84390C2'

Decrypt: 'VTOOL 델파이 강좌'

 

원래의 소스에는 Encrypt시의 데이터가 0-255값으로 이루어져 있었으나

필자가 Hex값으로 변환하는 루틴을 끼워넣었다.

 

이유는 0-255값 중 일반 TEXT 포맷으로 읽을 수 없는 컨트롤 값이 있고,

이런 것들은 Registry나 INI등에 저장하는데 문 제가 있기 때문이다.

 

외부에서 사용할 수 있는 두 개의 함수가 있다.

 

function Encrypt(const S: String; Key: Word): String;

 

문자열 데이터의 암호화는 Encrypt 함수를 통해 이루어진다.

인자 S로는 암호화 할 문자열를 준다.

 

그리고 Key에는 0..65536 사이의 키 값을 준다.

이 키 값은 Decrypt시 동일한 키 값을 주어야 한다.

 

함수를 호출하면 복귀값으로 Hex인 문자 열이 돌려진다.

 

function Decrypt(const S: String; Key: Word): String;

 

암호화된 문자열의 해독은 Decrypt 함수를 통해 이루어진다.

인자 S로는 암호화 된 문자열을 준다.

 

Key에는 0..65536 사이의 값을 주는데 Encrypt에서 사용한 Key 값과 동일한 값을 주어야 한다.

복귀값으로 해독된 문자열이 돌아온다.

 

아래의 소스코드를 UEncrypt.pas로 저장하라.

다음 강좌에서 이것을 이용한 예제 를 올리도록 하겠다.

 

unit UEncrypt;

 

interface

  function Encrypt(const S: String; Key: Word): String;

  function Decrypt(const S: String; Key: Word): String;

 

implementation

 

uses SysUtils;

 

const

  C1 = 74102;

  C2 = 12337;

  HexaChar : array [0..15] of Char = ( '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' );

  // Byte로 구성된 데이터를 Hexadecimal 문자열로 변환

  function ValueToHex(const S : String): String;

  var

    I : Integer;

  begin

    SetLength(Result, Length(S)*2); // 문자열 크기를 설정

    for I := 0 to Length(S)-1 do

    begin

      Result[(I*2)+1] := HexaChar[Integer(S[I+1]) shr 4];

      Result[(I*2)+2] := HexaChar[Integer(S[I+1]) and $0f];

    end;

  end;

 

// Hexadecimal로 구성된 문자열을 Byte 데이터로 변환

function HexToValue(const S : String) : String;

var

  I : Integer;

begin

  SetLength(Result, Length(S) div 2);

  for I := 0 to (Length(S) div 2) - 1 do

  begin

    Result[I+1] := Char(StrToInt('$'+Copy(S,(I*2)+1, 2))); end;

  end;

 

// 암호걸기

function Encrypt(const S: String; Key: Word): String;

var

  I: byte;

  FirstResult : String;

begin

SetLength(FirstResult, Length(S)); // 문자열의 크기를 설정

for I := 1 to Length(S) do

begin

  FirstResult[I] := char(byte(S[I]) xor (Key shr 8));

  Key := (byte(FirstResult[I]) + Key) * C1 + C2; end;

  Result := ValueToHex(FirstResult);

end;

 

// 암호풀기

function Decrypt(const S: String; Key: Word): String;

var

  I: byte;

  FirstResult : String;

begin

  FirstResult := HexToValue(S);

  SetLength( Result, Length(FirstResult) );

  for I := 1 to Length(FirstResult) do

  begin

    Result[I] := char(byte(FirstResult[I]) xor (Key shr 8));

    Key := (byte(FirstResult[I]) + Key) * C1 + C2;

  end;

end;

 

end.

 

 

ESource, EEncryptResult, EDecryptResult라는 세 개의 TEdit 컴퍼넌트를 폼에 두고,

BEncrypt, BDecrypt라는 두 개의 버튼을 폼에 둔다.

BEncrypt를 누르면 ESource.Text가 EEncryptResult.Text에 암호화 된 형태로 나타나고,

BDecrypt를 누르면 EDecryptResult.Text에 EEncryptResult.Text의 해독한 내용이 나타난다.

 

 

unit UTest;

 

interface

 

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

 

type

  TForm1 = class(TForm)

    ESource: TEdit;

    BEncrypt: TButton;

    Label1: TLabel;

    Label2: TLabel;

    EEncryptResult: TEdit;

    EDecryptResult: TEdit;

    Label3: TLabel;

    BDecrypt: TButton;

    procedure BEncryptClick(Sender: TObject);

    procedure BDecryptClick(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

  end;

 

var

  Form1: TForm1;

 

implementation

 

uses UEncrypt;

 

{$R *.DFM}

 

procedure TForm1.BEncryptClick(Sender: TObject);

begin

  EEncryptResult.Text := Encrypt( ESource.Text, 13579 );

end;

 

procedure TForm1.BDecryptClick(Sender: TObject);

begin

  EDecryptResult.Text := Decrypt( EEncryptResult.Text, 13579);

end;

 

end.

 

AES암호화

 

델파이에서 AES 암호화를 사용하기 위해서는 Cryptographic Service Provider API를 활용

 

uses

  SysUtils, IdCoderMIME, IdSSLOpenSSL, IdGlobal;

 

function AESEncrypt(plainText: string; password: string): string;

var

  encoder: TIdEncoderMIME;

  cipherText: TBytes;

  key: TBytes;

begin

  encoder := TIdEncoderMIME.Create(nil);

  try

    key := TIdSSLSession.GenerateRandomBytes(32); // 256-bit key

    cipherText := TIdSSLOpenSSL.EncryptAES256(plainText, key, password);

    Result := encoder.EncodeBytes(cipherText);

  finally

    encoder.Free;

  end;

end;

 

function AESDecrypt(cipherText: string; password: string): string;

var

  decoder: TIdDecoderMIME;

  decodedText: TBytes;

  key: TBytes;

begin

  decoder := TIdDecoderMIME.Create(nil);

  try

    key := TIdSSLSession.GenerateRandomBytes(32); // 256-bit key

    decodedText := decoder.DecodeBytes(cipherText);

    Result := TIdSSLOpenSSL.DecryptAES256(decodedText, key, password);

  finally

    decoder.Free;

  end;

end;

 

procedure Main;

var

  plaintext, password, ciphertext, decryptedtext: string;

begin

  plaintext := 'Hello, World!';

  password := 'MySecretPassword123';

  ciphertext := AESEncrypt(plaintext, password);

  WriteLn('Encrypted: ' + ciphertext);

  decryptedtext := AESDecrypt(ciphertext, password);

  WriteLn('Decrypted: ' + decryptedtext);

end;

 

begin

 

 

Main;

end.

 

위 예제에서는 IdCoderMIME 모듈을 사용하여 바이트 배열을 Base64로 인코딩하고 디코딩합니다.

TIdSSLOpenSSL 유닛에서 제공되는 EncryptAES256 및 DecryptAES256 함수를 사용하여

AES 암호화와 복호화를 수행합니다.

 

AES-256 암호화에는 256비트 키가 필요하므로 GenerateRandomBytes 함수를

사용하여 무작위 키를 생성합니다.

728x90
반응형

'Delphi Tip > +Tip' 카테고리의 다른 글

정해진 시간에 없어지는 MessageBox  (0) 2023.11.06
Drag and Drop(끌어서 놓기)  (0) 2023.07.26
가변 데이터의 저장 기법  (0) 2023.07.10
컨트롤의 Enter 처리  (0) 2023.07.04
프로그램 종료 막기  (0) 2023.06.29

댓글