unit UserAuthentification; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Winapi.ShellAPI, superobject, IdHTTP, IdSSLOpenSSL, System.Math, System.IOUtils, System.RegularExpressions, Tocsg.Safe, Tocsg.COLib, Tocsg.COLib.Encrypt; const CODE_AUTH_OK = 100; type // ·Î±×ÀÎ ¼º°ø ½Ã È£ÃâµÉ À̺¥Æ® ŸÀÔ Á¤ÀÇ TNotifyLoginSuccess = procedure(Sender: TObject; ResultCode: Integer) of object; TAuthForm = class(TForm) Label1, Label2: TLabel; EditID, EditPW: TEdit; ButtonLogin: TButton; procedure FormCreate(Sender: TObject); procedure FromClose(Sender: TObject; var Action: TCloseAction); procedure ButtonLoginClick(Sender: TObject); private { Private declarations } iFailCount: Integer; // ·Î±×ÀÎ ½ÇÆÐ Ƚ¼ö BlockStartTime: TDateTime; // Â÷´Ü ½ÃÀÛ ½Ã°£ FOnSuccess: TNotifyLoginSuccess; // À̺¥Æ®¸¦ ´ãÀ» º¯¼ö public { Public declarations } sAgentId, sDestIPort: string; property OnSuccess: TNotifyLoginSuccess read FOnSuccess write FOnSuccess; end; var AuthForm: TAuthForm; implementation {$R *.dfm} procedure TAuthForm.FormCreate(Sender: TObject); begin Self.Position := poScreenCenter; Self.BorderStyle := bsSingle; Self.FormStyle := fsStayOnTop; end; procedure TAuthForm.FromClose(Sender: TObject; var Action: TCloseAction); begin // ÆûÀÌ ´ÝÈú ¶§ ¸Þ¸ð¸®¿¡¼­ ÀÚµ¿À¸·Î Á¦°ÅµÇµµ·Ï ¼³Á¤ Action := caFree; end; function IsValidInput(const AText: string): Boolean; const // ¿µ¹®, ¼ýÀÚ, Ư¼ö¹®ÀÚ¸¸ Çã¿ë ALLOWED_PATTERN = '^[a-zA-Z0-9!@#$%^&*()_+\-=\[\]{};'':"\\|,.<>\/?]+$'; begin // ºó °ª üũ ÈÄ ÆÐÅÏ °Ë»ç Result := (AText <> '') and TRegEx.IsMatch(AText, ALLOWED_PATTERN); end; function GetPostData(sDestIPort, sUrl, sData: String; sReqType: String = ''): String; var HTTP: TIdHTTP; SSL: TIdSSLIOHandlerSocketOpenSSL; ss: TStringStream; begin Result := ''; try if sUrl = '' then exit; if sUrl[1] = '/' then Delete(sUrl, 1, 1); Guard(SSL, TIdSSLIOHandlerSocketOpenSSL.Create(nil)); SSL.SSLOptions.Method := sslvSSLv23; SSL.SSLOptions.SSLVersions := [sslvTLSv1_2, sslvTLSv1_1, sslvTLSv1]; Guard(HTTP, TIdHTTP.Create(nil)); HTTP.IOHandler := SSL; with HTTP do begin HandleRedirects := true; Request.Clear; Request.UserAgent := 'Mozilla/5.0'; Request.ContentType := 'application/xml'; Request.AcceptCharSet := 'UTF-8'; // Request.Connection := 'Keep-Alive'; // Request.CustomHeaders.Values['Keep-Alive'] := 'timeout=300, max=100'; Request.Connection := 'close'; HTTPOptions := HTTPOptions - [hoKeepOrigProtocol]; if sReqType <> '' then Request.CustomHeaders.Values['requestType'] := sReqType; HTTPOptions := HTTP.HTTPOptions + [hoForceEncodeParams]; ConnectTimeout := 5000; ReadTimeout := 5000; end; Guard(ss, TStringStream.Create(sData, TEncoding.UTF8)); Result := HTTP.Post(sDestIPort + sUrl, ss); except on E: EIdHTTPProtocolException do begin Result := E.ErrorMessage; end; on E: Exception do begin Result := 'Error: ' + E.Message; end; end; end; procedure TAuthForm.ButtonLoginClick(Sender: TObject); var O: ISuperObject; pSalt, pKek, pDek : TBytes; sResult, sSalt: String; nResult: integer; CurrentTime: TDateTime; begin if iFailCount >= 5 then begin CurrentTime := Now; // Â÷´Ü ½ÃÀÛ ½Ã°£À¸·ÎºÎÅÍ 5ºÐÀÌ Áö³µ´ÂÁö È®ÀÎ if (CurrentTime - BlockStartTime) < (5 / (24 * 60)) then begin MessageBox(0,'·Î±×ÀÎ 5ȸ ½ÇÆÐ·Î ÀÎÇØ 5ºÐ°£ ·Î±×ÀÎÀÌ Â÷´ÜµË´Ï´Ù', 'BSOne Login', MB_ICONINFORMATION or MB_TOPMOST or MB_SETFOREGROUND); Exit; end else begin // 5ºÐÀÌ Áö³µÀ¸¸é Ä«¿îÆ® ÃʱâÈ­ iFailCount := 0; end; end; if EditID.Text = '' then begin MessageBox(0,'ID¸¦ ÀÔ·ÂÇϼ¼¿ä', 'BSOne Login', MB_ICONINFORMATION or MB_TOPMOST or MB_SETFOREGROUND); EditID.SetFocus; Exit; end; if not IsValidInput(EditID.Text) then begin MessageBox(0,'ID¿¡´Â ¿µ¹®, ¼ýÀÚ, Ư¼ö±âÈ£¸¦ Á¦¿ÜÇÏ°í µé¾î°¥ ¼ö ¾ø½À´Ï´Ù', 'BSOne Login', MB_ICONINFORMATION or MB_TOPMOST or MB_SETFOREGROUND); Exit; end; if EditPW.Text = '' then begin MessageBox(0,'ÆÐ½º¿öµå¸¦ ÀÔ·ÂÇϼ¼¿ä', 'BSOne Login', MB_ICONINFORMATION or MB_TOPMOST or MB_SETFOREGROUND); EditPW.SetFocus; Exit; end; if not IsValidInput(EditPW.Text) then begin MessageBox(0,'ÆÐ½º¿öµå¿¡´Â ¿µ¹®, ¼ýÀÚ, Ư¼ö±âÈ£¸¦ Á¦¿ÜÇÏ°í µé¾î°¥ ¼ö ¾ø½À´Ï´Ù', 'BSOne Login', MB_ICONINFORMATION or MB_TOPMOST or MB_SETFOREGROUND); Exit; end; try O := SO; // O.S['empId'] := '0505'; // O.S['pw'] := 'Q!w2e3r4t5'; // O.S['agentId'] := '25111809085600FF96644E47'; O.S['empId'] := EditID.Text; O.S['pw'] := EditPW.Text; O.S['agentId'] := sAgentId; sResult := GetPostData(sDestIPort, 'aapi/auth/refresh-token/issue', O.AsJSon); //showmessage(sResult); if sResult <> '' then begin // ÀÏ¹Ý ¿¹¿Ü(Exception) if sResult.Contains('Error') then begin //_Trace('Error .. GetPostData() .. Msg="%s"', [Result]); MessageBox(0, LPCWSTR(sResult), 'BSOne Login', MB_ICONINFORMATION or MB_TOPMOST or MB_SETFOREGROUND); exit; end // ÇÁ·ÎÅäÄÝ ¿¹¿Ü(EIdHTTPProtocolException) else if sResult.Contains('err') then begin O := SO(sResult); sResult := O.S['err']; MessageBox(0, LPCWSTR('Error: ' + sResult), 'BSOne Login', MB_ICONINFORMATION or MB_TOPMOST or MB_SETFOREGROUND); Inc(iFailCount); if iFailCount >= 5 then begin BlockStartTime := Now; // ÇöÀç ½Ã°£ ±â·Ï MessageBox(0, 'ÀÎÁõ ½ÇÆÐ 5ȸ ¿¬¼ÓÀ¸·Î 5ºÐ°£ ·Î±×ÀÎÀÌ Â÷´ÜµË´Ï´Ù.', 'BSOne Login', MB_ICONINFORMATION or MB_TOPMOST or MB_SETFOREGROUND); exit; end else begin MessageBox(0, PChar(format('¾ÆÀ̵ð ¶Ç´Â ºñ¹Ð¹øÈ£°¡ ÀÏÄ¡ÇÏÁö ¾Ê½À´Ï´Ù' + sLineBreak + '(ÇöÀç %dȸ ½ÇÆÐ / 5ȸ ¿¬¼Ó½ÇÆÐ ½Ã Â÷´Ü)', [iFailCount])), 'BSOne Login', MB_ICONINFORMATION or MB_TOPMOST or MB_SETFOREGROUND); end; exit; end else if sResult.Contains('passwordChange') then begin O := SO(sResult); sResult := O.S['passwordChangeRequired']; if sResult = 'true' then begin var sUrl := O.S['passwordChangeUrl']; if sUrl.StartsWith('/') then sUrl := sUrl.Substring(1); var sFullUrl := sDestIPort + sUrl; ShellExecute(0, 'open', PChar(sFullUrl), nil, nil, SW_SHOWNORMAL); //sResult := GetPostData(sDestIPort, sUrl, O.AsJSon); end; //MessageBox(0, LPCWSTR(sResult), 'BSOne Login', MB_ICONINFORMATION or MB_TOPMOST or MB_SETFOREGROUND); exit; end else begin O := SO(sResult); //O.S['refreshToken'] iFailCount := 0; //ShowMessage('ÀÎÁõ ¿Ï·á'); end; end else begin end; except on E: Exception do begin ShowMessage('¿À·ù ¹ß»ý: Fail .. GetPostData()' + sLineBreak + E.Message); exit; end; end; // salt »ý¼º pSalt := TTgColibDrm.CreateSalt(COLIB_128BIT_KEY_LEN); if pSalt = nil then begin ShowMessage('CreateSalt is failed'); exit; end; for var i := 0 to COLIB_128BIT_KEY_LEN - 1 do sSalt := sSalt + Format('%.2x', [pSalt[i]]); //ShowMessage('Salt : ' + sSalt); // KEK »ý¼º ¹× ¾Ïȣȭ(»ç¿ëÀÚ ·Î±×ÀÎ °èÁ¤ ¾ÏÈ£) pKek := TTgColibDrm.CreateKEK(EditPW.Text, sSalt, 1000, SHA256_DIGEST_SIZE); if pKek = nil then begin ShowMessage('CreateKEK is failed'); exit; end; // KEK º¹È£È­ µð¹ö±× // var pTemp := TTgColibDrm.GetKEK('kek.bin'); // if (Length(pKek) = Length(pTemp)) and // CompareMem(@pKek[0], @pTemp[0], Length(pKek)) then // begin // ShowMessage('KEK º¹È£È­ ¼º°ø'); // end; // DEK »ý¼º ¹× ¾Ïȣȭ(KEK) TTgColibDrm.CreateDEK(SHA256_DIGEST_SIZE, pKek); if Assigned(FOnSuccess) then FOnSuccess(Self, CODE_AUTH_OK); // ¸ÞÀÎ ¼­ºñ½º¿¡ ¾Ë¸² ¹ß¼Û Close; end; end.