public async Task <ReadRequestResultModel> ReadAsync( string requestId ) { Guid reqId = GetIdFromString(requestId); CertificateRequest request = await _certificateRequests.GetAsync(reqId); switch (request.CertificateRequestState) { case CertificateRequestState.New: case CertificateRequestState.Rejected: case CertificateRequestState.Accepted: case CertificateRequestState.Approved: case CertificateRequestState.Deleted: case CertificateRequestState.Revoked: case CertificateRequestState.Removed: break; default: throw new ResourceInvalidStateException("The record is not in a valid state for this operation."); } return(new ReadRequestResultModel( requestId, request.ApplicationId, request.CertificateRequestState, request.CertificateGroupId, request.CertificateTypeId, request.SigningRequest, request.SubjectName, request.DomainNames, request.PrivateKeyFormat)); }
public async Task AcceptAsync( string requestId) { Guid reqId = GetIdFromString(requestId); bool retryUpdate; bool first = true; do { retryUpdate = false; CertificateRequest request = await _certificateRequests.GetAsync(reqId); if (request.CertificateRequestState != CertificateRequestState.Approved) { throw new ResourceInvalidStateException("The record is not in a valid state for this operation."); } if (request.PrivateKeyFormat != null && first) { try { await _certificateGroup.DeletePrivateKeyAsync(request.CertificateGroupId, requestId); } catch (KeyVaultErrorException kex) { if (kex.Response.StatusCode != HttpStatusCode.Forbidden) { throw kex; } // ok to ignore, default KeyVault secret access 'Delete' is not granted. // private key not deleted, must be handled by manager role } } first = false; request.CertificateRequestState = CertificateRequestState.Accepted; // erase information which is not required anymore request.SigningRequest = null; request.PrivateKeyFormat = null; request.PrivateKeyPassword = null; request.AcceptTime = DateTime.UtcNow; try { await _certificateRequests.UpdateAsync(request.RequestId, request, request.ETag); } catch (DocumentClientException dce) { if (dce.StatusCode == HttpStatusCode.PreconditionFailed) { retryUpdate = true; } } } while (retryUpdate); }
public async Task RevokeAsync(string requestId) { Guid reqId = GetIdFromString(requestId); bool retryUpdate; do { retryUpdate = false; CertificateRequest request = await _certificateRequests.GetAsync(reqId); if (request.Certificate == null || request.CertificateRequestState != CertificateRequestState.Deleted) { throw new ResourceInvalidStateException("The record is not in a valid state for this operation."); } request.CertificateRequestState = CertificateRequestState.Revoked; // erase information which is not required anymore request.PrivateKeyFormat = null; request.SigningRequest = null; request.PrivateKeyPassword = null; try { var cert = new X509Certificate2(request.Certificate); var crl = await _certificateGroup.RevokeCertificateAsync(request.CertificateGroupId, cert); } catch (Exception e) { StringBuilder error = new StringBuilder(); error.Append("Error Revoking Certificate=" + e.Message); error.Append("\r\nGroupId=" + request.CertificateGroupId); throw new ResourceInvalidStateException(error.ToString()); } request.RevokeTime = DateTime.UtcNow; try { await _certificateRequests.UpdateAsync(reqId, request, request.ETag); } catch (DocumentClientException dce) { if (dce.StatusCode == HttpStatusCode.PreconditionFailed) { retryUpdate = true; } } } while (retryUpdate); }
public async Task PurgeAsync(string requestId) { Guid reqId = GetIdFromString(requestId); CertificateRequest request = await _certificateRequests.GetAsync(reqId); if (request.CertificateRequestState != CertificateRequestState.Revoked && request.CertificateRequestState != CertificateRequestState.Rejected && request.CertificateRequestState != CertificateRequestState.New && request.CertificateRequestState != CertificateRequestState.Removed) { throw new ResourceInvalidStateException("The record is not in a valid state for this operation."); } await _certificateRequests.DeleteAsync(request.RequestId); }
public async Task <string> StartSigningRequestAsync( string applicationId, string certificateGroupId, string certificateTypeId, byte[] certificateSigningRequest, string authorityId) { Application application = await _applicationsDatabase.GetApplicationAsync(applicationId); if (string.IsNullOrEmpty(certificateGroupId)) { //TODO: } if (string.IsNullOrEmpty(certificateTypeId)) { //TODO } CertificateRequest request = new CertificateRequest() { RequestId = Guid.NewGuid(), AuthorityId = authorityId }; request.ID = _certRequestIdCounter++; request.CertificateRequestState = (int)CertificateRequestState.New; request.CertificateGroupId = certificateGroupId; request.CertificateTypeId = certificateTypeId; request.SubjectName = null; request.DomainNames = null; request.PrivateKeyFormat = null; request.PrivateKeyPassword = null; request.SigningRequest = certificateSigningRequest; request.ApplicationId = applicationId; request.RequestTime = DateTime.UtcNow; bool retry; do { retry = false; try { var result = await _certificateRequests.CreateAsync(request); } catch (DocumentClientException dce) { if (dce.StatusCode == System.Net.HttpStatusCode.Conflict) { // retry with new guid and id request.RequestId = Guid.NewGuid(); _certRequestIdCounter = await GetMaxCertIDAsync(); request.ID = _certRequestIdCounter++; retry = true; } } } while (retry); return(request.RequestId.ToString()); }
public async Task <FetchRequestResultModel> FetchRequestAsync( string requestId, string applicationId) { Guid reqId = GetIdFromString(requestId); Application application = await _applicationsDatabase.GetApplicationAsync(applicationId); CertificateRequest request = await _certificateRequests.GetAsync(reqId); if (request.ApplicationId != application.ApplicationId.ToString()) { throw new ArgumentException("The recordId does not match the applicationId."); } switch (request.CertificateRequestState) { case CertificateRequestState.New: case CertificateRequestState.Rejected: case CertificateRequestState.Revoked: case CertificateRequestState.Deleted: case CertificateRequestState.Removed: return(new FetchRequestResultModel(request.CertificateRequestState) { ApplicationId = applicationId, RequestId = requestId }); case CertificateRequestState.Accepted: case CertificateRequestState.Approved: break; default: throw new ResourceInvalidStateException("The record is not in a valid state for this operation."); } // get private key byte[] privateKey = null; if (request.CertificateRequestState == CertificateRequestState.Approved && request.PrivateKeyFormat != null) { try { privateKey = await _certificateGroup.LoadPrivateKeyAsync(request.CertificateGroupId, requestId, request.PrivateKeyFormat); } catch { // intentionally ignore error when reading private key // it may have been disabled by keyvault due to inactivity... request.PrivateKeyFormat = null; privateKey = null; } } return(new FetchRequestResultModel( request.CertificateRequestState, applicationId, requestId, request.CertificateGroupId, request.CertificateTypeId, request.Certificate, request.PrivateKeyFormat, privateKey, request.AuthorityId)); }
public async Task RevokeGroupAsync(string groupId, bool?allVersions) { var queryParameters = new SqlParameterCollection(); string query = "SELECT * FROM CertificateRequest r WHERE "; query += " r.CertificateRequestState = @state"; queryParameters.Add(new SqlParameter("@state", CertificateRequestState.Deleted.ToString())); SqlQuerySpec sqlQuerySpec = new SqlQuerySpec { QueryText = query, Parameters = queryParameters }; var deletedRequests = await _certificateRequests.GetAsync(sqlQuerySpec); if (deletedRequests == null || deletedRequests.Count() == 0) { return; } var revokedId = new List <Guid>(); var certCollection = new X509Certificate2Collection(); foreach (var request in deletedRequests) { if (request.Certificate != null) { if (String.Compare(request.CertificateGroupId, groupId, StringComparison.OrdinalIgnoreCase) == 0) { try { var cert = new X509Certificate2(request.Certificate); certCollection.Add(cert); revokedId.Add(request.RequestId); } catch { // skip } } } } var remainingCertificates = await _certificateGroup.RevokeCertificatesAsync(groupId, certCollection); foreach (var reqId in deletedRequests) { bool retryUpdate; do { retryUpdate = false; CertificateRequest request = await _certificateRequests.GetAsync(reqId.RequestId); if (request.CertificateRequestState != CertificateRequestState.Deleted) { // skip, there may have been a concurrent update to the database. continue; } // TODO: test for remaining certificates request.CertificateRequestState = CertificateRequestState.Revoked; request.RevokeTime = DateTime.UtcNow; // erase information which is not required anymore request.Certificate = null; request.PrivateKeyFormat = null; request.SigningRequest = null; request.PrivateKeyPassword = null; try { await _certificateRequests.UpdateAsync(reqId.RequestId, request, request.ETag); } catch (DocumentClientException dce) { if (dce.StatusCode == HttpStatusCode.PreconditionFailed) { retryUpdate = true; } } } while (retryUpdate); } }
public async Task ApproveAsync( string requestId, bool isRejected ) { Guid reqId = GetIdFromString(requestId); bool retryUpdate; do { retryUpdate = false; CertificateRequest request = await _certificateRequests.GetAsync(reqId); if (request.CertificateRequestState != CertificateRequestState.New) { throw new ResourceInvalidStateException("The record is not in a valid state for this operation."); } Application application = await _applicationsDatabase.GetApplicationAsync(request.ApplicationId); if (isRejected) { request.CertificateRequestState = CertificateRequestState.Rejected; // erase information which is not required anymore request.PrivateKeyFormat = null; request.SigningRequest = null; request.PrivateKeyPassword = null; } else { request.CertificateRequestState = CertificateRequestState.Approved; X509Certificate2 certificate; if (request.SigningRequest != null) { try { certificate = await _certificateGroup.SigningRequestAsync( request.CertificateGroupId, application.ApplicationUri, request.SigningRequest ); request.Certificate = certificate.RawData; } catch (Exception e) { StringBuilder error = new StringBuilder(); error.Append("Error Generating Certificate=" + e.Message); error.Append("\r\nApplicationId=" + application.ApplicationId); error.Append("\r\nApplicationUri=" + application.ApplicationUri); error.Append("\r\nApplicationName=" + application.ApplicationNames[0].Text); throw new ResourceInvalidStateException(error.ToString()); } } else { Opc.Ua.Gds.Server.X509Certificate2KeyPair newKeyPair = null; try { newKeyPair = await _certificateGroup.NewKeyPairRequestAsync( request.CertificateGroupId, requestId, application.ApplicationUri, request.SubjectName, request.DomainNames, request.PrivateKeyFormat, request.PrivateKeyPassword); } catch (Exception e) { StringBuilder error = new StringBuilder(); error.Append("Error Generating New Key Pair Certificate=" + e.Message); error.Append("\r\nApplicationId=" + application.ApplicationId); error.Append("\r\nApplicationUri=" + application.ApplicationUri); throw new ResourceInvalidStateException(error.ToString()); } request.Certificate = newKeyPair.Certificate.RawData; // ignore private key, it is stored in KeyVault } } request.ApproveRejectTime = DateTime.UtcNow; try { await _certificateRequests.UpdateAsync(reqId, request, request.ETag); } catch (DocumentClientException dce) { if (dce.StatusCode == HttpStatusCode.PreconditionFailed) { retryUpdate = true; } } } while (retryUpdate); }
public async Task <string> StartNewKeyPairRequestAsync( string applicationId, string certificateGroupId, string certificateTypeId, string subjectName, IList <string> domainNames, string privateKeyFormat, string privateKeyPassword, string authorityId) { Application application = await _applicationsDatabase.GetApplicationAsync(applicationId); if (string.IsNullOrEmpty(certificateGroupId)) { //TODO } if (string.IsNullOrEmpty(certificateTypeId)) { //TODO } if (string.IsNullOrEmpty(subjectName)) { throw new ArgumentNullException(nameof(subjectName)); } CertificateRequest request = null; request = new CertificateRequest() { RequestId = Guid.NewGuid(), AuthorityId = authorityId }; var subjectList = Opc.Ua.Utils.ParseDistinguishedName(subjectName); if (subjectList == null || subjectList.Count == 0) { throw new ArgumentException("Invalid Subject", nameof(subjectName)); } if (!subjectList.Any(c => c.StartsWith("CN=", StringComparison.InvariantCulture))) { throw new ArgumentException("Invalid Subject, must have a common name (CN=).", nameof(subjectName)); } // enforce proper formatting for the subject name string subjectName = string.Join(", ", subjectList); List <string> discoveryUrlDomainNames = new List <string>(); if (domainNames != null) { foreach (var domainName in domainNames) { if (!String.IsNullOrWhiteSpace(domainName)) { string ipAddress = Opc.Ua.Utils.NormalizedIPAddress(domainName); if (!String.IsNullOrEmpty(ipAddress)) { discoveryUrlDomainNames.Add(ipAddress); } else { discoveryUrlDomainNames.Add(domainName); } } } } else { discoveryUrlDomainNames = new List <string>(); } if (application.DiscoveryUrls != null) { foreach (var discoveryUrl in application.DiscoveryUrls) { Uri url = Opc.Ua.Utils.ParseUri(discoveryUrl); if (url == null) { continue; } string domainName = url.DnsSafeHost; if (url.HostNameType != UriHostNameType.Dns) { domainName = Opc.Ua.Utils.NormalizedIPAddress(domainName); } if (!Opc.Ua.Utils.FindStringIgnoreCase(discoveryUrlDomainNames, domainName)) { discoveryUrlDomainNames.Add(domainName); } } } request.ID = _certRequestIdCounter++; request.CertificateRequestState = (int)CertificateRequestState.New; request.CertificateGroupId = certificateGroupId; request.CertificateTypeId = certificateTypeId; request.SubjectName = subjectName; request.DomainNames = discoveryUrlDomainNames.ToArray(); request.PrivateKeyFormat = privateKeyFormat; request.PrivateKeyPassword = privateKeyPassword; request.SigningRequest = null; request.ApplicationId = application.ApplicationId.ToString(); request.RequestTime = DateTime.UtcNow; bool retry; do { retry = false; try { var result = await _certificateRequests.CreateAsync(request); } catch (DocumentClientException dce) { if (dce.StatusCode == System.Net.HttpStatusCode.Conflict) { // retry with new guid and id request.RequestId = Guid.NewGuid(); _certRequestIdCounter = await GetMaxCertIDAsync(); request.ID = _certRequestIdCounter++; retry = true; } } } while (retry); return(request.RequestId.ToString()); }