/// <inheritdoc/> public async Task AcceptRequestAsync(string requestId, VaultOperationContextModel context, CancellationToken ct) { var result = await _repo.UpdateAsync(requestId, request => { switch (request.Record.State) { case CertificateRequestState.New: case CertificateRequestState.Approved: case CertificateRequestState.Rejected: case CertificateRequestState.Failure: case CertificateRequestState.Completed: request.Record.State = CertificateRequestState.Accepted; request.Record.Accepted = context.Validate(); return(true); case CertificateRequestState.Accepted: return(false); default: throw new ResourceInvalidStateException( $"Request in the wrong state ({request.Record.State}."); } }, ct); await _broker.NotifyAllAsync( l => l.OnCertificateRequestAcceptedAsync(result)); }
/// <inheritdoc/> public async Task <FinishNewKeyPairRequestResultModel> FinishNewKeyPairRequestAsync( string requestId, VaultOperationContextModel context, CancellationToken ct) { if (string.IsNullOrEmpty(requestId)) { throw new ArgumentNullException(nameof(requestId)); } var request = await _repo.FindAsync(requestId, ct); if (request == null) { throw new ResourceNotFoundException("Request not found"); } try { var entity = await _entities.FindEntityAsync(request.Entity.Id); if (entity != null) { throw new ResourceInvalidStateException("Entity removed."); } var result = new FinishNewKeyPairRequestResultModel { Request = request.Record }; if (request.Record.State == CertificateRequestState.Completed) { result.Certificate = request.Certificate; // get private key if (request.KeyHandle != null) { var handle = Try.Op( () => _serializer.DeserializeHandle(request.KeyHandle)); if (handle != null) { var privateKey = await Try.Async( () => _keys.ExportKeyAsync(handle, ct)); result.PrivateKey = privateKey.ToServiceModel(); await Try.Async( () => _keys.DeleteKeyAsync(handle, ct)); } } } return(result); } finally { if (request.Record.State == CertificateRequestState.Completed) { // Accept await _broker.NotifyAllAsync( l => l.OnCertificateRequestAcceptedAsync(request)); _logger.Information("Key pair response accepted and finished."); } } }
/// <summary> /// Create new context /// </summary> /// <param name="model"></param> public static VaultOperationContextApiModel ToApiModel( this VaultOperationContextModel model) { if (model == null) { return(null); } return(new VaultOperationContextApiModel { Time = model.Time, AuthorityId = model.AuthorityId, }); }
/// <inheritdoc/> public async Task <FinishSigningRequestResultModel> FinishSigningRequestAsync( string requestId, VaultOperationContextModel context, CancellationToken ct) { if (string.IsNullOrEmpty(requestId)) { throw new ArgumentNullException(nameof(requestId)); } var request = await _repo.FindAsync(requestId, ct); if (request == null) { throw new ResourceNotFoundException("Request not found"); } try { var entity = await _entities.FindEntityAsync(request.Entity.Id); if (entity != null) { throw new ResourceInvalidStateException("Entity removed."); } if (request.Record.State == CertificateRequestState.Completed) { return(new FinishSigningRequestResultModel { Request = request.Record, Certificate = request.Certificate }); } return(new FinishSigningRequestResultModel { Request = request.Record }); } finally { if (request.Record.State == CertificateRequestState.Completed) { // Accept await _broker.NotifyAllAsync( l => l.OnCertificateRequestAcceptedAsync(request)); _logger.Information("Signing response accepted and finished."); } } }
/// <inheritdoc/> public async Task DeleteRequestAsync(string requestId, VaultOperationContextModel context, CancellationToken ct) { var result = await _repo.DeleteAsync(requestId, request => { switch (request.Record.State) { case CertificateRequestState.Accepted: return(true); default: if (request.Record.Accepted != null && (request.Record.Accepted.Time + TimeSpan.FromDays(1)) < DateTime.UtcNow) { return(true); } throw new ResourceInvalidStateException( $"Request in the wrong state ({request.Record.State}."); } }, ct); await _broker.NotifyAllAsync( l => l.OnCertificateRequestDeletedAsync(result)); }
/// <summary> /// Create new context /// </summary> /// <param name="model"></param> public VaultOperationContextApiModel(VaultOperationContextModel model) { Time = model.Time; AuthorityId = model.AuthorityId; }
/// <inheritdoc/> public async Task <StartSigningRequestResultModel> StartSigningRequestAsync( StartSigningRequestModel request, VaultOperationContextModel context, CancellationToken ct) { if (request == null) { throw new ArgumentNullException(nameof(request)); } if (string.IsNullOrEmpty(request.EntityId)) { throw new ArgumentNullException(nameof(request.EntityId)); } if (string.IsNullOrEmpty(request.GroupId)) { throw new ArgumentNullException(nameof(request.GroupId)); } var entity = await _entities.FindEntityAsync(request.EntityId); if (entity == null) { throw new ResourceNotFoundException("Entity not found"); } // Validate signing request and update entity information var signingRequest = request.ToRawData(); var info = signingRequest.ToCertificationRequest(); var altNames = info.Extensions? .OfType <X509SubjectAltNameExtension>() .SingleOrDefault(); if (!(entity.Uris?.All( u => altNames?.Uris?.Any(x => u.EqualsIgnoreCase(x)) ?? false) ?? true)) { throw new ArgumentException( "Signing Request's alternative names does not include entity's uris"); } var domainNames = new HashSet <string>(entity.Addresses ?? new List <string>()); if (altNames?.DomainNames != null) { foreach (var name in altNames.DomainNames) { domainNames.Add(name); } } if (altNames?.IPAddresses != null) { foreach (var name in altNames.IPAddresses) { domainNames.Add(name); } } var uris = new HashSet <string>(entity.Uris ?? new List <string>()); if (altNames?.Uris != null) { foreach (var name in altNames.Uris) { uris.Add(name); } } entity.Addresses = domainNames.ToList(); entity.Uris = uris.ToList(); entity.SubjectName = info.Subject.Name; var result = await _repo.AddAsync(new CertificateRequestModel { Record = new CertificateRequestRecordModel { Type = CertificateRequestType.KeyPairRequest, EntityId = entity.Id, GroupId = request.GroupId, Submitted = context.Validate(), }, Entity = entity.Validate(), SigningRequest = signingRequest }, ct); await _broker.NotifyAllAsync( l => l.OnCertificateRequestSubmittedAsync(result)); _logger.Information("New signing request submitted."); return(new StartSigningRequestResultModel { RequestId = result.Record.RequestId }); }
/// <inheritdoc/> public async Task<StartNewKeyPairRequestResultModel> StartNewKeyPairRequestAsync( StartNewKeyPairRequestModel request, VaultOperationContextModel context, CancellationToken ct) { if (request == null) { throw new ArgumentNullException(nameof(request)); } if (string.IsNullOrEmpty(request.EntityId)) { throw new ArgumentNullException(nameof(request.EntityId)); } if (string.IsNullOrEmpty(request.GroupId)) { throw new ArgumentNullException(nameof(request.GroupId)); } if (string.IsNullOrEmpty(request.SubjectName)) { throw new ArgumentNullException(nameof(request.SubjectName)); } // Get entity var entity = await _entities.FindEntityAsync(request.EntityId); if (entity == null) { throw new ResourceNotFoundException("Entity not found"); } // Validate subject name var subjectList = Opc.Ua.Utils.ParseDistinguishedName(request.SubjectName); if (subjectList == null || subjectList.Count == 0) { throw new ArgumentException("Invalid Subject", nameof(request.SubjectName)); } if (!subjectList.Any(c => c.StartsWith("CN=", StringComparison.InvariantCulture))) { throw new ArgumentException("Invalid Subject, must have a common name (CN=).", nameof(request.SubjectName)); } entity.SubjectName = string.Join(", ", subjectList); // Add domain names if (request.DomainNames != null) { if (entity.Addresses == null) { entity.Addresses = request.DomainNames; } else { entity.Addresses.AddRange(request.DomainNames); } } var result = await _repo.AddAsync(new CertificateRequestModel { Record = new CertificateRequestRecordModel { Type = CertificateRequestType.KeyPairRequest, EntityId = entity.Id, GroupId = request.GroupId, Submitted = context.Validate(), }, Entity = entity }, ct); await _broker.NotifyAllAsync( l => l.OnCertificateRequestSubmittedAsync(result)); _logger.Information("New Key pair request submitted."); return new StartNewKeyPairRequestResultModel { RequestId = result.Record.RequestId }; }