private async Task <SignatureValidationResult> ValidateSignature(string actor, string rawSig, string method, string path, string queryString, Dictionary <string, string> requestHeaders, string body) { //Check Date Validity var date = requestHeaders["date"]; var d = DateTime.Parse(date).ToUniversalTime(); var now = DateTime.UtcNow; var delta = Math.Abs((d - now).TotalSeconds); if (delta > 30) { return new SignatureValidationResult { SignatureIsValidated = false } } ; //Check Digest var digest = requestHeaders["digest"]; var digestHash = digest.Split(new [] { "SHA-256=" }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault(); var calculatedDigestHash = _cryptoService.ComputeSha256Hash(body); if (digestHash != calculatedDigestHash) { return new SignatureValidationResult { SignatureIsValidated = false } } ; //Check Signature var signatures = rawSig.Split(','); var signature_header = new Dictionary <string, string>(); foreach (var signature in signatures) { var m = HeaderRegexes.HeaderSignature.Match(signature); signature_header.Add(m.Groups[1].ToString(), m.Groups[2].ToString()); } var key_id = signature_header["keyId"]; var headers = signature_header["headers"]; var algorithm = signature_header["algorithm"]; var sig = Convert.FromBase64String(signature_header["signature"]); // Retrieve User var remoteUser = await _activityPubService.GetUser(actor); // Prepare Key data var toDecode = remoteUser.publicKey.publicKeyPem.Trim().Remove(0, remoteUser.publicKey.publicKeyPem.IndexOf('\n')); toDecode = toDecode.Remove(toDecode.LastIndexOf('\n')).Replace("\n", ""); var signKey = ASN1.ToRSA(Convert.FromBase64String(toDecode)); var toSign = new StringBuilder(); foreach (var headerKey in headers.Split(' ')) { if (headerKey == "(request-target)") { toSign.Append($"(request-target): {method.ToLower()} {path}{queryString}\n"); } else { toSign.Append($"{headerKey}: {string.Join(", ", requestHeaders[headerKey])}\n"); } } toSign.Remove(toSign.Length - 1, 1); // Import key var key = new RSACryptoServiceProvider(); var rsaKeyInfo = key.ExportParameters(false); rsaKeyInfo.Modulus = Convert.FromBase64String(toDecode); key.ImportParameters(rsaKeyInfo); // Trust and Verify var result = signKey.VerifyData(Encoding.UTF8.GetBytes(toSign.ToString()), sig, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); return(new SignatureValidationResult() { SignatureIsValidated = result, User = remoteUser }); } }
private async Task <SignatureValidationResult> ValidateSignature(string actor, string rawSig, string method, string path, string queryString, Dictionary <string, string> requestHeaders, string body) { //Check Date Validity var date = requestHeaders["date"]; var d = DateTime.Parse(date).ToUniversalTime(); var now = DateTime.UtcNow; var delta = Math.Abs((d - now).TotalSeconds); if (delta > 30) { return new SignatureValidationResult { SignatureIsValidated = false } } ; //Check Digest var digest = requestHeaders["digest"]; var digestHash = digest.Split(new [] { "SHA-256=" }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault(); var calculatedDigestHash = _cryptoService.ComputeSha256Hash(body); if (digestHash != calculatedDigestHash) { return new SignatureValidationResult { SignatureIsValidated = false } } ; //Check Signature var signatures = rawSig.Split(','); var signature_header = new Dictionary <string, string>(); foreach (var signature in signatures) { var splitSig = signature.Replace("\"", string.Empty).Split('='); signature_header.Add(splitSig[0], splitSig[1]); } signature_header["signature"] = signature_header["signature"] + "=="; var key_id = signature_header["keyId"]; var headers = signature_header["headers"]; var algorithm = signature_header["algorithm"]; var sig = Convert.FromBase64String(signature_header["signature"]); var remoteUser = await _activityPubService.GetUser(actor); var toDecode = remoteUser.publicKey.publicKeyPem.Trim().Remove(0, remoteUser.publicKey.publicKeyPem.IndexOf('\n')); toDecode = toDecode.Remove(toDecode.LastIndexOf('\n')).Replace("\n", ""); var signKey = ASN1.ToRSA(Convert.FromBase64String(toDecode)); var toSign = new StringBuilder(); //var comparisonString = headers.Split(' ').Select(signed_header_name => //{ // if (signed_header_name == "(request-target)") // return "(request-target): post /inbox"; // else // return $"{signed_header_name}: {r.Headers[signed_header_name.ToUpperInvariant()]}"; //}); foreach (var headerKey in headers.Split(' ')) { if (headerKey == "(request-target)") { toSign.Append($"(request-target): {method.ToLower()} {path}{queryString}\n"); } else { toSign.Append($"{headerKey}: {string.Join(", ", requestHeaders[headerKey])}\n"); } } toSign.Remove(toSign.Length - 1, 1); //var signKey = ASN1.ToRSA(Convert.FromBase64String(toDecode)); //new RSACryptoServiceProvider(keyId.publicKey.publicKeyPem); //Create a new instance of RSACryptoServiceProvider. RSACryptoServiceProvider key = new RSACryptoServiceProvider(); //Get an instance of RSAParameters from ExportParameters function. RSAParameters RSAKeyInfo = key.ExportParameters(false); //Set RSAKeyInfo to the public key values. RSAKeyInfo.Modulus = Convert.FromBase64String(toDecode); key.ImportParameters(RSAKeyInfo); var result = signKey.VerifyData(Encoding.UTF8.GetBytes(toSign.ToString()), sig, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); return(new SignatureValidationResult() { SignatureIsValidated = result, User = remoteUser }); } }
public async Task <Tuple <bool, string> > VerifyHttpSignature(HttpContext context) { var signatureHeader = context.Request.Headers["Signature"].FirstOrDefault(); if (signatureHeader == null) { return(new Tuple <bool, string>(true, null)); } var parameters = signatureHeader.Replace("\\\"", "\n").Split(',').Select((a) => a.Split(new[] { '=' }, 2)).ToDictionary((a) => a[0], (a) => a[1].Trim('"').Replace("\n", "\\\"")); if (!parameters.ContainsKey("keyId") || !parameters.ContainsKey("algorithm") || !parameters.ContainsKey("signature")) { return(new Tuple <bool, string>(false, null)); } if (!parameters.ContainsKey("headers")) { parameters["headers"] = "date"; } var key = await _entityStore.GetEntity(parameters["keyId"], true); if (key == null) { return(new Tuple <bool, string>(false, null)); } var owner = await _entityStore.GetEntity(key.Data["owner"].First().Id ?? (string)key.Data["owner"].First().Primitive, true); if (!owner.Data["publicKey"].Any((a) => a.Id == key.Id)) { return(new Tuple <bool, string>(false, null)); } var stringKey = (string)key.Data["publicKeyPem"].First().Primitive; if (!stringKey.StartsWith("-----BEGIN PUBLIC KEY-----")) { return(new Tuple <bool, string>(false, null)); } var toDecode = stringKey.Trim().Remove(0, stringKey.IndexOf('\n')); toDecode = toDecode.Remove(toDecode.LastIndexOf('\n')).Replace("\n", ""); var signKey = ASN1.ToRSA(Convert.FromBase64String(toDecode)); var toSign = new StringBuilder(); foreach (var headerKey in parameters["headers"].Split(' ')) { if (headerKey == "(request-target)") { toSign.Append($"(request-target): {context.Request.Method.ToLower()} {context.Request.Path}{context.Request.QueryString}\n"); } else { toSign.Append($"{headerKey}: {string.Join(", ", context.Request.Headers[headerKey])}\n"); } } toSign.Remove(toSign.Length - 1, 1); var signature = Convert.FromBase64String(parameters["signature"]); switch (parameters["algorithm"]) { case "rsa-sha256": return(new Tuple <bool, string>(signKey.VerifyData(Encoding.UTF8.GetBytes(toSign.ToString()), signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1), owner.Id)); } return(new Tuple <bool, string>(false, owner.Id)); }