AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Netzwerke Delphi Delphi und Microsoft Authenticator
Thema durchsuchen
Ansicht
Themen-Optionen

Delphi und Microsoft Authenticator

Ein Thema von Dumpfbacke · begonnen am 2. Okt 2025 · letzter Beitrag vom 2. Okt 2025
Antwort Antwort
Dumpfbacke

Registriert seit: 10. Mär 2005
Ort: Mitten in Deutschland
342 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#1

Delphi und Microsoft Authenticator

  Alt 2. Okt 2025, 12:48
Hallo Leute,
ich wollte das ganze gerne in einen Anwendung von mir einbauen. Ich habe hierzu etwas Code gefunden. Als erstes erzeuge ich den QRCode. Diesen binde ich dann ins Handy ein nei dem Microsoft Authenticator und hier wird dann dalle 30 Sekunden ein neuer Key erzeugt. Mein Problem ist nun, das wenn ich den Key (die 6 Stellige Zahl) im Programm prüfe ist diese immer falsch. Hat jemadn ggf. einen Tip für mich ?

Delphi-Quellcode:
function TForm1.GenerateBase32Secret(ALength: Integer): string;
const
  Alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
var
  i: Integer;
begin
  Randomize;
  Result := '';
  for i := 1 to ALength do
    Result := Result + Alphabet[Random(Length(Alphabet)) + 1];
end;

function TForm1.BuildOtpAuthURI(const User, Issuer, Secret: string): string;
begin
  Result := Format(
    'otpauth://totp/%s:%s?secret=%s&issuer=%s&digits=6&period=30',
    [Issuer, User, Secret, Issuer]);
end;

procedure TForm1.GenerateQRCode(const Text: string; Bitmap: TBitmap);
var
  QRCode: TDelphiZXingQRCode;
  x, y: Integer;
  Scale: Integer;
begin
  QRCode := TDelphiZXingQRCode.Create;
  try
    QRCode.Data := Text;
    Scale := 5;
    Bitmap.SetSize(QRCode.Rows * Scale, QRCode.Columns * Scale);
    Bitmap.Canvas.Brush.Color := clWhite;
    Bitmap.Canvas.FillRect(Rect(0, 0, Bitmap.Width, Bitmap.Height));
    for y := 0 to QRCode.Rows - 1 do
      for x := 0 to QRCode.Columns - 1 do
        if QRCode.IsBlack[y, x] then
        begin
          Bitmap.Canvas.Brush.Color := clBlack;
          Bitmap.Canvas.FillRect(Rect(x*Scale, y*Scale,
                                      (x+1)*Scale, (y+1)*Scale));
        end;
  finally
    QRCode.Free;
  end;
end;

function TForm1.GenerateTOTP(const Secret: string; TimeStep: Int64): string;
var
  Counter: Int64;
  Msg, Hash: TBytes;
  Offset, Binary, Code: Integer;
begin
  Counter := DateTimeToUnix(Now) div TimeStep;
  SetLength(Msg, 8);
  PInt64(@Msg[0])^ := Swap(Counter); // BigEndian

  Hash := THashSHA1.GetHMACAsBytes(Msg, TEncoding.ASCII.GetBytes(Secret));

  Offset := Hash[High(Hash)] and $0F;
  Binary := ((Hash[Offset] and $7F) shl 24) or
            ((Hash[Offset+1] and $FF) shl 16) or
            ((Hash[Offset+2] and $FF) shl 8) or
            (Hash[Offset+3] and $FF);

  Code := Binary mod 1000000;
  Result := Format('%.6d', [Code]);
end;

procedure TForm1.btnGenerate1Click(Sender: TObject);
var
  URI: string;
  bmp: TBitmap;
begin
 FSecret := GenerateBase32Secret;
 URI := BuildOtpAuthURI('user@example.com', 'MeineApp', FSecret);

  bmp := TBitmap.Create;
  try
    GenerateQRCode(URI, bmp);
    imgQR.Picture.Assign(bmp);
  finally
    bmp.Free;
  end;

  lblResult.Caption := 'QR-Code erzeugt. Bitte mit Authenticator scannen.';
