Reifen Späth – Anmeldecode
Hallo {username},
Ihr Einmal-Code für die Anmeldung lautet:
{code}
Dieser Code ist 5 Minuten gültig. Falls Sie diese Anmeldung nicht angefordert haben, ignorieren Sie diese E-Mail.
"""
};
message.To.Add(toEmail);
await client.SendMailAsync(message);
_logger.LogInformation("2FA email code sent to {Email} for user {Username}", toEmail, username);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to send 2FA email to {Email}", toEmail);
return false;
}
}
public string GenerateQrCodeDataUrl(string otpAuthUri)
{
using var qrGenerator = new QRCodeGenerator();
using var qrCodeData = qrGenerator.CreateQrCode(otpAuthUri, QRCodeGenerator.ECCLevel.M);
using var qrCode = new PngByteQRCode(qrCodeData);
var pngBytes = qrCode.GetGraphic(8, new byte[] { 30, 30, 32 }, new byte[] { 255, 255, 255 });
return $"data:image/png;base64,{Convert.ToBase64String(pngBytes)}";
}
#region TOTP Internals
private static string ComputeTotp(byte[] secret, long timeStep)
{
var counterBytes = BitConverter.GetBytes(timeStep);
if (BitConverter.IsLittleEndian)
Array.Reverse(counterBytes);
var hash = HMACSHA1.HashData(secret, counterBytes);
var offset = hash[^1] & 0x0F;
var code = ((hash[offset] & 0x7F) << 24) |
((hash[offset + 1] & 0xFF) << 16) |
((hash[offset + 2] & 0xFF) << 8) |
(hash[offset + 3] & 0xFF);
return (code % 1_000_000).ToString("D6");
}
#endregion
#region Base32
private static string Base32Encode(byte[] data)
{
var sb = new StringBuilder((data.Length * 8 + 4) / 5);
int buffer = data[0];
int next = 1;
int bitsLeft = 8;
while (bitsLeft > 0 || next < data.Length)
{
if (bitsLeft < 5)
{
if (next < data.Length)
{
buffer <<= 8;
buffer |= data[next++] & 0xFF;
bitsLeft += 8;
}
else
{
int pad = 5 - bitsLeft;
buffer <<= pad;
bitsLeft += pad;
}
}
int index = 0x1F & (buffer >> (bitsLeft - 5));
bitsLeft -= 5;
sb.Append(Base32Chars[index]);
}
return sb.ToString();
}
private static byte[] Base32Decode(string base32)
{
var cleanInput = base32.TrimEnd('=').ToUpperInvariant();
var output = new byte[cleanInput.Length * 5 / 8];
int buffer = 0;
int bitsLeft = 0;
int outputIndex = 0;
foreach (var c in cleanInput)
{
var val = Array.IndexOf(Base32Chars, c);
if (val < 0) continue;
buffer <<= 5;
buffer |= val & 0x1F;
bitsLeft += 5;
if (bitsLeft >= 8)
{
output[outputIndex++] = (byte)(buffer >> (bitsLeft - 8));
bitsLeft -= 8;
}
}
return output;
}
#endregion
}