-
Notifications
You must be signed in to change notification settings - Fork 108
/
Copy pathRequestVerification.cs
91 lines (76 loc) · 3.35 KB
/
RequestVerification.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
namespace Alexa.NET.Request
{
public static class RequestVerification
{
private const int AllowedTimestampToleranceInSeconds = 150;
public static bool RequestTimestampWithinTolerance(SkillRequest request)
{
return RequestTimestampWithinTolerance(request.Request.Timestamp);
}
public static bool RequestTimestampWithinTolerance(DateTime timestamp)
{
return Math.Abs(DateTimeOffset.Now.Subtract(timestamp).TotalSeconds) <= AllowedTimestampToleranceInSeconds;
}
public static async Task<bool> Verify(string encodedSignature, Uri certificatePath, string body, Func<Uri,Task<X509Certificate2>> getCertificate = null)
{
if (!VerifyCertificateUrl(certificatePath))
{
return false;
}
var certificate = await (getCertificate ?? GetCertificate)(certificatePath);
return Verify(encodedSignature, certificate, body);
}
public static bool Verify(string encodedSignature, X509Certificate2 certificate, string body)
{
if (!ValidSigningCertificate(certificate) || !VerifyChain(certificate))
{
return false;
}
if (!AssertHashMatch(certificate, encodedSignature, body))
{
return false;
}
return true;
}
public static bool AssertHashMatch(X509Certificate2 certificate, string encodedSignature, string body)
{
var signature = Convert.FromBase64String(encodedSignature);
var rsa = certificate.GetRSAPublicKey();
return rsa.VerifyData(Encoding.UTF8.GetBytes(body), signature,HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
}
public static async Task<X509Certificate2> GetCertificate(Uri certificatePath)
{
var response = await new HttpClient().GetAsync(certificatePath);
var bytes = await response.Content.ReadAsByteArrayAsync();
return new(bytes);
}
public static bool VerifyChain(X509Certificate2 certificate)
{
//https://stackoverflow.com/questions/24618798/automated-downloading-of-x509-certificatePath-chain-from-remote-host
X509Chain certificateChain = new();
//If you do not provide revokation information, use the following line.
certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
return certificateChain.Build(certificate);
}
private static bool ValidSigningCertificate(X509Certificate2 certificate)
{
return DateTime.Now < certificate.NotAfter && DateTime.Now > certificate.NotBefore &&
certificate.GetNameInfo(X509NameType.SimpleName, false) == "echo-api.amazon.com";
}
public static bool VerifyCertificateUrl(Uri certificate)
{
return certificate.Scheme == "https" &&
certificate.Host == "s3.amazonaws.com" &&
certificate.LocalPath.StartsWith("/echo.api") &&
certificate.IsDefaultPort;
}
}
}