Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
AcmeClient.cs
1using System;
3using System.IO;
4using System.Net;
5using System.Net.Http;
6using System.Reflection;
7using System.Security.Authentication;
9using System.Security.Cryptography.X509Certificates;
10using System.Text;
11using System.Text.RegularExpressions;
12using System.Threading.Tasks;
13using Waher.Content;
17
18namespace Waher.Security.ACME
19{
23 public class AcmeClient : IDisposable
24 {
25 private const int KeySize = 4096;
26
27 private readonly Uri directoryEndpoint;
28 private HttpClient httpClient;
29 private AcmeDirectory directory = null;
30 private RsaSsaPkcsSha256 jws;
31 private string nonce = null;
32 private string jwkThumbprint = null;
33
39 public AcmeClient(Uri DirectoryEndpoint, RSAParameters Parameters)
40 {
41 this.directoryEndpoint = DirectoryEndpoint;
42 this.jws = new RsaSsaPkcsSha256(Parameters);
43
44 try
45 {
46 this.httpClient = new HttpClient(new HttpClientHandler()
47 {
48 AllowAutoRedirect = true,
49 AutomaticDecompression = (DecompressionMethods)(-1), // All
50 CheckCertificateRevocationList = true,
51 SslProtocols = Crypto.SecureTls
52 }, true);
53 }
54 catch (PlatformNotSupportedException)
55 {
56 this.httpClient = new HttpClient(new HttpClientHandler()
57 {
58 AllowAutoRedirect = true
59 }, true);
60 }
61
62 Type T = typeof(AcmeClient);
63 Version Version = T.Assembly.GetName().Version;
64 StringBuilder UserAgent = new StringBuilder();
65
66 UserAgent.Append(T.Namespace);
67 UserAgent.Append('/');
68 UserAgent.Append(Version.Major.ToString());
69 UserAgent.Append('.');
70 UserAgent.Append(Version.Minor.ToString());
71 UserAgent.Append('.');
72 UserAgent.Append(Version.Build.ToString());
73
74 this.httpClient.DefaultRequestHeaders.Add("User-Agent", UserAgent.ToString());
75 this.httpClient.DefaultRequestHeaders.Add("Accept", JwsAlgorithm.JwsContentType);
76 this.httpClient.DefaultRequestHeaders.Add("Accept-Language", "en");
77 }
78
82 public void Dispose()
83 {
84 if (!(this.httpClient is null))
85 {
86 this.httpClient.Dispose();
87 this.httpClient = null;
88 }
89
90 if (!(this.jws is null))
91 {
92 this.jws.Dispose();
93 this.jws = null;
94 }
95 }
96
101 public async Task<AcmeDirectory> GetDirectory()
102 {
103 if (this.directory is null)
104 this.directory = new AcmeDirectory(this, (await this.GET(this.directoryEndpoint)).Payload);
105
106 return this.directory;
107 }
108
109 internal Task<AcmeResponse> POST_as_GET(Uri URL, Uri AccountLocation)
110 {
111 return this.POST(URL, AccountLocation, null);
112 }
113
114 internal async Task<AcmeResponse> GET(Uri URL)
115 {
116 HttpResponseMessage Response = await this.httpClient.GetAsync(URL);
117
118 byte[] Bin = await Response.Content.ReadAsByteArrayAsync();
119 string CharSet = Response.Content.Headers.ContentType?.CharSet;
120 Encoding Encoding;
121
122 if (string.IsNullOrEmpty(CharSet))
123 Encoding = Encoding.UTF8;
124 else
125 Encoding = InternetContent.GetEncoding(CharSet);
126
127 string JsonResponse = Encoding.GetString(Bin);
128
129 if (!(JSON.Parse(JsonResponse) is IEnumerable<KeyValuePair<string, object>> Obj))
130 throw new Exception("Unexpected response returned.");
131
132 if (Response.Content.Headers.TryGetValues("Retry-After", out IEnumerable<string> _))
133 {
134 // TODO: Rate limit
135 }
136
137 if (Response.IsSuccessStatusCode)
138 {
139 return new AcmeResponse()
140 {
141 Payload = Obj,
142 Location = URL,
143 Json = JsonResponse,
144 ResponseMessage = Response
145 };
146 }
147 else
148 throw CreateException(Obj, Response);
149 }
150
151 internal async Task<string> NextNonce()
152 {
153 if (!string.IsNullOrEmpty(this.nonce))
154 {
155 string s = this.nonce;
156 this.nonce = null;
157 return s;
158 }
159
160 if (this.directory is null)
161 await this.GetDirectory();
162
163 HttpRequestMessage Request = new HttpRequestMessage(HttpMethod.Head, this.directory.NewNonce);
164 HttpResponseMessage Response = await this.httpClient.SendAsync(Request);
165
166 if (!Response.IsSuccessStatusCode)
167 {
168 ContentResponse Temp = await Content.Getters.WebGetter.ProcessResponse(Response, Request.RequestUri);
169 Temp.AssertOk();
170 }
171
172 if (Response.Headers.TryGetValues("Replay-Nonce", out IEnumerable<string> Values))
173 {
174 foreach (string s in Values)
175 return s;
176 }
177
178 throw new Exception("No nonce returned from server.");
179 }
180
184 public Task<AcmeDirectory> Directory
185 {
186 get
187 {
188 if (this.directory is null)
189 return this.GetDirectory();
190 else
191 return Task.FromResult<AcmeDirectory>(this.directory);
192 }
193 }
194
195 internal class AcmeResponse
196 {
197 public IEnumerable<KeyValuePair<string, object>> Payload;
198 public HttpResponseMessage ResponseMessage;
199 public Uri Location;
200 public string Json;
201 }
202
203 private async Task<HttpResponseMessage> HttpPost(Uri URL, Uri KeyID, string Accept, params KeyValuePair<string, object>[] Payload)
204 {
205 string HeaderString;
206 string PayloadString;
207 string Signature;
208
209 if (KeyID is null)
210 {
211 this.jws.Sign(new KeyValuePair<string, object>[]
212 {
213 new KeyValuePair<string, object>("nonce", await this.NextNonce()),
214 new KeyValuePair<string, object>("url", URL.ToString())
215 }, Payload, out HeaderString, out PayloadString, out Signature);
216 }
217 else
218 {
219 this.jws.Sign(new KeyValuePair<string, object>[]
220 {
221 new KeyValuePair<string, object>("kid", KeyID.ToString()),
222 new KeyValuePair<string, object>("nonce", await this.NextNonce()),
223 new KeyValuePair<string, object>("url", URL.ToString())
224 }, Payload, out HeaderString, out PayloadString, out Signature);
225 }
226
227 string Json = JSON.Encode(new KeyValuePair<string, object>[]
228 {
229 new KeyValuePair<string, object>("protected", HeaderString),
230 new KeyValuePair<string, object>("payload", PayloadString),
231 new KeyValuePair<string, object>("signature", Signature)
232 }, null);
233
234 HttpContent Content = new ByteArrayContent(Encoding.ASCII.GetBytes(Json));
235 Content.Headers.Add("Content-Type", JwsAlgorithm.JwsContentType);
236
237 if (!string.IsNullOrEmpty(Accept))
238 Content.Headers.TryAddWithoutValidation("Accept", Accept);
239
240 HttpResponseMessage Response = await this.httpClient.PostAsync(URL, Content);
241
242 this.GetNextNonce(Response);
243
244 return Response;
245 }
246
247 internal async Task<AcmeResponse> POST(Uri URL, Uri KeyID, params KeyValuePair<string, object>[] Payload)
248 {
249 HttpResponseMessage Response = await this.HttpPost(URL, KeyID, null, Payload);
250 byte[] Bin = await Response.Content.ReadAsByteArrayAsync();
251 string CharSet = Response.Content.Headers.ContentType?.CharSet;
252 Encoding Encoding;
253
254 if (string.IsNullOrEmpty(CharSet))
255 Encoding = Encoding.UTF8;
256 else
257 Encoding = InternetContent.GetEncoding(CharSet);
258
259 AcmeResponse AcmeResponse = new AcmeResponse()
260 {
261 Json = Encoding.GetString(Bin),
262 Location = URL,
263 ResponseMessage = Response,
264 Payload = null
265 };
266
267 if (Response.Headers.TryGetValues("Location", out IEnumerable<string> Values))
268 {
269 foreach (string s in Values)
270 {
271 AcmeResponse.Location = new Uri(s);
272 break;
273 }
274 }
275
276 if (string.IsNullOrEmpty(AcmeResponse.Json))
277 AcmeResponse.Payload = null;
278 else if ((AcmeResponse.Payload = JSON.Parse(AcmeResponse.Json) as IEnumerable<KeyValuePair<string, object>>) is null)
279 throw new Exception("Unexpected response returned.");
280
281 if (Response.IsSuccessStatusCode)
282 return AcmeResponse;
283 else
284 throw CreateException(AcmeResponse.Payload, Response);
285 }
286
287 internal static AcmeException CreateException(IEnumerable<KeyValuePair<string, object>> Obj, HttpResponseMessage Response)
288 {
289 AcmeException[] Subproblems = null;
290 string Type = null;
291 string Detail = null;
292 string instance = null;
293 int? Status = null;
294
295 foreach (KeyValuePair<string, object> P in Obj)
296 {
297 switch (P.Key)
298 {
299 case "type":
300 Type = P.Value as string;
301 break;
302
303 case "detail":
304 Detail = P.Value as string;
305 break;
306
307 case "instance":
308 instance = P.Value as string;
309 break;
310
311 case "status":
312 if (int.TryParse(P.Value as string, out int i))
313 Status = i;
314 break;
315
316 case "subproblems":
317 if (P.Value is Array A)
318 {
319 List<AcmeException> Subproblems2 = new List<AcmeException>();
320
321 foreach (object Obj2 in A)
322 {
323 if (Obj2 is IEnumerable<KeyValuePair<string, object>> Obj3)
324 Subproblems2.Add(CreateException(Obj3, Response));
325 }
326
327 Subproblems = Subproblems2.ToArray();
328 }
329 break;
330 }
331 }
332
333 if (Type.StartsWith("urn:ietf:params:acme:error:"))
334 {
335 switch (Type.Substring(27))
336 {
337 case "accountDoesNotExist": return new AcmeAccountDoesNotExistException(Type, Detail, Status, Subproblems);
338 case "badCSR": return new AcmeBadCsrException(Type, Detail, Status, Subproblems);
339 case "badNonce": return new AcmeBadNonceException(Type, Detail, Status, Subproblems);
340 case "badRevocationReason": return new AcmeBadRevocationReasonException(Type, Detail, Status, Subproblems);
341 case "badSignatureAlgorithm": return new AcmeBadSignatureAlgorithmException(Type, Detail, Status, Subproblems);
342 case "caa": return new AcmeCaaException(Type, Detail, Status, Subproblems);
343 case "compound": return new AcmeCompoundException(Type, Detail, Status, Subproblems);
344 case "connection": return new AcmeConnectionException(Type, Detail, Status, Subproblems);
345 case "dns": return new AcmeDnsException(Type, Detail, Status, Subproblems);
346 case "externalAccountRequired": return new AcmeExternalAccountRequiredException(Type, Detail, Status, Subproblems);
347 case "incorrectResponse": return new AcmeIncorrectResponseException(Type, Detail, Status, Subproblems);
348 case "invalidContact": return new AcmeInvalidContactException(Type, Detail, Status, Subproblems);
349 case "malformed": return new AcmeMalformedException(Type, Detail, Status, Subproblems);
350 case "rateLimited": return new AcmeRateLimitedException(Type, Detail, Status, Subproblems);
351 case "rejectedIdentifier": return new AcmeRejectedIdentifierException(Type, Detail, Status, Subproblems);
352 case "serverInternal": return new AcmeServerInternalException(Type, Detail, Status, Subproblems);
353 case "tls": return new AcmeTlsException(Type, Detail, Status, Subproblems);
354 case "unauthorized": return new AcmeUnauthorizedException(Type, Detail, Status, Subproblems);
355 case "unsupportedContact": return new AcmeUnsupportedContactException(Type, Detail, Status, Subproblems);
356 case "unsupportedIdentifier": return new AcmeUnsupportedIdentifierException(Type, Detail, Status, Subproblems);
357 case "userActionRequired": return new AcmeUserActionRequiredException(Type, Detail, Status, Subproblems, new Uri(instance), GetLink(Response, "terms-of-service"));
358 default: return new AcmeException(Type, Detail, Status, Subproblems);
359 }
360 }
361 else
362 return new AcmeException(Type, Detail, Status, Subproblems);
363 }
364
365 private static readonly Regex nextUrl = new Regex("^\\s*[<](?'URL'[^>]+)[>]\\s*;\\s*rel\\s*=\\s*['\"](?'Rel'.*)['\"]\\s*$", RegexOptions.Singleline | RegexOptions.Compiled);
366
367 internal static Uri GetLink(HttpResponseMessage Response, string Rel)
368 {
369 if (Response.Headers.TryGetValues("Link", out IEnumerable<string> Values))
370 {
371 foreach (string s in Values)
372 {
373 Match M = nextUrl.Match(s);
374 if (M.Success)
375 {
376 if (M.Groups["Rel"].Value == Rel)
377 return new Uri(M.Groups["URL"].Value);
378 }
379 }
380 }
381
382 return null;
383 }
384
385 private void GetNextNonce(HttpResponseMessage Response)
386 {
387 if (Response.Headers.TryGetValues("Replay-Nonce", out IEnumerable<string> Values))
388 {
389 foreach (string s in Values)
390 {
391 this.nonce = s;
392 return;
393 }
394 }
395 }
396
403 public async Task<AcmeAccount> CreateAccount(string[] ContactURLs, bool TermsOfServiceAgreed)
404 {
405 if (this.directory is null)
406 await this.GetDirectory();
407
408 AcmeResponse Response = await this.POST(this.directory.NewAccount, null,
409 new KeyValuePair<string, object>("termsOfServiceAgreed", TermsOfServiceAgreed),
410 new KeyValuePair<string, object>("contact", ContactURLs));
411
412 AcmeAccount Account;
413
414 if (Response.Payload is null)
415 {
416 Response = await this.POST(Response.Location, Response.Location);
417 Account = new AcmeAccount(this, Response.Location, Response.Payload);
418
419 bool ContactsDifferent = false;
420 int i, c = ContactURLs.Length;
421
422 if (c != Account.Contact.Length)
423 ContactsDifferent = true;
424 else
425 {
426 for (i = 0; i < c; i++)
427 {
428 if (ContactURLs[i] != Account.Contact[i])
429 {
430 ContactsDifferent = true;
431 break;
432 }
433 }
434 }
435
436 if (ContactsDifferent)
437 Account = await this.UpdateAccount(Account.Location, ContactURLs);
438 }
439 else
440 Account = new AcmeAccount(this, Response.Location, Response.Payload);
441
442 return Account;
443 }
444
449 public async Task<AcmeAccount> GetAccount()
450 {
451 if (this.directory is null)
452 await this.GetDirectory();
453
454 AcmeResponse Response = await this.POST(this.directory.NewAccount, null,
455 new KeyValuePair<string, object>("onlyReturnExisting", true));
456
457 if (Response.Payload is null)
458 Response = await this.POST(Response.Location, Response.Location);
459
460 return new AcmeAccount(this, Response.Location, Response.Payload);
461 }
462
469 public async Task<AcmeAccount> UpdateAccount(Uri AccountLocation, string[] Contact)
470 {
471 if (this.directory is null)
472 await this.GetDirectory();
473
474 AcmeResponse Response = await this.POST(AccountLocation, AccountLocation,
475 new KeyValuePair<string, object>("contact", Contact));
476
477 return new AcmeAccount(this, Response.Location, Response.Payload);
478 }
479
485 public async Task<AcmeAccount> DeactivateAccount(Uri AccountLocation)
486 {
487 if (this.directory is null)
488 await this.GetDirectory();
489
490 AcmeResponse Response = await this.POST(AccountLocation, AccountLocation,
491 new KeyValuePair<string, object>("status", "deactivated"));
492
493 return new AcmeAccount(this, Response.Location, Response.Payload);
494 }
495
500 public async Task<AcmeAccount> NewKey(Uri AccountLocation)
501 {
502 if (this.directory is null)
503 await this.GetDirectory();
504 RSA NewKey = RSA.Create();
505 NewKey.KeySize = KeySize;
506
507 if (NewKey.KeySize != KeySize) // Happens when using library from traditioanl .NET FW
508 {
509 Type T = Runtime.Inventory.Types.GetType("System.Security.Cryptography.RSACryptoServiceProvider")
510 ?? throw new Exception("Unable to set RSA key size to anything but default (" + NewKey.KeySize.ToString() + " bits).");
511
512 NewKey = Runtime.Inventory.Types.Instantiate(T, KeySize) as RSA;
513 }
514
516
517 try
518 {
519 Jws2.Sign(new KeyValuePair<string, object>[]
520 {
521 new KeyValuePair<string, object>("url", this.directory.KeyChange.ToString())
522 }, new KeyValuePair<string, object>[]
523 {
524 new KeyValuePair<string, object>("account", AccountLocation.ToString()),
525 new KeyValuePair<string, object>("oldkey", this.jws.PublicWebKey),
526 }, out string Header, out string Payload, out string Signature);
527
528 AcmeResponse Response = await this.POST(this.directory.KeyChange, AccountLocation,
529 new KeyValuePair<string, object>("protected", Header),
530 new KeyValuePair<string, object>("payload", Payload),
531 new KeyValuePair<string, object>("signature", Signature));
532
533 this.jwkThumbprint = null;
534 this.jws.ImportKey(NewKey);
535
536 return new AcmeAccount(this, Response.Location, Response.Payload);
537 }
538 finally
539 {
540 Jws2.Dispose();
541 }
542 }
543
552 public async Task<AcmeOrder> OrderCertificate(Uri AccountLocation, AcmeIdentifier[] Identifiers,
553 DateTime? NotBefore, DateTime? NotAfter)
554 {
555 if (this.directory is null)
556 await this.GetDirectory();
557
558 int i, c = Identifiers.Length;
559 IEnumerable<KeyValuePair<string, object>>[] Identifiers2 = new IEnumerable<KeyValuePair<string, object>>[c];
560
561 for (i = 0; i < c; i++)
562 {
563 Identifiers2[i] = new KeyValuePair<string, object>[]
564 {
565 new KeyValuePair<string, object>("type", Identifiers[i].Type),
566 new KeyValuePair<string, object>("value", Identifiers[i].Value)
567 };
568 }
569
570 List<KeyValuePair<string, object>> Payload = new List<KeyValuePair<string, object>>()
571 {
572 new KeyValuePair<string, object>("identifiers", Identifiers2)
573 };
574
575 if (NotBefore.HasValue)
576 Payload.Add(new KeyValuePair<string, object>("notBefore", NotBefore.Value));
577
578 if (NotAfter.HasValue)
579 Payload.Add(new KeyValuePair<string, object>("notAfter", NotAfter.Value));
580
581 AcmeResponse Response = await this.POST(this.directory.NewOrder, AccountLocation, Payload.ToArray());
582
583 return new AcmeOrder(this, AccountLocation, Response.Location, Response.Payload, Response.ResponseMessage);
584 }
585
592 public async Task<AcmeOrder> GetOrder(Uri AccountLocation, Uri OrderLocation)
593 {
594 AcmeResponse Response = await this.POST_as_GET(OrderLocation, AccountLocation);
595 return new AcmeOrder(this, AccountLocation, OrderLocation, Response.Payload, Response.ResponseMessage);
596 }
597
604 public async Task<AcmeOrder[]> GetOrders(Uri AccountLocation, Uri OrdersLocation)
605 {
606 AcmeResponse _ = await this.GET(OrdersLocation);
607 throw new NotImplementedException("Method not implemented.");
608 }
609
616 public async Task<AcmeAuthorization> GetAuthorization(Uri AccountLocation, Uri AuthorizationLocation)
617 {
618 AcmeResponse Response = await this.POST_as_GET(AuthorizationLocation, AccountLocation);
619 return new AcmeAuthorization(this, AccountLocation, AuthorizationLocation, Response.Payload);
620 }
621
628 public async Task<AcmeAuthorization> DeactivateAuthorization(Uri AccountLocation, Uri AuthorizationLocation)
629 {
630 AcmeResponse Response = await this.POST(AuthorizationLocation, AccountLocation,
631 new KeyValuePair<string, object>("status", "deactivated"));
632
633 return new AcmeAuthorization(this, AccountLocation, Response.Location, Response.Payload);
634 }
635
642 public async Task<AcmeChallenge> AcknowledgeChallenge(Uri AccountLocation, Uri ChallengeLocation)
643 {
644 AcmeResponse Response = await this.POST(ChallengeLocation, AccountLocation);
645 return this.CreateChallenge(AccountLocation, Response.Payload);
646 }
647
648 internal AcmeChallenge CreateChallenge(Uri AccountLocation, IEnumerable<KeyValuePair<string, object>> Obj)
649 {
650 string Type = string.Empty;
651
652 foreach (KeyValuePair<string, object> P2 in Obj)
653 {
654 if (P2.Key == "type" && P2.Value is string s)
655 {
656 Type = s;
657 break;
658 }
659 }
660
661 switch (Type)
662 {
663 case "http-01": return new AcmeHttpChallenge(this, AccountLocation, Obj);
664 case "dns-01": return new AcmeDnsChallenge(this, AccountLocation, Obj);
665 default: return new AcmeChallenge(this, AccountLocation, Obj);
666 }
667 }
668
673 internal string JwkThumbprint
674 {
675 get
676 {
677 if (this.jwkThumbprint is null)
678 {
679 SortedDictionary<string, object> Sorted = new SortedDictionary<string, object>();
680
681 foreach (KeyValuePair<string, object> P in this.jws.PublicWebKey)
682 {
683 switch (P.Key)
684 {
685 case "kty":
686 case "n":
687 case "e":
688 Sorted[P.Key] = P.Value;
689 break;
690 }
691 }
692
693 string Json = JSON.Encode(Sorted, null);
694 byte[] Bin = Encoding.UTF8.GetBytes(Json);
695 byte[] Hash = Hashes.ComputeSHA256Hash(Bin);
696
697 this.jwkThumbprint = Base64Url.Encode(Hash);
698 }
699
700 return this.jwkThumbprint;
701 }
702 }
703
711 public async Task<AcmeOrder> FinalizeOrder(Uri AccountLocation, Uri FinalizeLocation, CertificateRequest CertificateRequest)
712 {
713 byte[] CSR = CertificateRequest.BuildCSR();
714 AcmeResponse Response = await this.POST(FinalizeLocation, AccountLocation,
715 new KeyValuePair<string, object>("csr", Base64Url.Encode(CSR)));
716
717 return new AcmeOrder(this, AccountLocation, Response.Location, Response.Payload, Response.ResponseMessage);
718 }
719
726 public async Task<X509Certificate2[]> DownloadCertificate(Uri AccountLocation, Uri CertificateLocation)
727 {
728 string ContentType = PemDecoder.ContentType;
729 HttpResponseMessage Response = await this.HttpPost(CertificateLocation, AccountLocation, ContentType, null);
730
731 if (!Response.IsSuccessStatusCode)
732 {
733 ContentResponse Temp = await Waher.Content.Getters.WebGetter.ProcessResponse(Response, AccountLocation);
734 Temp.AssertOk();
735 }
736
737 byte[] Bin = await Response.Content.ReadAsByteArrayAsync();
738
739 if (Response.Headers.TryGetValues("Content-Type", out IEnumerable<string> Values))
740 {
741 foreach (string s in Values)
742 {
743 ContentType = s;
744 break;
745 }
746 }
747
748 ContentResponse Content = await InternetContent.DecodeAsync(ContentType, Bin, CertificateLocation);
749 Content.AssertOk();
750
751 if (!(Content.Decoded is X509Certificate2[] Certificates))
752 throw new Exception("Unexpected response returned. Content-Type: " + ContentType);
753
754 return Certificates;
755 }
756
762 public RSAParameters ExportAccountKey(bool IncludePrivateParameters)
763 {
764 return this.jws.RSA.ExportParameters(IncludePrivateParameters);
765 }
766
767 }
768}
Static class that does BASE64URL encoding (using URL and filename safe alphabet), as defined in RFC46...
Definition: Base64Url.cs:11
static string Encode(byte[] Data)
Converts a binary block of data to a Base64URL-encoded string.
Definition: Base64Url.cs:48
Contains information about a response to a content request.
object Decoded
Decoded object.
void AssertOk()
Asserts response is OK.
Gets resources from the Web (i.e. using HTTP or HTTPS).
Definition: WebGetter.cs:25
static async Task< ContentResponse > ProcessResponse(HttpResponseMessage Response, Uri Uri)
Decodes a response from the web. If the response is a success, the decoded response is returned....
Definition: WebGetter.cs:386
Static class managing encoding and decoding of internet content.
static Task< ContentResponse > DecodeAsync(string ContentType, byte[] Data, Encoding Encoding, KeyValuePair< string, string >[] Fields, Uri BaseUri)
Decodes an object.
static Encoding GetEncoding(string CharacterSet)
Gets a character encoding from its name.
Helps with common JSON-related tasks.
Definition: JSON.cs:16
static object Parse(string Json)
Parses a JSON string.
Definition: JSON.cs:45
static string Encode(string s)
Encodes a string for inclusion in JSON.
Definition: JSON.cs:537
Represents an ACME account.
Definition: AcmeAccount.cs:34
string[] Contact
Optional array of URLs that the server can use to contact the client for issues related to this accou...
Definition: AcmeAccount.cs:98
Represents an ACME authorization.
Base class of all ACME challenges.
Implements an ACME client for the generation of certificates using ACME-compliant certificate servers...
Definition: AcmeClient.cs:24
async Task< AcmeAuthorization > DeactivateAuthorization(Uri AccountLocation, Uri AuthorizationLocation)
Deactivates an authorization.
Definition: AcmeClient.cs:628
async Task< AcmeAccount > NewKey(Uri AccountLocation)
Generates a new key for the account. (Account keys are managed by the CSP.)
Definition: AcmeClient.cs:500
async Task< AcmeOrder > GetOrder(Uri AccountLocation, Uri OrderLocation)
Gets the state of an order.
Definition: AcmeClient.cs:592
RSAParameters ExportAccountKey(bool IncludePrivateParameters)
Exports the account key.
Definition: AcmeClient.cs:762
async Task< AcmeAccount > GetAccount()
Gets the account object from the ACME server.
Definition: AcmeClient.cs:449
async Task< AcmeAccount > CreateAccount(string[] ContactURLs, bool TermsOfServiceAgreed)
Creates an account on the ACME server.
Definition: AcmeClient.cs:403
async Task< AcmeOrder > OrderCertificate(Uri AccountLocation, AcmeIdentifier[] Identifiers, DateTime? NotBefore, DateTime? NotAfter)
Orders certificate.
Definition: AcmeClient.cs:552
void Dispose()
IDisposable.Dispose
Definition: AcmeClient.cs:82
async Task< AcmeChallenge > AcknowledgeChallenge(Uri AccountLocation, Uri ChallengeLocation)
Acknowledges a challenge from the server.
Definition: AcmeClient.cs:642
async Task< AcmeAccount > UpdateAccount(Uri AccountLocation, string[] Contact)
Updates an account.
Definition: AcmeClient.cs:469
async Task< AcmeOrder[]> GetOrders(Uri AccountLocation, Uri OrdersLocation)
Gets the list of current orders for an account.
Definition: AcmeClient.cs:604
async Task< AcmeDirectory > GetDirectory()
Gets the ACME directory.
Definition: AcmeClient.cs:101
AcmeClient(Uri DirectoryEndpoint, RSAParameters Parameters)
Implements an ACME client for the generation of certificates using ACME-compliant certificate servers...
Definition: AcmeClient.cs:39
async Task< AcmeAuthorization > GetAuthorization(Uri AccountLocation, Uri AuthorizationLocation)
Gets the state of an authorization.
Definition: AcmeClient.cs:616
async Task< AcmeOrder > FinalizeOrder(Uri AccountLocation, Uri FinalizeLocation, CertificateRequest CertificateRequest)
Finalize order.
Definition: AcmeClient.cs:711
Task< AcmeDirectory > Directory
Directory object.
Definition: AcmeClient.cs:185
async Task< X509Certificate2[]> DownloadCertificate(Uri AccountLocation, Uri CertificateLocation)
Downloads a certificate.
Definition: AcmeClient.cs:726
async Task< AcmeAccount > DeactivateAccount(Uri AccountLocation)
Deactivates an account.
Definition: AcmeClient.cs:485
Represents an ACME directory.
Uri KeyChange
URL for keyChange method.
Uri NewAccount
URL for newAccount method.
Uri NewOrder
URL for newOrder method.
Represents an ACME identifier.
Represents an ACME order.
Definition: AcmeOrder.cs:50
Uri Location
Location of resource.
Definition: AcmeResource.cs:37
Contains methods for simple hash calculations.
Definition: Hashes.cs:57
static byte[] ComputeSHA256Hash(byte[] Data)
Computes the SHA-256 hash of a block of binary data.
Definition: Hashes.cs:469
Abstract base class for JWS algorithm.
Definition: JwsAlgorithm.cs:15
const string JwsContentType
application/jose+json
Definition: JwsAlgorithm.cs:19
RSASSA-PKCS1-v1_5 SHA-256 algorithm. https://tools.ietf.org/html/rfc3447#page-32
RSA RSA
RSA Cryptographic service provider.
void ImportKey(RSA RSA)
Imports a new key from an external RSA Cryptographic service provider.
override IEnumerable< KeyValuePair< string, object > > PublicWebKey
The public JSON web key, if supported.
override void Dispose()
IDisposable.Dispose
override string Sign(string HeaderEncoded, string PayloadEncoded)
Signs data.
Contains information about a Certificate Signing Request (CSR).
byte[] BuildCSR()
Building a Certificate Signing Request (CSR) in accordance with RFC 2986
Decodes certificates encoded using the application/pem-certificate-chain content type.
Definition: PemDecoder.cs:15
const string ContentType
application/pem-certificate-chain
Definition: PemDecoder.cs:19
Definition: ImplTypes.g.cs:58
Definition: App.xaml.cs:4