Пример #1
0
        /// <summary>
        /// Sends a secure error reply to the envelope.
        /// </summary>
        /// <param name="envelope">The envelope.</param>
        /// <param name="errorMsg">The error message.</param>
        /// <returns></returns>
        private async Task ReplyErrorAsync(Envelope envelope, SecureErrorMsg errorMsg)
        {
            using (MemoryStream ms = new MemoryStream()) {
                Serializer.Serialize(ms, errorMsg);

                // reply
                await envelope.ReplyAsync(ms.ToArray(), new Dictionary <string, object>() {
                    { SecureHeader.HEADER_NAME, new SecureHeader(SecureHeader.HEADER_VERSION, SecureMessageType.Error).ToString() }
                });
            }
        }
Пример #2
0
        /// <summary>
        /// Performs the secure handshake so we can start sending messages.
        /// </summary>
        /// <returns></returns>
        private async Task HandshakeAsync()
        {
            // wait on semaphore
            await _handshakeSemaphore.WaitAsync();

            // check if we got raced
            if (_serverEncryptionKey != null && !SecureUtils.HasTimeSlotExpired(_serverEncryptionKeyTimeSlot, false))
            {
                return;
            }

            try {
                // create client key
                GenerateHandshakeKey();

                // get certificate if we don't have it yet
                if (_serverCertificate == null)
                {
                    // log
#if DEBUG_SECURE
                    Console.WriteLine($"[Secure] Handshake Requesting certificate...");
#endif

                    // request certificate
                    SecureHeader requestCertificate = new SecureHeader(SecureHeader.HeaderVersion, SecureMessageType.RequestCertificate);

                    // send request
                    Envelope respondCert = await _node.AskAsync(_address, new byte[0], _configuration.HandshakeTimeout, new Dictionary <string, object>() {
                        { SecureHeader.HeaderName, requestCertificate.ToString() }
                    });

                    // parse response header
                    SecureHeader respondCertHeader = null;

                    try {
                        respondCertHeader = new SecureHeader(Encoding.UTF8.GetString(respondCert.Headers[SecureHeader.HeaderName] as byte[]));
                    } catch (Exception ex) {
                        throw new InvalidDataException("The certificate request response header was invalid", ex);
                    }

                    // check if the certificate response is an error or if it's an incorrect type
                    if (respondCertHeader.Type == SecureMessageType.Error)
                    {
                        SecureErrorMsg errorMsg = respondCert.AsProtoBuf <SecureErrorMsg>();

                        throw new SecurityException($"{errorMsg.Message} ({errorMsg.Code})");
                    }
                    else if (respondCertHeader.Type != SecureMessageType.RespondCertificate)
                    {
                        throw new InvalidDataException("The certificate request response header was invalid");
                    }

                    // decode the certificate response and then check it has actual data
                    SecureRespondCertificateMsg respondCertMsg = respondCert.AsProtoBuf <SecureRespondCertificateMsg>();

                    if (respondCertMsg.CertificateData == null)
                    {
                        throw new InvalidDataException("The certificate request response was invalid");
                    }

                    // load certificate from response
                    X509Certificate2 cert = new X509Certificate2(respondCertMsg.CertificateData);

                    // validate it's allowed to act as this service
                    if (_configuration.ValidateAddress)
                    {
                        // check extension is actually there
                        if (cert.Extensions["HolonSecureServices"] == null)
                        {
                            throw new InvalidDataException("The service certificate has no claim for the invoked operation");
                        }

                        // parse extension
                        X509Extension servicesExt = cert.Extensions["HolonSecureServices"];
                    }
                    else
                    {
#if DEBUG_SECURE
                        Console.WriteLine($"[Secure] Handshake WARNING: Not validating services due to configuration!");
#endif
                    }

                    // validate that it's signed by ca authority
                    if (_configuration.ValidateAuthority)
                    {
                        X509Chain chain = new X509Chain();
                        chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
                        chain.ChainPolicy.ExtraStore.Add(_configuration.RootAuthority);
                        chain.Build(cert);

                        // get status
                        X509ChainStatus status = chain.ChainStatus.First();

                        if (status.Status != X509ChainStatusFlags.UntrustedRoot && status.Status != X509ChainStatusFlags.NoError)
                        {
                            throw new InvalidDataException("The service certificate is not signed by the root authority");
                        }
                    }
                    else
                    {
#if DEBUG_SECURE
                        Console.WriteLine($"[Secure] Handshake WARNING: Not validating authority due to configuration!");
#endif
                    }

                    // log
#if DEBUG_SECURE
                    Console.WriteLine($"[Secure] Handshake Got certifcate {cert.Subject} ({cert.Thumbprint})");
#endif

                    _serverCertificate = cert;
                }

                // log
#if DEBUG_SECURE
                Console.WriteLine($"[Secure] Handshake Requesting key... FirstTime: {_serverEncryptionKey == null}");
#endif

                // key request
                SecureRequestKeyMsg requestKeyMsg = new SecureRequestKeyMsg()
                {
                    HandshakeIV  = _handshakeEncryptionIV,
                    HandshakeKey = _handshakeEncryptionKey
                };
                SecureHeader requestKey = new SecureHeader(SecureHeader.HeaderVersion, SecureMessageType.RequestKey);

                // send request
                Envelope respondKey = null;

                using (RSA rsa = _serverCertificate.GetRSAPublicKey()) {
                    using (MemoryStream ms = new MemoryStream()) {
                        // serialize to stream
                        Serializer.Serialize(ms, requestKeyMsg);

                        // encrypt with server certificate
                        byte[] keyRequestBody = rsa.Encrypt(ms.ToArray(), RSAEncryptionPadding.Pkcs1);

                        respondKey = await _node.AskAsync(_address, keyRequestBody, _configuration.HandshakeTimeout, new Dictionary <string, object>() {
                            { SecureHeader.HeaderName, requestKey.ToString() }
                        });
                    }
                }

                // parse response
                SecureHeader respondKeyHeader = null;

                try {
                    respondKeyHeader = new SecureHeader(Encoding.UTF8.GetString(respondKey.Headers[SecureHeader.HeaderName] as byte[]));
                } catch (Exception ex) {
                    throw new InvalidDataException("The key request response header was invalid", ex);
                }

                // check if the key response is an error or if it's an incorrect type
                if (respondKeyHeader.Type == SecureMessageType.Error)
                {
                    SecureErrorMsg errorMsg = respondKey.AsProtoBuf <SecureErrorMsg>();

                    throw new SecurityException($"{errorMsg.Message} ({errorMsg.Code})");
                }
                else if (respondKeyHeader.Type != SecureMessageType.RespondKey)
                {
                    throw new InvalidDataException("The key request response header was invalid");
                }

                // try and decrypt
                using (MemoryStream decryptedStream = new MemoryStream()) {
                    using (Aes aes = Aes.Create()) {
                        aes.Key = _handshakeEncryptionKey;
                        aes.IV  = _handshakeEncryptionIV;

                        using (CryptoStream decryptStream = new CryptoStream(respondKey.AsStream(), aes.CreateDecryptor(), CryptoStreamMode.Read)) {
                            decryptStream.CopyTo(decryptedStream);
                        }
                    }

                    // seek to beginning
                    decryptedStream.Seek(0, SeekOrigin.Begin);

                    // deserialize
                    SecureRespondKeyMsg respondKeyMsg = Serializer.Deserialize <SecureRespondKeyMsg>(decryptedStream);

                    // validate key
                    if (respondKeyMsg.ServerKey == null || respondKeyMsg.ServerNonce == null || respondKeyMsg.ServerKey.Length != 16)
                    {
                        throw new InvalidDataException("The secure key is invalid");
                    }

                    // log
#if DEBUG_SECURE
                    Console.WriteLine($"[Secure] Handshake Got key Timeslot: {respondKeyMsg.KeyTimeSlot} Nonce: {BitConverter.ToString(respondKeyMsg.ServerNonce).Replace("-", "")}");
#endif

                    // set server encryption key
                    _serverEncryptionKey         = respondKeyMsg.ServerKey;
                    _serverNonce                 = respondKeyMsg.ServerNonce;
                    _serverEncryptionKeyTimeSlot = respondKeyMsg.KeyTimeSlot;
                }
            } finally {
                // make sure to release semaphore no matter what
                _handshakeSemaphore.Release();
            }
        }
