Files
jdescopingtool/NEW/src/JdeScoping.Client/Services/CryptoService.cs
T
Joseph Doherty 30153dcbf8 feat(client): add CryptoService for login encryption
Implements ICryptoService for encrypting login credentials using RSA-OAEP.
Uses JavaScript interop with browser's native SubtleCrypto API instead of
Blazor.SubtleCrypto package (which only supports AES-GCM, not RSA-OAEP).

- ICryptoService interface in JdeScoping.Client.Services namespace
- CryptoService fetches server's public key once, caches it
- interop.js rsaEncrypt function for RSA-OAEP encryption via Web Crypto API
2026-01-03 08:33:10 -05:00

64 lines
1.9 KiB
C#

using System.Net.Http.Json;
using System.Text.Json;
using JdeScoping.Core.Models.Auth;
using Microsoft.JSInterop;
namespace JdeScoping.Client.Services;
/// <summary>
/// Encrypts login credentials using Web Crypto API via JavaScript interop.
/// Uses RSA-OAEP with SHA-256 to encrypt credentials before transmission.
/// </summary>
public class CryptoService : ICryptoService
{
private readonly HttpClient _httpClient;
private readonly IJSRuntime _jsRuntime;
private string? _cachedPublicKeyPem;
private readonly SemaphoreSlim _keyLock = new(1, 1);
public CryptoService(HttpClient httpClient, IJSRuntime jsRuntime)
{
_httpClient = httpClient;
_jsRuntime = jsRuntime;
}
/// <inheritdoc />
public async Task<string> EncryptLoginAsync(LoginModel model)
{
var publicKeyPem = await GetOrFetchPublicKeyAsync();
var json = JsonSerializer.Serialize(model);
// Use JavaScript interop to encrypt with RSA-OAEP via browser's SubtleCrypto API
var encryptedBase64 = await _jsRuntime.InvokeAsync<string>(
"jdeScopingInterop.rsaEncrypt",
publicKeyPem,
json);
return encryptedBase64;
}
private async Task<string> GetOrFetchPublicKeyAsync()
{
if (_cachedPublicKeyPem is not null)
return _cachedPublicKeyPem;
await _keyLock.WaitAsync();
try
{
if (_cachedPublicKeyPem is not null)
return _cachedPublicKeyPem;
var response = await _httpClient.GetFromJsonAsync<PublicKeyResponse>("api/auth/public-key")
?? throw new InvalidOperationException("Failed to fetch public key from server");
_cachedPublicKeyPem = response.PublicKeyPem;
return _cachedPublicKeyPem;
}
finally
{
_keyLock.Release();
}
}
}