public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log, ExecutionContext context) { log.LogInformation("Certificate trigger function processed a request."); try { await ReadAppSettings(context, log); string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); Certificate_Request request = JsonConvert.DeserializeObject <Certificate_Request>(requestBody); // Validate payload if (string.IsNullOrEmpty(request.RegistrationId) || string.IsNullOrEmpty(request.Csr)) { return(new BadRequestResult()); } // Check if the device is authorized to request a certificate bool isAuthorized = CheckIfAuthorized(request.RegistrationId); if (isAuthorized) { log.LogInformation($"{request.RegistrationId} is authorized."); Pkcs10CertificationRequest decodedCsr = null; RsaKeyParameters publicKey = null; CertificationRequestInfo info = null; // Get the signing certificate from a location X509Certificate serverCertificate = ReadCertificate(cert, location, log); if (serverCertificate == null) { throw new System.Exception("ReadCertificate() was unable to retrieve the signing certificate."); } // Get signing cert private key from a location. AsymmetricKeyParameter serverPrivateKey = ReadPrivateKey(key, location, log); if (serverPrivateKey == null) { throw new System.Exception("ReadPrivateKey() was unable to retrieve the private key."); } byte[] csr = Convert.FromBase64String(request.Csr); // Decode DER decodedCsr = new Pkcs10CertificationRequest(csr); info = decodedCsr.GetCertificationRequestInfo(); SubjectPublicKeyInfo publicKeyInfo = info.SubjectPublicKeyInfo; RsaPublicKeyStructure publicKeyStructure = RsaPublicKeyStructure.GetInstance(publicKeyInfo.ParsePublicKey()); publicKey = new RsaKeyParameters(false, publicKeyStructure.Modulus, publicKeyStructure.PublicExponent); bool certIsOK = decodedCsr.Verify(publicKey); // Create the device certificate X509V3CertificateGenerator generator = new X509V3CertificateGenerator(); generator.SetSerialNumber(BigInteger.ProbablePrime(120, new Random())); generator.SetIssuerDN(serverCertificate.SubjectDN); generator.SetNotBefore(DateTime.Now); generator.SetNotAfter(DateTime.Now.AddYears(certificateLifespanInYears)); generator.SetSubjectDN(info.Subject); generator.SetPublicKey(publicKey); generator.SetSignatureAlgorithm("SHA512withRSA"); generator.AddExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure(serverCertificate)); generator.AddExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(publicKey)); // Generate the device certificate var deviceCert = generator.Generate(serverPrivateKey); // Convert to DER byte[] encoded = deviceCert.GetEncoded(); // Convert byte array to Base64 string string encodedString = Convert.ToBase64String(encoded); Certificate_Response responseMessage = new Certificate_Response { Certificate = encodedString }; log.LogInformation($"Certificate issued for: {info.Subject}"); return(new OkObjectResult(responseMessage)); } else { log.LogError($"{request.RegistrationId} is NOT authorized."); return(new UnauthorizedResult()); } } catch (Exception ex) { log.LogInformation(ex.Message); } return(new BadRequestResult()); }
/// <summary> /// Generates and sends a CSR to Xboot.Server REST endpoint. /// </summary> /// <param name="registrationId"></param> /// <param name="name"></param> /// <returns>Pfx in DER-encoded byte array</returns> public byte[] GetDeviceCertificate(string registrationId, X500DistinguishedName name) { byte[] pfx = null; var req = new CertificateRequest(name, _rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pss); // Returns a DER-encoded PKCS#10 CSR var csr = req.CreateSigningRequest(); Certificate_Request request = new Certificate_Request { RegistrationId = registrationId, Csr = Convert.ToBase64String(csr) }; var json = JsonConvert.SerializeObject(request); var client = new HttpClient(); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/json")); var buffer = System.Text.Encoding.UTF8.GetBytes(json); var byteContent = new ByteArrayContent(buffer); try { var response = client.PostAsync(_xBootUri, byteContent).Result; if (response.IsSuccessStatusCode) { // Get the response var jsonString = response.Content.ReadAsStringAsync().Result; var certString = JsonConvert.DeserializeObject <Certificate_Response>(jsonString); byte[] certBytes = Convert.FromBase64String(certString.Certificate); // Read in signed device certificate in DER format X509Certificate2 cert = new X509Certificate2(certBytes); // Add private key to cert cert = cert.CopyWithPrivateKey(_rsa); if (cert.HasPrivateKey) { // Combine certificate and private key into single pfx // The IoT Device SDK needs both the certificate and the private key information. // It expects to load a single PFX-formatted file containing all necessarily information. pfx = cert.Export(X509ContentType.Pfx); } } else { throw new Exception(response.StatusCode.ToString()); } } catch (Exception ex) { throw new Exception(ex.Message); } // Return certificate in DER-encoded byte array return(pfx); }