Пример #3
0
        /// <summary>
        /// Sends the envelope message to the provided service address and waits for a response.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="timeout">The timeout.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns></returns>
        public async Task <Envelope> AskAsync(Message message, TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken))
        {
            // perform handshake if we don't have our key yet or it has expired
            if (_serverEncryptionKey == null || SecureUtils.HasTimeSlotExpired(_serverEncryptionKeyTimeSlot, false))
            {
#if DEBUG_SECURE
                Console.WriteLine($"[Secure] InvokeOperation KeyNull: {_serverEncryptionKey == null} Expired: {SecureUtils.HasTimeSlotExpired(_serverEncryptionKeyTimeSlot, false)}");
#endif

                // perform handshake (partial if required)
                await HandshakeAsync();
            }

            // add secure header
            if (message.Headers == null)
            {
                message.Headers = new Dictionary <string, object>(StringComparer.CurrentCultureIgnoreCase);
            }

            message.Headers[SecureHeader.HeaderName] = new SecureHeader(SecureHeader.HeaderVersion, SecureMessageType.RequestMessage).ToString();
            message.Address = _address;
            message.Body    = EncryptBody(message.Body);

            // send the envelope and wait for the response
            Envelope response = await _node.AskAsync(message, timeout, cancellationToken);

            if (!response.Headers.ContainsKey(SecureHeader.HeaderName))
            {
                throw new InvalidDataException("The secure service sent an invalid response");
            }

            // decode header and take action depending on the response
            SecureHeader header = new SecureHeader(Encoding.UTF8.GetString(response.Headers[SecureHeader.HeaderName] as byte[]));

            if (header.Type == SecureMessageType.RespondMessage)
            {
                using (MemoryStream outputStream = new MemoryStream()) {
                    // decrypt
                    using (MemoryStream inputStream = new MemoryStream(response.Body)) {
                        using (Aes aes = Aes.Create()) {
                            aes.Key = _serverEncryptionKey;
                            aes.IV  = _serverNonce;

                            using (CryptoStream decryptStream = new CryptoStream(inputStream, aes.CreateDecryptor(), CryptoStreamMode.Read)) {
                                decryptStream.CopyTo(outputStream);
                            }
                        }
                    }

                    response.Body = outputStream.ToArray();
                    return(response);
                }
            }
            else if (header.Type == SecureMessageType.Error)
            {
                // deserialize
                SecureErrorMsg errorMsg = null;

                try {
                    errorMsg = response.AsProtoBuf <SecureErrorMsg>();
                } catch (Exception ex) {
                    throw new InvalidDataException("The secure service sent an invalid error respsonse", ex);
                }

                throw new SecurityException($"{errorMsg.Message} ({errorMsg.Code})");
            }
            else
            {
                throw new InvalidDataException($"The secure service sent an invalid response ({header.Type})");
            }
        }