/// <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));
        }
Example #6
0
 /// <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
            };
        }