unit Tocsg.COLib.Encrypt; interface uses Winapi.Windows, System.SysUtils, System.Classes, Vcl.Dialogs, System.Math, System.IOUtils, Tocsg.COLib, Tocsg.Safe; const SIG_CO_DRM = 'ToCSG_CO'; DRM_CO_VER = 1; MAX_HEAD_STR = 260; type // Çì´õ ±¸Á¶Ã¼ (Packed·Î ¼±¾ðÇÏ¿© ¸Þ¸ð¸® Á¤·Ä °íÁ¤) TTgDrmHead = packed record wVer: WORD; dwCustomerCode: DWORD; sEmpNo, sHostName, sPartName, sPgName: array [0..MAX_HEAD_STR - 1] of UTF8Char; dtEnc: TDateTime; end; TTgColibDrm = class private class function EncryptKEK(const pKek: TBytes; const sFilePath: string): Boolean; class function DecryptKEK(const sPath: string): TBytes; class procedure EncryptDEKtoKEK(const pDek, pKek: TBytes); class function DecryptDEKtoKEK(sPath:string; pKek:TBytes): TBytes; class function CheckSig(aStream: TStream): Boolean; // Çì´õ Á¤º¸¸¦ ÀÐ°í º¹È£È­ÇÏ¿© ½ºÆ®¸² À§Ä¡¸¦ µ¥ÀÌÅÍ ½ÃÀÛÁ¡À¸·Î À̵¿ class function ExtractHead(aSrcStream: TStream; pDek: TBytes; var aHead: TTgDrmHead): Boolean; public class function CreateSalt(const iKeylen: Integer): TBytes; class function CreateKEK(const sPass, sSalt: AnsiString; Iterations, iKeyLen: Integer): TBytes; class procedure CreateDEK(const iKeylen: Integer; pKek: TBytes); class function GetKEK(sPath:string ): TBytes; class function GetDEK(sPath:string; pKek:TBytes): TBytes; // Çì´õ Á¤º¸¸¦ »ý¼ºÇÏ°í ÆÄÀÏ ½ºÆ®¸²¿¡ ±â·Ï //class function SetHead(aDstStream: TStream; pDek: TBytes; sEmpNo, sHostName, sPartName, sPgName: UTF8String): Boolean; //class procedure DoEncrypt(sPath: string; pDek: TBytes; sEmpNo, sHostName, sPartName, sPgName: UTF8String); class function SetHead(aDstStream: TStream; pDek: TBytes): Boolean; class function DoEncrypt(sPath: string; pDek: TBytes): Boolean; class procedure DoDecrypt(sPath: string; pDek: TBytes); class function IsEncrypt(sPath: string): Boolean; end; implementation { TTgColibDrm } type TDataBlob = record cbData: DWORD; pbData: PByte; end; PDataBlob = ^TDataBlob; function CryptProtectData(pDataIn: PDataBlob; szDataDescr: PWideChar; pOptionalEntropy: PDataBlob; pvReserved: Pointer; pPromptStruct: Pointer; dwFlags: DWORD; pDataOut: PDataBlob): BOOL; stdcall; external 'crypt32.dll' name 'CryptProtectData'; function CryptUnprotectData(pDataIn: PDataBlob; ppszDataDescr: PPWideChar; pOptionalEntropy: PDataBlob; pvReserved: Pointer; pPromptStruct: Pointer; dwFlags: DWORD; pDataOut: PDataBlob): BOOL; stdcall; external 'crypt32.dll' name 'CryptUnprotectData'; function ConcatBytes(const pBuf1, pBuf2: TBytes): TBytes; begin SetLength(Result, Length(pBuf1) + Length(pBuf2)); Move(pBuf1[0], Result[0], Length(pBuf1)); Move(pBuf2[0], Result[Length(pBuf1)], Length(pBuf2)); end; procedure XORBytes(var pBuf1: TBytes; const pBuf2: TBytes); var i: Integer; begin for i := 0 to Min(Length(pBuf1), Length(pBuf2)) - 1 do pBuf1[i] := pBuf1[i] xor pBuf2[i]; end; class function TTgColibDrm.CreateSalt(const iKeylen: Integer): TBytes; var nResult: Integer; pSalt: TBytes; sPath: string; begin sPath := 'salt.bin'; if TFile.Exists(sPath) then begin Result := TFile.ReadAllBytes(sPath); Exit; end; // ÆÄÀÏÀÌ ¾ø´Â °æ¿ì: »õ·Î¿î Salt »ý¼º SetLength(pSalt, iKeylen); // SHA-256 ³­¼ö nResult := COLibDrbg(@pSalt[0], Length(pSalt), nil, 0, nil, 0, 0); if nResult = COLIB_SUCCESS then begin // »ý¼º ¼º°ø ½Ã ÆÄÀÏ·Î ±â·Ï try TFile.WriteAllBytes(sPath, pSalt); Result := pSalt; except on E: Exception do begin ShowMessage('Salt ÆÄÀÏ ÀúÀå Áß ¿À·ù: ' + E.Message); Result := nil; end; end; end else begin // »ý¼º ½ÇÆÐ ½Ã ó¸® ShowMessage('Salt »ý¼º ½ÇÆÐ: ' + IntToStr(nResult) + ', ' + COLibGetErrorStr(nResult)); Result := nil; end; end; class function TTgColibDrm.EncryptKEK(const pKek: TBytes; const sFilePath: string): Boolean; var DataIn, DataOut: TDataBlob; FileStream: TFileStream; begin Result := False; if Length(pKek) = 0 then begin ShowMessage('KEK°¡ ºñ¾îÀÖ½À´Ï´Ù.'); Exit; end; // TBytes µ¥ÀÌÅ͸¦ DataIn ±¸Á¶Ã¼¿¡ ¿¬°á DataIn.pbData := @pKek[0]; DataIn.cbData := Length(pKek); // DPAPI ¾Ïȣȭ È£Ãâ (ÇöÀç »ç¿ëÀÚ °èÁ¤À¸·Î¸¸ º¹È£È­ °¡´ÉÇϵµ·Ï ¼³Á¤) // ¼¼ ¹øÂ° ÀÎÀÚ´Â ¿£Æ®·ÎÇÇ(Ãß°¡ ¾ÏÈ£) if CryptProtectData(@DataIn, nil, nil, nil, nil, 0, @DataOut) then begin try FileStream := TFileStream.Create(sFilePath, fmCreate); try FileStream.WriteBuffer(DataOut.pbData^, DataOut.cbData); Result := True; finally FileStream.Free; end; finally // DPAPI¿¡¼­ ÇÒ´çÇÑ ¸Þ¸ð¸® ÇØÁ¦ LocalFree(HLOCAL(DataOut.pbData)); end; end else begin ShowMessage('CryptProtectData is failed: ' + IntToStr(GetLastError)); end; end; class function TTgColibDrm.DecryptKEK(const sPath: string): TBytes; var DataIn, DataOut: TDataBlob; ms: TMemoryStream; begin SetLength(Result, 0); if not FileExists(sPath) then begin ShowMessage('KEK°¡ Á¸ÀçÇÏÁö ¾Ê½À´Ï´Ù'); Exit; end; ms := TMemoryStream.Create; try ms.LoadFromFile(sPath); DataIn.pbData := ms.Memory; DataIn.cbData := ms.Size; // DPAPI º¹È£È­ È£Ãâ if CryptUnprotectData(@DataIn, nil, nil, nil, nil, 0, @DataOut) then begin try // º¹È£È­µÈ ¹ÙÀ̳ʸ® µ¥ÀÌÅ͸¦ TBytes·Î º¹»ç SetLength(Result, DataOut.cbData); if DataOut.cbData > 0 then Move(DataOut.pbData^, Result[0], DataOut.cbData); finally LocalFree(HLOCAL(DataOut.pbData)); end; end else begin ShowMessage('CryptUnprotectData is failed: ' + IntToStr(GetLastError)); end; finally ms.Free; end; end; class function TTgColibDrm.CreateKEK(const sPass, sSalt: AnsiString; Iterations, iKeyLen: Integer): TBytes; var i, n, nBlockCnt: Integer; pSaltAndBlkIdx, pBlockIdx, pTemp, U_prev, U_current, pPass, pSalt: TBytes; ci: TCIPHER_INFO; nOut, nResult: Integer; path: string; begin path := 'kek.bin'; if TFile.Exists(path) then begin // ÆÄÀÏÀÌ Á¸ÀçÇÏ¸é º¹È£È­ÈÄ ¸®ÅÏ //ShowMessage('½ÇÁ¦ °¨ÁöµÈ °æ·Î: ' + TPath.GetFullPath(path)); Result := DecryptKEK(path); Exit; end; SetLength(Result, iKeyLen); ZeroMemory(Result, iKeyLen); if (sPass = '') or (sSalt = '') then exit; n := Length(sPass); if n < 16 then begin SetLength(pPass, 16); ZeroMemory(pPass, 16); end else SetLength(pPass, n); CopyMemory(pPass, @sPass[1], n); n := Length(sSalt); SetLength(pSalt, n); CopyMemory(pSalt, @sSalt[1], n); ZeroMemory(@ci, SizeOf(ci)); SetLength(pBlockIdx, 4); SetLength(pTemp, iKeyLen); SetLength(U_prev, iKeyLen); SetLength(U_current, iKeyLen); // PBKDF // 32¹ÙÀÌÆ® ´ÜÀ§(SHA256)ÀÇ °¢ ºí·Ï¸¶´Ù ´ÙÀ½À» ¼öÇà // 1) U©û = HMAC(Pass, Salt || BlockIndex) // 2) U_n = HMAC(Pass, U_{n-1}) // 3) U_{n-1} XOR U_n // 32 ¹ÙÀÌÆ®¾¿ ³ª´²¼­ ºí·Ï°³¼ö °è»ê nBlockCnt := (iKeyLen + 31) div iKeyLen; // SHA256 = 32 bytes for i := 1 to nBlockCnt do begin // BlockIndex = i (Big Endian) // ºí·Ï ¹øÈ£¸¦ Big EndianÀ¸·Î Ç¥ÇöÇÑ 4¹ÙÀÌÆ® ex) i=[0,0,0,1] // int(i)·Î Ç¥Çö pBlockIdx[0] := Byte((i shr 24) and $FF); pBlockIdx[1] := Byte((i shr 16) and $FF); pBlockIdx[2] := Byte((i shr 8) and $FF); pBlockIdx[3] := Byte(i and $FF); // Salt || int(i) pSaltAndBlkIdx := ConcatBytes(pSalt, pBlockIdx); nOut := 0; // 1) U©û = HMAC(Pass, Salt || BlockIndex) COLibSetCipherInfo(@ci, COLIB_MODE_HMAC, BLOCK_CRYPTO_MODE_UNUSED, COLIB_PAD_TYPE_UNUSED, @pPass[0], Length(pPass), nil, 0, @pSaltAndBlkIdx[0], Length(pSaltAndBlkIdx), @U_prev[0], @nOut); nResult := COLibEncrypt(@ci); if nResult <> COLIB_SUCCESS then exit; CopyMemory(pTemp, U_prev, Length(U_prev)); for n := 2 to Iterations do begin nOut := 0; // 2) U_n = HMAC(Pass, U_{n-1}) COLibSetCipherInfo(@ci, COLIB_MODE_HMAC, BLOCK_CRYPTO_MODE_UNUSED, COLIB_PAD_TYPE_UNUSED, @pPass[0], Length(pPass), nil, 0, @U_prev[0], Length(U_prev), @U_current[0], @nOut); nResult := COLibEncrypt(@ci); if nResult <> COLIB_SUCCESS then exit; // 3) U_{n-1} XOR U_n XORBytes(pTemp, U_current); CopyMemory(U_prev, U_current, Length(U_current)); end; Move(pTemp[0], Result[(i - 1) * iKeyLen], Min(iKeyLen, iKeyLen - (i - 1) * iKeyLen)); end; if not (EncryptKEK(Result, path)) then begin ZeroMemory(Result, iKeyLen); ShowMessage('EncryptKEK is failed'); end; end; class function TTgColibDrm.GetKEK(sPath:string): TBytes; begin if TFile.Exists(sPath) then begin Result := DecryptKEK(sPath); end else begin //ShowMessage('KEK°¡ Á¸ÀçÇÏÁö ¾Ê½À´Ï´Ù'); Result := nil; end; end; class function TTgColibDrm.GetDEK(sPath:string; pKek:TBytes): TBytes; begin if TFile.Exists(sPath) then begin Result := DecryptDEKtoKEK(sPath, pKek); end else begin //ShowMessage('DEK°¡ Á¸ÀçÇÏÁö ¾Ê½À´Ï´Ù.'); Result := nil; end; end; class procedure TTgColibDrm.EncryptDEKtoKEK(const pDek, pKek: TBytes); var nResult, nOut: Integer; ci: TCIPHER_INFO; arrIV: array [0..COLIB_BLOCK_LEN-1] of Byte; arrEnc: TBytes; sOut, sIn: string; begin ZeroMemory(@ci, SizeOf(ci)); ZeroMemory(@arrIV, SizeOf(arrIV)); SetLength(arrEnc, Length(pDek)*2); // for var i := 0 to Length(pDek) - 1 do // sIn := sIn + Format('%.2x', [pDek[i]]); // ShowMessage('Æò¹® DEK (Hex) : ' + Inttostr(Length(pDek))+', '+sIn); COLibSetCipherInfo(@ci, COLIB_MODE_ARIA, BLOCK_CRYPTO_MODE_CBC, COLIB_PAD_TYPE_NO, @pKek[0], Length(pKek), @arrIV, SizeOf(arrIV), @pDek[0], Length(pDek), @arrEnc[0], @nOut); nResult := COLibEncrypt(@ci); if nResult <> COLIB_SUCCESS then begin ShowMessage('COLibEncrypt is failed: ' + IntToStr(nResult) + ', ' + COLibGetErrorStr(nResult)); Exit; end; // for var i := 0 to nOut - 1 do // sOut := sOut + Format('%.2x', [arrEnc[i]]); // ShowMessage('¾Ïȣȭ DEK (Hex) : ' + Inttostr(nOut)+', '+sOut); // ¾ÏȣȭµÈ DEK¸¦ ¹ÙÀ̳ʸ® ±×´ë·Î ÆÄÀÏ¿¡ ÀúÀå TFile.WriteAllBytes('dek.bin', Copy(arrEnc, 0, nOut)); end; class procedure TTgColibDrm.CreateDEK(const iKeylen: Integer; pKek: TBytes); var pDek: TBytes; sPath: string; begin sPath := 'dek.bin'; if TFile.Exists(sPath) then begin Exit; end; SetLength(pDek, iKeylen); COLibGetKey(@pDek[0], iKeylen); EncryptDEKtoKEK(pDek, pKek); end; class function TTgColibDrm.DecryptDEKtoKEK(sPath:string; pKek:TBytes): TBytes; var nResult, nOut: Integer; ci: TCIPHER_INFO; arrIV: array [0..COLIB_BLOCK_LEN-1] of Byte; arrDec, encDek: TBytes; sOut, sIn: string; begin ZeroMemory(@ci, SizeOf(ci)); ZeroMemory(@arrIV, SizeOf(arrIV)); // if not TFile.Exists(sPath) then // begin // ShowMessage('DEK Á¸ÀçÇÏÁö ¾ÊÀ½'); // Result := nil; // Exit; // end; encDek := TFile.ReadAllBytes(sPath); SetLength(arrDec, Length(encDek)); // ¾ÏÈ£¹® ±æÀ̸¸Å­ ¹öÆÛ È®º¸ // for var i := 0 to Length(encDek) - 1 do // sIn := sIn + Format('%.2x', [encDek[i]]); // ShowMessage('¾ÏȣȭµÈ DEK (Hex) : ' + Inttostr(Length(encDek))+', '+sIn); COLibSetCipherInfo(@ci, COLIB_MODE_ARIA, BLOCK_CRYPTO_MODE_CBC, COLIB_PAD_TYPE_NO, @pKek[0], Length(pKek), @arrIV, SizeOf(arrIV), @encDek[0], Length(encDek), @arrDec[0], @nOut); nResult := COLibDecrypt(@ci); if nResult <> COLIB_SUCCESS then begin ShowMessage('COLibDecrypt is failed: ' + IntToStr(nResult) + ', ' + COLibGetErrorStr(nResult)); Result := nil; Exit; end; // ½ÇÁ¦ º¹È£È­µÈ ±æÀ̸¸Å­ Àß¶ó³»±â //SetLength(arrDec, nOut); // for var i := 0 to nOut - 1 do // sOut := sOut + Format('%.2x', [arrDec[i]]); // ShowMessage('º¹È£È­ DEK (Hex) : ' + Inttostr(Length(arrDec))+', '+sOut); Result := arrDec; end; class function TTgColibDrm.CheckSig(aStream: TStream): Boolean; var sSig: AnsiString; pBuf: array[0..7] of AnsiChar; // SIG_CO_DRM ±æÀ̸¸Å­ begin Result := False; if aStream.Size < Length(SIG_CO_DRM) then Exit; aStream.Position := 0; aStream.Read(pBuf, Length(SIG_CO_DRM)); sSig := Copy(pBuf, 0, Length(SIG_CO_DRM)); Result := (sSig = SIG_CO_DRM); end; class function TTgColibDrm.IsEncrypt(sPath: string): Boolean; var fs: TFileStream; begin Result := False; if not FileExists(sPath) then Exit; try Guard(fs, TFileStream.Create(sPath, fmOpenRead or fmShareDenyNone)); Result := CheckSig(fs); except Result := False; end; end; class function TTgColibDrm.SetHead(aDstStream: TStream; pDek: TBytes): Boolean; var Head: TTgDrmHead; ci: TCIPHER_INFO; arrIV: array [0..COLIB_BLOCK_LEN-1] of Byte; pEncHead: TBytes; nOut, nResult: Integer; begin Result := False; ZeroMemory(@Head, SizeOf(Head)); Head.wVer := DRM_CO_VER; Head.dwCustomerCode := 13001; Head.dtEnc := Now; //Head.llEncDT := DelphiDtToJavaDt(Now); StrCopy(Head.sPgName, PUTF8Char(SIG_CO_DRM)); StrCopy(Head.sEmpNo, PUTF8Char(SIG_CO_DRM)); StrCopy(Head.sPartName, PUTF8Char(SIG_CO_DRM)); StrCopy(Head.sHostName, PUTF8Char(SIG_CO_DRM)); // Move(PAnsiChar(sEmpNo)^, Head.sEmpNo[0], Min(Length(sEmpNo), MAX_HEAD_STR)); // Move(PAnsiChar(sHostName)^, Head.sHostName[0], Min(Length(sHostName), MAX_HEAD_STR)); // Move(PAnsiChar(sPartName)^, Head.sPartName[0], Min(Length(sPartName), MAX_HEAD_STR)); // Move(PAnsiChar(sPgName)^, Head.sPgName[0], Min(Length(sPgName), MAX_HEAD_STR)); // Çì´õ ¾Ïȣȭ ZeroMemory(@ci, SizeOf(ci)); ZeroMemory(@arrIV, SizeOf(arrIV)); SetLength(pEncHead, SizeOf(Head) + 16); // ÆÐµù °í·Á COLibSetCipherInfo(@ci, COLIB_MODE_ARIA, BLOCK_CRYPTO_MODE_CBC, COLIB_PAD_TYPE_PKCS, @pDek[0], Length(pDek), @arrIV, SizeOf(arrIV), @Head, SizeOf(Head), @pEncHead[0], @nOut); nResult := COLibEncrypt(@ci); if nResult = COLIB_SUCCESS then begin // ¾ÏȣȭµÈ Çì´õ ±æÀÌ(Integer, 4byte)¸¦ ¸ÕÀú ¾²°í µ¥ÀÌÅ͸¦ ¾¸ (º¹È£È­ ½Ã ÇÊ¿ä) aDstStream.Write(nOut, SizeOf(Integer)); aDstStream.Write(pEncHead[0], nOut); Result := True; end; end; class function TTgColibDrm.DoEncrypt(sPath: string; pDek: TBytes): Boolean; const BUF_LEN_64K = 1024 * 64; var fsSrc, fsDst: TFileStream; arrBuf: array [0..BUF_LEN_64K-1] of Byte; arrEnc: array [0..BUF_LEN_64K + 31] of Byte; // ÆÐµù ´ëºñ ¿©À¯ °ø°£ dwRead: DWORD; nOut, nResult: Integer; pt: TCOLIB_PAD_TYPE; ci: TCIPHER_INFO; arrIV: array [0..COLIB_BLOCK_LEN-1] of Byte; sSig: AnsiString; begin sSig := SIG_CO_DRM; ZeroMemory(@arrIV, SizeOf(arrIV)); Guard(fsSrc, TFileStream.Create(sPath, fmOpenRead)); Guard(fsDst, TFileStream.Create(sPath + '.enc', fmCreate)); // ½Ã±×´Ïó (Æò¹®) fsDst.Write(sSig[1], Length(sSig)); if not SetHead(fsDst, pDek) then Exit; while fsSrc.Position < fsSrc.Size do begin dwRead := fsSrc.Read(arrBuf, BUF_LEN_64K); // ¸¶Áö¸· ºí·ÏÀÎ °æ¿ì ÆÐµù ó¸® if fsSrc.Position >= fsSrc.Size then pt := COLIB_PAD_TYPE_PKCS else pt := COLIB_PAD_TYPE_NO; ZeroMemory(@ci, SizeOf(ci)); COLibSetCipherInfo(@ci, COLIB_MODE_ARIA, BLOCK_CRYPTO_MODE_CBC, pt, @pDek[0], Length(pDek), @arrIV, SizeOf(arrIV), @arrBuf, dwRead, @arrEnc, @nOut); nResult := COLibEncrypt(@ci); if nResult <> COLIB_SUCCESS then begin ShowMessage('ÆÄÀÏ ¾Ïȣȭ ½ÇÆÐ: ' + IntToStr(nResult)); Result := False; Exit; end; fsDst.Write(arrEnc, nOut); Result := True; // CBC ¸ðµåÀ̹ǷΠ´ÙÀ½ ºí·ÏÀÇ IV´Â ÇöÀç ¾ÏÈ£¹®ÀÇ ¸¶Áö¸· ºí·ÏÀ̾î¾ß ÇÔ (¶óÀ̺귯¸® ³»ºÎ ó¸® È®ÀÎ ÇÊ¿ä) // ¸¸¾à ¶óÀ̺귯¸®°¡ IV¸¦ ÀÚµ¿ °»½ÅÇÏÁö ¾Ê´Â´Ù¸é ¿©±â¼­ ¸¶Áö¸· ¾ÏÈ£¹® ºí·ÏÀ» arrIV¿¡ º¹»çÇØ¾ß ÇÑ´Ù. end; end; class function TTgColibDrm.ExtractHead(aSrcStream: TStream; pDek: TBytes; var aHead: TTgDrmHead): Boolean; var nEncHeadLen, nOut, nResult: Integer; pEncBuf, pDecBuf: TBytes; ci: TCIPHER_INFO; arrIV: array [0..COLIB_BLOCK_LEN-1] of Byte; begin Result := False; // ¾ÏȣȭµÈ Çì´õ ±æÀÌ (4¹ÙÀÌÆ®) if aSrcStream.Read(nEncHeadLen, SizeOf(Integer)) <> SizeOf(Integer) then Exit; // ¾ÏȣȭµÈ Çì´õ º»¹® SetLength(pEncBuf, nEncHeadLen); if aSrcStream.Read(pEncBuf[0], nEncHeadLen) <> nEncHeadLen then Exit; // Çì´õ º¹È£È­ SetLength(pDecBuf, nEncHeadLen); ZeroMemory(@ci, SizeOf(ci)); ZeroMemory(@arrIV, SizeOf(arrIV)); COLibSetCipherInfo(@ci, COLIB_MODE_ARIA, BLOCK_CRYPTO_MODE_CBC, COLIB_PAD_TYPE_PKCS, @pDek[0], Length(pDek), @arrIV, SizeOf(arrIV), @pEncBuf[0], nEncHeadLen, @pDecBuf[0], @nOut); nResult := COLibDecrypt(@ci); if nResult = COLIB_SUCCESS then begin Move(pDecBuf[0], aHead, SizeOf(TTgDrmHead)); Result := True; end; end; class procedure TTgColibDrm.DoDecrypt(sPath: string; pDek:TBytes); const BUF_LEN_64K = 1024 * 64; var fsSrc, fsDst: TFileStream; arrBuf: array [0..BUF_LEN_64K - 1] of Byte; arrDec: array [0..BUF_LEN_64K + 31] of Byte; // ÆÐµù ´ëºñ ¿©À¯ °ø°£ dwRead: DWORD; nOut, nResult: Integer; pt: TCOLIB_PAD_TYPE; ci: TCIPHER_INFO; arrIV: array [0..COLIB_BLOCK_LEN - 1] of Byte; HeaderInfo: TTgDrmHead; // º¹È£È­µÈ Çì´õ Á¤º¸¸¦ ´ãÀ» º¯¼ö begin Guard(fsSrc, TFileStream.Create(sPath, fmOpenRead or fmShareDenyNone)); // ±âÁ¸ ÆÄÀÏ¸í¿¡¼­ .enc°¡ ÀÖ´Ù¸é Á¦°ÅÇϰí .dec¸¦ ºÙÀÓ Guard(fsDst, TFileStream.Create(ChangeFileExt(sPath, '') + '.dec', fmCreate)); try // ½Ã±×´Ïó À§Ä¡(8¹ÙÀÌÆ®) °Ç³Ê¶Ù±â fsSrc.Position := Length(SIG_CO_DRM); // Çì´õ ÃßÃâ ¹× º¹È£È­ (µ¥ÀÌÅÍ ½ÃÀÛ ÁöÁ¡À¸·Î ½ºÆ®¸² Æ÷ÀÎÅÍ À̵¿) if not ExtractHead(fsSrc, pDek, HeaderInfo) then begin ShowMessage('Çì´õ º¹È£È­¿¡ ½ÇÆÐÇß½À´Ï´Ù. Ű °ªÀÌ ¿Ã¹Ù¸£Áö ¾ÊÀ» ¼ö ÀÖ½À´Ï´Ù.'); Exit; end; // º¹È£È­µÈ Çì´õ Á¤º¸ Ȱ¿ë // ShowMessage('º¹È£È­ ´ë»ó »ç¹ø: ' + string(UTF8String(HeaderInfo.sEmpNo))); ZeroMemory(@arrIV, SizeOf(arrIV)); while fsSrc.Position < fsSrc.Size do begin dwRead := fsSrc.Read(arrBuf, BUF_LEN_64K); // ½ºÆ®¸²ÀÇ ¸¶Áö¸· ºí·ÏÀÎ °æ¿ì¿¡¸¸ PKCS ÆÐµù Àû¿ë if fsSrc.Position >= fsSrc.Size then pt := COLIB_PAD_TYPE_PKCS else pt := COLIB_PAD_TYPE_NO; ZeroMemory(@ci, SizeOf(ci)); COLibSetCipherInfo(@ci, COLIB_MODE_ARIA, BLOCK_CRYPTO_MODE_CBC, pt, @pDek[0], Length(pDek), @arrIV, SizeOf(arrIV), @arrBuf, dwRead, @arrDec, @nOut); nResult := COLibDecrypt(@ci); if nResult <> COLIB_SUCCESS then begin ShowMessage('µ¥ÀÌÅÍ º¹È£È­ Áß ¿À·ù ¹ß»ý: ' + IntToStr(nResult)); Exit; end; fsDst.Write(arrDec, nOut); end; //ShowMessage('º¹È£È­°¡ ¿Ï·áµÇ¾ú½À´Ï´Ù.'); except on E: Exception do ShowMessage('º¹È£È­ Áß ¿¹¿Ü ¹ß»ý: ' + E.Message); end; end; end.