using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using PrivaPub.Federation.Signing; using System.Globalization; using System.Security.Cryptography; using System.Text; using System.Text.Json.Nodes; namespace PrivaPub.Tests.Support { public sealed class RemoteActor { readonly RSA _key = RSA.Create(2048); public RemoteActor(Peer peer, string name, string origin = default) { Name = $"{name}{Guid.NewGuid():N}"[..20]; Id = $"{origin ?? peer.A}/users/{Name}"; peer.Serve($"/users/{Name}", Document().ToJsonString()); } public string Name { get; } public string Id { get; } public string KeyId => Id + "#main-key"; public JsonObject Document() => new() { ["id"] = Id, ["type"] = "Person", ["preferredUsername"] = Name, ["inbox"] = Id + "/inbox", ["followers"] = Id + "/followers", ["publicKey"] = new JsonObject { ["id"] = KeyId, ["owner"] = Id, ["publicKeyPem"] = _key.ExportSubjectPublicKeyInfoPem() } }; public HttpRequest Post(string host, string path, JsonNode activity) { var body = Encoding.UTF8.GetBytes(activity.ToJsonString()); var date = DateTimeOffset.UtcNow.ToString("r", CultureInfo.InvariantCulture); var digest = HttpSignatures.Digest(body); var signingString = $"(request-target): post {path}\nhost: {host}\ndate: {date}\ndigest: {digest}"; var signature = Convert.ToBase64String(_key.SignData(Encoding.UTF8.GetBytes(signingString), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)); var context = new DefaultHttpContext(); context.Request.Method = "POST"; context.Request.Host = new HostString(host); context.Request.Path = path; context.Features.Get().RawTarget = path; context.Request.Headers["Date"] = date; context.Request.Headers["Digest"] = digest; context.Request.Headers["Content-Type"] = "application/activity+json"; context.Request.Headers["Signature"] = $"keyId=\"{KeyId}\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest\",signature=\"{signature}\""; context.Request.Body = new MemoryStream(body); context.Request.ContentLength = body.Length; return context.Request; } } }