public object SerializeSignedObject() { object token = null; if (Payload == null) { throw new ArgumentException("Payload must be set before the token can be created and signed."); } JWK jwk = null; string kid = null; if (string.IsNullOrEmpty(_kid)) { //Create the JWK jwk = new JWK() { e = Base64Tool.Encode(_rsaParameters.Exponent), kty = "RSA", n = Base64Tool.Encode(_rsaParameters.Modulus) }; } else { kid = _kid; } //Create the Protected Header PROTECTED @protected = new PROTECTED() { alg = "RS256", jwk = jwk, kid = kid, nonce = _nonce, url = _directory }; //Encode jwk and payload string encodedProtected = Base64Tool.Encode(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(@protected, Formatting.None))); string encodedPayload = Base64Tool.Encode(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(Payload, Formatting.None))); //Sign Token string sigBase = $"{encodedProtected}.{encodedPayload}"; byte[] sigBytes = Encoding.ASCII.GetBytes(sigBase); byte[] signedBytes = _cryptoProvider.SignData(sigBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); string signature = Base64Tool.Encode(signedBytes); token = new { @protected = encodedProtected, payload = encodedPayload, signature = signature }; return(token); }
private string GenerateCSR(AcmeAccount account, params string[] domainNames) { HashAlgorithmName hashName = HashAlgorithmName.SHA256; var builder = new SubjectAlternativeNameBuilder(); foreach (var name in domainNames) { builder.AddDnsName(name); } RSA rsa = RSA.Create(4096); //rsa.ImportParameters(account.SecurityInfo); var dn = new X500DistinguishedName($"CN={domainNames.First()}"); var csr = new CertificateRequest(dn, rsa, hashName, RSASignaturePadding.Pkcs1); csr.CertificateExtensions.Add(builder.Build()); return(Base64Tool.Encode(csr.CreateSigningRequest())); }
public static string CreateAuthorizationKey(AcmeAccount account, string challengeToken) { string jwkThumbprint = string.Empty; //Compute the JWK Thumbprint var jwk = new { e = Base64Tool.Encode(account.SecurityInfo.Exponent), kty = "RSA", n = Base64Tool.Encode(account.SecurityInfo.Modulus) }; string sjwk = JsonConvert.SerializeObject(jwk, Formatting.None); using (HashAlgorithm sha = SHA256.Create()) { byte[] bjwk = Encoding.UTF8.GetBytes(sjwk); jwkThumbprint = Base64Tool.Encode(sha.ComputeHash(bjwk)); } return($"{challengeToken}.{jwkThumbprint}"); }
/// <summary> /// Changes and updates the account security info for an existing account. /// </summary> /// <param name="directory">Directory object.</param> /// <param name="nonce">Nonce</param> /// <param name="account">Must be existing account.</param> /// <returns>Return api response with status.</returns> /// <remarks>Will update the security info on the passed in account, so you will need to reserialize and update your existing account object to update the security info.</remarks> public async Task <AcmeApiResponse> RollOverAccountKeyAsync(AcmeDirectory directory, string nonce, AcmeAccount account) { if (directory == null) { throw new ArgumentNullException("directory"); } if (string.IsNullOrEmpty(directory.NewAccount)) { throw new ArgumentException("directory is missing Account url."); } if (string.IsNullOrEmpty(nonce)) { throw new ArgumentNullException("nonce"); } if (account == null) { throw new ArgumentNullException("account"); } RSACryptoServiceProvider cryptoProvider = new RSACryptoServiceProvider(2048); RSAParameters rsaPrams = cryptoProvider.ExportParameters(true); JwsContainer <ACCKEY> innerJwsObject = new JwsContainer <ACCKEY>( rsaPrams, nonce, directory.KeyChange, new ACCKEY() { account = account.KID, newKey = new JWK() { e = Base64Tool.Encode(rsaPrams.Exponent), kty = "RSA", n = Base64Tool.Encode(rsaPrams.Modulus) } }); object signedInnerJwsObject = innerJwsObject.SerializeSignedObject(); JwsContainer <object> outerJwsObject = new JwsContainer <object>(account.SecurityInfo, nonce, directory.KeyChange, account.KID, signedInnerJwsObject); string jwsToken = outerJwsObject.SerializeSignedToken(); var apiResp = await SendPostData( url : directory.KeyChange, data : jwsToken); string apiRespString = await apiResp.Content?.ReadAsStringAsync(); if (apiResp.StatusCode != HttpStatusCode.OK) { return(ErrorResponse(apiRespString)); } if (!apiResp.Headers.TryGetValues(ProtoacmeContants.HEADER_NONCE, out IEnumerable <string> nonces)) { return(ErrorResponse <AcmeAccount>("Missing Replay-Nonce Header on RolloverKey Response.")); } account.SecurityInfo = rsaPrams; return(new AcmeApiResponse() { Status = AcmeApiResponseStatus.Success, Nonce = nonces.FirstOrDefault() }); }