end;


procedure TForm1.btnVerifyClick(Sender: TObject);
var
  Code: string;
begin
 Code := GenerateTOTP(FSecret);
  if edtCode.Text = Code then
    lblResult.Caption := '✅ Code korrekt!'+Code
  else
    lblResult.Caption := '❌ Ungültiger Code'+Code;
end;
Danke schon einmal allen die ggf. den Fehler hier finden werden. Ich bin nicht dazu in der laage.

Tanja
Tanja
  Mit Zitat antworten Zitat
EKON 29
shebang

Registriert seit: 7. Feb 2020
156 Beiträge
 
Delphi 12 Athens
 
#2

AW: Delphi und Microsoft Authenticator

  Alt 2. Okt 2025, 13:16
Kann es sein, dass die Uhrzeit hier das Problem ist? Dein Rechner wird ja wahrscheinlich MESZ, also UTC+2 liefern. Versuch es mal mit:
DateTimeToUnix(TTimeZone.Local.ToUniversalTime(Now, FALSE))
  Mit Zitat antworten Zitat
Dumpfbacke

Registriert seit: 10. Mär 2005
Ort: Mitten in Deutschland
342 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#3

AW: Delphi und Microsoft Authenticator

  Alt 2. Okt 2025, 14:30
Kann es sein, dass die Uhrzeit hier das Problem ist? Dein Rechner wird ja wahrscheinlich MESZ, also UTC+2 liefern. Versuch es mal mit:
DateTimeToUnix(TTimeZone.Local.ToUniversalTime(Now, FALSE))
Das geht leider auch nicht.
Tanja
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
10.150 Beiträge
 
Delphi 12 Athens
 
#4

AW: Delphi und Microsoft Authenticator

  Alt 2. Okt 2025, 22:40
Der zweite Parameter muss zur Umwandlung der Zeit True sein. Aber da waren noch ein paar andere Punkte drin. Unter anderem kann Swap nur Integers und keine UInt64 Werte. Probiere es mal so:
Delphi-Quellcode:
uses
  System.DateUtils, System.Hash, System.Character, System.NetEncoding, System.Types, System.StrUtils;

function Base32Decode(const ABase32: string): TBytes;
const
  Alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
var
  clean: string;
  ch: Char;
  value, bits: Integer;
  outIdx, idx: Integer;
  buf: TBytes;
begin
  clean := '';
  for ch in ABase32 do
    if (not ch.IsWhiteSpace) and (ch <> '-') and (ch <> '=') then
      clean := clean + ch.ToUpper;

  SetLength(buf, (Length(clean) * 5) div 8 + 1);
  value := 0; bits := 0; outIdx := 0;

  for ch in clean do
  begin
    idx := Pos(ch, Alphabet);
    if idx = 0 then
      raise Exception.CreateFmt('Ungültiges Base32-Zeichen: %s', [ch]);

    value := (value shl 5) or (idx - 1);
    Inc(bits, 5);

    while bits >= 8 do
    begin
      Dec(bits, 8);
      buf[outIdx] := (value shr bits) and $FF;
      Inc(outIdx);
    end;
  end;

  SetLength(buf, outIdx);
  Result := buf;
end;

function BuildOtpAuthURI(const User, Issuer, SecretBase32: string): string;
var
  lbl: string;
begin
  lbl := TNetEncoding.URL.Encode(Format('%s:%s', [Issuer, User]));
  Result := Format('otpauth://totp/%s?secret=%s&issuer=%s&digits=6&period=30',
                   [lbl, SecretBase32, TNetEncoding.URL.Encode(Issuer)]);
end;

