127 lines
4.7 KiB
C#
127 lines
4.7 KiB
C#
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using Org.BouncyCastle.Crypto.Generators;
|
|
using Org.BouncyCastle.Crypto.Parameters;
|
|
using Org.BouncyCastle.Crypto.Signers;
|
|
using Org.BouncyCastle.Security;
|
|
using TheCr3w.Shared.Models;
|
|
|
|
namespace TheCr3w.Shared.Crypto;
|
|
|
|
/// <summary>
|
|
/// Serwis kryptograficzny odpowiedzialny za generowanie par kluczy, podpisywanie
|
|
/// oraz weryfikację podpisów cyfrowych Ed25519 (RFC 8032) dla metadanych wydań i manifestów wdrożeniowych.
|
|
/// </summary>
|
|
public static class Ed25519Service
|
|
{
|
|
/// <summary>
|
|
/// Generuje nową parę kluczy Ed25519.
|
|
/// Zwraca klucz prywatny (64-bajtowy w hex) oraz klucz publiczny (32-bajtowy w hex).
|
|
/// </summary>
|
|
public static (string PrivateKeyHex, string PublicKeyHex) GenerateKeyPair()
|
|
{
|
|
var random = new SecureRandom();
|
|
var keyGen = new Ed25519KeyPairGenerator();
|
|
keyGen.Init(new Ed25519KeyGenerationParameters(random));
|
|
var keyPair = keyGen.GenerateKeyPair();
|
|
|
|
var privKey = (Ed25519PrivateKeyParameters)keyPair.Private;
|
|
var pubKey = (Ed25519PublicKeyParameters)keyPair.Public;
|
|
|
|
var privBytes = privKey.GetEncoded();
|
|
var pubBytes = pubKey.GetEncoded();
|
|
|
|
return (Convert.ToHexString(privBytes).ToLowerInvariant(), Convert.ToHexString(pubBytes).ToLowerInvariant());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Podpisuje dane binarne kluczem prywatnym Ed25519.
|
|
/// Zwraca 64-bajtowy podpis w postaci małych liter hex (128 znaków).
|
|
/// </summary>
|
|
public static string Sign(byte[] data, string privateKeyHex)
|
|
{
|
|
var privBytes = Convert.FromHexString(privateKeyHex);
|
|
var privKey = new Ed25519PrivateKeyParameters(privBytes, 0);
|
|
|
|
var signer = new Ed25519Signer();
|
|
signer.Init(true, privKey);
|
|
signer.BlockUpdate(data, 0, data.Length);
|
|
var signature = signer.GenerateSignature();
|
|
|
|
return Convert.ToHexString(signature).ToLowerInvariant();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Weryfikuje podpis Ed25519 dla przekazanych danych binarnych i klucza publicznego.
|
|
/// </summary>
|
|
public static bool Verify(byte[] data, string signatureHex, string publicKeyHex)
|
|
{
|
|
try
|
|
{
|
|
var pubBytes = Convert.FromHexString(publicKeyHex);
|
|
var sigBytes = Convert.FromHexString(signatureHex);
|
|
var pubKey = new Ed25519PublicKeyParameters(pubBytes, 0);
|
|
|
|
var verifier = new Ed25519Signer();
|
|
verifier.Init(false, pubKey);
|
|
verifier.BlockUpdate(data, 0, data.Length);
|
|
return verifier.VerifySignature(sigBytes);
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tworzy kanoniczny ciąg bajtów dla metadanych wydania aplikacji natywnej.
|
|
/// Kolejność właściwości jest ściśle zdefiniowana i niezmienna, a wartości są znormalizowane.
|
|
/// </summary>
|
|
public static byte[] GetCanonicalBytes(ReleaseMetadata metadata)
|
|
{
|
|
// Kanoniczny format JSON o ścisłej kolejności kluczy bez zbędnych spacji (invariant UTF-8)
|
|
var canonicalDto = new
|
|
{
|
|
app_id = metadata.AppId,
|
|
architecture = metadata.Architecture.ToLowerInvariant(),
|
|
channel = metadata.Channel.ToLowerInvariant(),
|
|
file_size = metadata.FileSize,
|
|
mandatory = metadata.Mandatory,
|
|
minimum_supported_version = metadata.MinimumSupportedVersion,
|
|
platform = metadata.Platform.ToLowerInvariant(),
|
|
published_at = metadata.PublishedAt.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"),
|
|
release_id = metadata.ReleaseId.ToString().ToLowerInvariant(),
|
|
sha256 = metadata.Sha256.ToLowerInvariant(),
|
|
version = metadata.Version,
|
|
version_code = metadata.VersionCode
|
|
};
|
|
|
|
var json = JsonSerializer.Serialize(canonicalDto, new JsonSerializerOptions
|
|
{
|
|
WriteIndented = false
|
|
});
|
|
|
|
return Encoding.UTF8.GetBytes(json);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wylicza skrót SHA256 dla strumienia danych binarnych.
|
|
/// </summary>
|
|
public static async Task<string> ComputeSha256Async(Stream stream, CancellationToken cancellationToken = default)
|
|
{
|
|
using var sha256 = SHA256.Create();
|
|
var hash = await sha256.ComputeHashAsync(stream, cancellationToken);
|
|
return Convert.ToHexString(hash).ToLowerInvariant();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wylicza skrót SHA256 z tablicy bajtów.
|
|
/// </summary>
|
|
public static string ComputeSha256(byte[] data)
|
|
{
|
|
using var sha256 = SHA256.Create();
|
|
var hash = sha256.ComputeHash(data);
|
|
return Convert.ToHexString(hash).ToLowerInvariant();
|
|
}
|
|
}
|