function TOTPFromCounter(const SecretBase32: string; Counter: UInt64): string;
var
  SecretBytes, Msg, Hash: TBytes;
  i: Integer;
  tmp: UInt64;
  Offset: Integer;
  Binary: UInt32;
  Code: UInt32;
begin
  SecretBytes := Base32Decode(SecretBase32);

  SetLength(Msg, 8);
  tmp := Counter;
  for i := 0 to 7 do
  begin
    Msg[7 - i] := Byte(tmp and $FF);
    tmp := tmp shr 8;
  end;

  Hash := THashSHA1.GetHMACAsBytes(Msg, SecretBytes);

  Offset := Hash[High(Hash)] and $0F;
  Binary := ((UInt32(Hash[Offset]) and $7F) shl 24) or
            ((UInt32(Hash[Offset+1]) and $FF) shl 16) or
            ((UInt32(Hash[Offset+2]) and $FF) shl 8) or
            (UInt32(Hash[Offset+3]) and $FF);

  Code := Binary mod 1000000;
  Result := Format('%.6d', [Code]);
end;

function ValidateTOTP(const SecretBase32, UserCode: string; TimeStep: Int64 = 30; SkewSteps: Integer = 1): Boolean;
var
  unixSeconds: Int64;
  baseCounter: Int64;
  i: Integer;
begin
  unixSeconds := DateTimeToUnix(TTimeZone.Local.ToUniversalTime(Now), True);
  baseCounter := unixSeconds div TimeStep;

  for i := -SkewSteps to SkewSteps do
    if TOTPFromCounter(SecretBase32, UInt64(baseCounter + i)) = UserCode then
      Exit(True);

  Result := False;
end;

function GenerateBase32Secret(ALength: Integer): string;
const
  Alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
var
  i: Integer;
begin
  Randomize;
  Result := '';
  for i := 1 to ALength do
    Result := Result + Alphabet[Random(Length(Alphabet)) + 1];
end;

procedure GenerateQRCode(const Text: string; Bitmap: TBitmap);
var
  QRCode: TDelphiZXingQRCode;
  x, y: Integer;
  Scale: Integer;
begin
  QRCode := TDelphiZXingQRCode.Create;
  try
    QRCode.Data := Text;
    Scale := 5;
    Bitmap.SetSize(QRCode.Rows * Scale, QRCode.Columns * Scale);
    Bitmap.Canvas.Brush.Color := clWhite;
    Bitmap.Canvas.FillRect(Rect(0, 0, Bitmap.Width, Bitmap.Height));
    for y := 0 to QRCode.Rows - 1 do
      for x := 0 to QRCode.Columns - 1 do
        if QRCode.IsBlack[y, x] then
        begin
          Bitmap.Canvas.Brush.Color := clBlack;
          Bitmap.Canvas.FillRect(Rect(x*Scale, y*Scale,
                                      (x+1)*Scale, (y+1)*Scale));
        end;
  finally
    QRCode.Free;
  end;
end;

procedure TForm1.btnGenerate1Click(Sender: TObject);
var
  URI: string;
  bmp: TBitmap;
begin
 FSecret := GenerateBase32Secret(16);
 URI := BuildOtpAuthURI('user@example.com', 'MeineApp', FSecret);

  bmp := TBitmap.Create;
  try
    GenerateQRCode(URI, bmp);
    imgQR.Picture.Assign(bmp);
  finally
    bmp.Free;
  end;

  lblResult.Caption := 'QR-Code erzeugt. Bitte mit Authenticator scannen.';
end;

procedure TForm1.btnVerifyClick(Sender: TObject);
begin
  if ValidateTOTP(FSecret, edtCode.Text) then
    lblResult.Caption := '✅ Code korrekt!'
  else
    lblResult.Caption := '❌ Ungültiger Code';
end;
Wenn du SkewSteps auf 0 setzt, wird nur der aktuelle 30 Sekunden Abschnitt beachtet, mit 1 auch die benachbarten.
Sebastian Jänicke
AppCentral
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:29 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz