Example #1
0
        public virtual async Task Init()
        {
            Utils.LogInfo("InitializeCertificateGroup: {0}", SubjectName);

            using (ICertificateStore store = CertificateStoreIdentifier.OpenStore(AuthoritiesStorePath))
            {
                X509Certificate2Collection certificates = await store.Enumerate().ConfigureAwait(false);

                foreach (var certificate in certificates)
                {
                    if (X509Utils.CompareDistinguishedName(certificate.Subject, SubjectName))
                    {
                        if (X509Utils.GetRSAPublicKeySize(certificate) != Configuration.CACertificateKeySize)
                        {
                            continue;
                        }
                        // TODO check hash size

                        if (Certificate != null)
                        {
                            // always use latest issued cert in store
                            if (certificate.NotBefore > DateTime.UtcNow ||
                                Certificate.NotBefore > certificate.NotBefore)
                            {
                                continue;
                            }
                        }
                        Certificate = certificate;
                    }
                }
            }

            if (Certificate == null)
            {
                Utils.LogInfo(Utils.TraceMasks.Security,
                              "Create new CA Certificate: {0}, KeySize: {1}, HashSize: {2}, LifeTime: {3} months",
                              SubjectName,
                              Configuration.CACertificateKeySize,
                              Configuration.CACertificateHashSize,
                              Configuration.CACertificateLifetime
                              );
                X509Certificate2 newCertificate = await CreateCACertificateAsync(SubjectName).ConfigureAwait(false);

                Certificate = new X509Certificate2(newCertificate.RawData);
                Utils.LogCertificate(Utils.TraceMasks.Security, "Created CA certificate: ", Certificate);
            }
        }
Example #2
0
        /// <summary>
        /// Creates an application instance certificate if one does not already exist.
        /// </summary>
        private static async Task <bool> CheckApplicationInstanceCertificate(
            ApplicationConfiguration configuration,
            X509Certificate2 certificate,
            bool silent,
            ushort minimumKeySize)
        {
            if (certificate == null)
            {
                return(false);
            }

            Utils.Trace(Utils.TraceMasks.Information, "Checking application instance certificate. {0}", certificate.Subject);

            try
            {
                // validate certificate.
                configuration.CertificateValidator.Validate(certificate);
            }
            catch (Exception ex)
            {
                string message = Utils.Format(
                    "Error validating certificate. Exception: {0}. Use certificate anyway?", ex.Message);
                if (!await ApproveMessage(message, silent))
                {
                    return(false);
                }
            }

            // check key size.
            int keySize = X509Utils.GetRSAPublicKeySize(certificate);

            if (minimumKeySize > keySize)
            {
                string message = Utils.Format(
                    "The key size ({0}) in the certificate is less than the minimum provided ({1}). Use certificate anyway?",
                    keySize,
                    minimumKeySize);

                if (!await ApproveMessage(message, silent))
                {
                    return(false);
                }
            }

            // check domains.
            if (configuration.ApplicationType != ApplicationType.Client)
            {
                if (!await CheckDomainsInCertificate(configuration, certificate, silent))
                {
                    return(false);
                }
            }

            // check uri.
            string applicationUri = X509Utils.GetApplicationUriFromCertificate(certificate);

            if (String.IsNullOrEmpty(applicationUri))
            {
                string message = "The Application URI could not be read from the certificate. Use certificate anyway?";
                if (!await ApproveMessage(message, silent))
                {
                    return(false);
                }
            }
            else
            {
                configuration.ApplicationUri = applicationUri;
            }

            // update configuration.
            configuration.SecurityConfiguration.ApplicationCertificate.Certificate = certificate;

            return(true);
        }
Example #3
0
        /// <summary>
        /// Creates an application instance certificate if one does not already exist.
        /// </summary>
        private async Task <bool> CheckApplicationInstanceCertificate(
            ApplicationConfiguration configuration,
            X509Certificate2 certificate,
            bool silent,
            ushort minimumKeySize)
        {
            if (certificate == null)
            {
                return(false);
            }

            // set suppressible errors
            var certValidator = new CertValidationSuppressibleStatusCodes(
                new StatusCode[] {
                StatusCodes.BadCertificateUntrusted,
                StatusCodes.BadCertificateTimeInvalid,
                StatusCodes.BadCertificateHostNameInvalid,
                StatusCodes.BadCertificateRevocationUnknown,
                StatusCodes.BadCertificateIssuerRevocationUnknown,
            });

            Utils.LogCertificate("Check application instance certificate.", certificate);

            try
            {
                // validate certificate.
                configuration.CertificateValidator.CertificateValidation += certValidator.OnCertificateValidation;
                configuration.CertificateValidator.Validate(certificate.HasPrivateKey ? new X509Certificate2(certificate.RawData) : certificate);
            }
            catch (Exception ex)
            {
                string message = Utils.Format(
                    "Error validating certificate. Exception: {0}. Use certificate anyway?", ex.Message);
                if (!await ApproveMessage(message, silent).ConfigureAwait(false))
                {
                    return(false);
                }
            }
            finally
            {
                configuration.CertificateValidator.CertificateValidation -= certValidator.OnCertificateValidation;
            }

            // check key size.
            int keySize = X509Utils.GetRSAPublicKeySize(certificate);

            if (minimumKeySize > keySize)
            {
                string message = Utils.Format(
                    "The key size ({0}) in the certificate is less than the minimum allowed ({1}). Use certificate anyway?",
                    keySize,
                    minimumKeySize);

                if (!await ApproveMessage(message, silent).ConfigureAwait(false))
                {
                    return(false);
                }
            }

            // check domains.
            if (configuration.ApplicationType != ApplicationType.Client)
            {
                if (!await CheckDomainsInCertificate(configuration, certificate, silent).ConfigureAwait(false))
                {
                    return(false);
                }
            }

            // check uri.
            string applicationUri = X509Utils.GetApplicationUriFromCertificate(certificate);

            if (String.IsNullOrEmpty(applicationUri))
            {
                string message = "The Application URI could not be read from the certificate. Use certificate anyway?";
                if (!await ApproveMessage(message, silent).ConfigureAwait(false))
                {
                    return(false);
                }
            }
            else if (!configuration.ApplicationUri.Equals(applicationUri, StringComparison.InvariantCulture))
            {
                Utils.LogInfo("Updated the ApplicationUri: {0} --> {1}", configuration.ApplicationUri, applicationUri);
                configuration.ApplicationUri = applicationUri;
            }

            Utils.LogInfo("Using the ApplicationUri: {0}", applicationUri);

            // update configuration.
            configuration.SecurityConfiguration.ApplicationCertificate.Certificate = certificate;

            return(true);
        }
Example #4
0
        /// <summary>
        /// Processes an OpenSecureChannel request message.
        /// </summary>
        protected ArraySegment <byte> ReadAsymmetricMessage(
            ArraySegment <byte> buffer,
            X509Certificate2 receiverCertificate,
            out uint channelId,
            out X509Certificate2 senderCertificate,
            out uint requestId,
            out uint sequenceNumber)
        {
            BinaryDecoder decoder = new BinaryDecoder(buffer.Array, buffer.Offset, buffer.Count, Quotas.MessageContext);

            string securityPolicyUri = null;
            X509Certificate2Collection senderCertificateChain;

            // parse the security header.
            ReadAsymmetricMessageHeader(
                decoder,
                receiverCertificate,
                out channelId,
                out senderCertificateChain,
                out securityPolicyUri);

            if (senderCertificateChain != null && senderCertificateChain.Count > 0)
            {
                senderCertificate = senderCertificateChain[0];
            }
            else
            {
                senderCertificate = null;
            }

            // validate the sender certificate.
            if (senderCertificate != null && Quotas.CertificateValidator != null && securityPolicyUri != SecurityPolicies.None)
            {
                CertificateValidator certificateValidator = Quotas.CertificateValidator as CertificateValidator;

                if (certificateValidator != null)
                {
                    certificateValidator.Validate(senderCertificateChain);
                }
                else
                {
                    Quotas.CertificateValidator.Validate(senderCertificate);
                }
            }

            // check if this is the first open secure channel request.
            if (!m_uninitialized)
            {
                if (securityPolicyUri != m_securityPolicyUri)
                {
                    throw ServiceResultException.Create(StatusCodes.BadSecurityPolicyRejected, "Cannot change the security policy after creating the channnel.");
                }
            }
            else
            {
                // find a matching endpoint description.
                if (m_endpoints != null)
                {
                    foreach (EndpointDescription endpoint in m_endpoints)
                    {
                        // There may be multiple endpoints with the same securityPolicyUri.
                        // Just choose the first one that matches. This choice will be re-examined
                        // When the OpenSecureChannel request body is processed.
                        if (endpoint.SecurityPolicyUri == securityPolicyUri || (securityPolicyUri == SecurityPolicies.None && endpoint.SecurityMode == MessageSecurityMode.None))
                        {
                            m_securityMode      = endpoint.SecurityMode;
                            m_securityPolicyUri = securityPolicyUri;
                            m_discoveryOnly     = false;
                            m_uninitialized     = false;
                            m_selectedEndpoint  = endpoint;

                            // recalculate the key sizes.
                            CalculateSymmetricKeySizes();
                            break;
                        }
                    }
                }

                // allow a discovery only channel with no security if policy not suppported
                if (m_uninitialized)
                {
                    if (securityPolicyUri != SecurityPolicies.None)
                    {
                        throw ServiceResultException.Create(StatusCodes.BadSecurityPolicyRejected, "The security policy is not supported.");
                    }

                    m_securityMode      = MessageSecurityMode.None;
                    m_securityPolicyUri = SecurityPolicies.None;
                    m_discoveryOnly     = true;
                    m_uninitialized     = false;
                    m_selectedEndpoint  = null;
                }
            }

            int headerSize = decoder.Position;

            // decrypt the body.
            ArraySegment <byte> plainText = Decrypt(
                new ArraySegment <byte>(buffer.Array, buffer.Offset + headerSize, buffer.Count - headerSize),
                new ArraySegment <byte>(buffer.Array, buffer.Offset, headerSize),
                receiverCertificate);

            // extract signature.
            int signatureSize = GetAsymmetricSignatureSize(senderCertificate);

            byte[] signature = new byte[signatureSize];

            for (int ii = 0; ii < signatureSize; ii++)
            {
                signature[ii] = plainText.Array[plainText.Offset + plainText.Count - signatureSize + ii];
            }

            // verify the signature.
            ArraySegment <byte> dataToVerify = new ArraySegment <byte>(plainText.Array, plainText.Offset, plainText.Count - signatureSize);

            if (!Verify(dataToVerify, signature, senderCertificate))
            {
                Utils.Trace("Could not verify signature on message.");
                throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "Could not verify the signature on the message.");
            }

            // verify padding.
            int paddingCount = 0;

            if (SecurityMode != MessageSecurityMode.None)
            {
                int paddingEnd = -1;
                if (X509Utils.GetRSAPublicKeySize(receiverCertificate) > TcpMessageLimits.KeySizeExtraPadding)
                {
                    paddingEnd   = plainText.Offset + plainText.Count - signatureSize - 1;
                    paddingCount = plainText.Array[paddingEnd - 1] + plainText.Array[paddingEnd] * 256;

                    //parse until paddingStart-1; the last one is actually the extrapaddingsize
                    for (int ii = paddingEnd - paddingCount; ii < paddingEnd; ii++)
                    {
                        if (plainText.Array[ii] != plainText.Array[paddingEnd - 1])
                        {
                            throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "Could not verify the padding in the message.");
                        }
                    }
                }
                else
                {
                    paddingEnd   = plainText.Offset + plainText.Count - signatureSize - 1;
                    paddingCount = plainText.Array[paddingEnd];

                    for (int ii = paddingEnd - paddingCount; ii < paddingEnd; ii++)
                    {
                        if (plainText.Array[ii] != plainText.Array[paddingEnd])
                        {
                            throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "Could not verify the padding in the message.");
                        }
                    }
                }

                paddingCount++;
            }

            // decode message.
            decoder = new BinaryDecoder(
                plainText.Array,
                plainText.Offset + headerSize,
                plainText.Count - headerSize,
                Quotas.MessageContext);

            sequenceNumber = decoder.ReadUInt32(null);
            requestId      = decoder.ReadUInt32(null);

            headerSize += decoder.Position;
            decoder.Close();

            Utils.Trace("Security Policy: {0}", SecurityPolicyUri);
            Utils.Trace("Sender Certificate: {0}", (senderCertificate != null) ? senderCertificate.Subject : "(none)");

            // return the body.
            return(new ArraySegment <byte>(
                       plainText.Array,
                       plainText.Offset + headerSize,
                       plainText.Count - headerSize - signatureSize - paddingCount));
        }
Example #5
0
        /// <summary>
        /// Sends a OpenSecureChannel request.
        /// </summary>
        protected BufferCollection WriteAsymmetricMessage(
            uint messageType,
            uint requestId,
            X509Certificate2 senderCertificate,
            X509Certificate2Collection senderCertificateChain,
            X509Certificate2 receiverCertificate,
            ArraySegment <byte> messageBody)
        {
            bool             success      = false;
            BufferCollection chunksToSend = new BufferCollection();

            byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "WriteAsymmetricMessage");

            try
            {
                BinaryEncoder encoder    = new BinaryEncoder(buffer, 0, SendBufferSize, Quotas.MessageContext);
                int           headerSize = 0;

                if (senderCertificateChain != null && senderCertificateChain.Count > 0)
                {
                    int senderCertificateSize = 0;

                    WriteAsymmetricMessageHeader(
                        encoder,
                        messageType | TcpMessageType.Intermediate,
                        ChannelId,
                        SecurityPolicyUri,
                        senderCertificate,
                        senderCertificateChain,
                        receiverCertificate,
                        out senderCertificateSize);

                    headerSize = GetAsymmetricHeaderSize(SecurityPolicyUri, senderCertificate, senderCertificateSize);
                }
                else
                {
                    WriteAsymmetricMessageHeader(
                        encoder,
                        messageType | TcpMessageType.Intermediate,
                        ChannelId,
                        SecurityPolicyUri,
                        senderCertificate,
                        receiverCertificate);

                    headerSize = GetAsymmetricHeaderSize(SecurityPolicyUri, senderCertificate);
                }

                int signatureSize = GetAsymmetricSignatureSize(senderCertificate);

                // save the header.
                ArraySegment <byte> header = new ArraySegment <byte>(buffer, 0, headerSize);

                // calculate the space available.
                int plainTextBlockSize  = GetPlainTextBlockSize(receiverCertificate);
                int cipherTextBlockSize = GetCipherTextBlockSize(receiverCertificate);
                int maxCipherTextSize   = SendBufferSize - headerSize;
                int maxCipherBlocks     = maxCipherTextSize / cipherTextBlockSize;
                int maxPlainTextSize    = maxCipherBlocks * plainTextBlockSize;
                int maxPayloadSize      = maxPlainTextSize - signatureSize - 1 - TcpMessageLimits.SequenceHeaderSize;

                int bytesToWrite = messageBody.Count;
                int startOfBytes = messageBody.Offset;

                while (bytesToWrite > 0)
                {
                    encoder.WriteUInt32(null, GetNewSequenceNumber());
                    encoder.WriteUInt32(null, requestId);

                    int payloadSize = bytesToWrite;

                    if (payloadSize > maxPayloadSize)
                    {
                        payloadSize = maxPayloadSize;
                    }
                    else
                    {
                        UpdateMessageType(buffer, 0, messageType | TcpMessageType.Final);
                    }

                    // write the message body.
                    encoder.WriteRawBytes(messageBody.Array, messageBody.Offset + startOfBytes, payloadSize);

                    // calculate the amount of plain text to encrypt.
                    int plainTextSize = encoder.Position - headerSize + signatureSize;

                    // calculate the padding.
                    int padding = 0;

                    if (SecurityMode != MessageSecurityMode.None)
                    {
                        if (X509Utils.GetRSAPublicKeySize(receiverCertificate) <= TcpMessageLimits.KeySizeExtraPadding)
                        {
                            // need to reserve one byte for the padding.
                            plainTextSize++;

                            if (plainTextSize % plainTextBlockSize != 0)
                            {
                                padding = plainTextBlockSize - (plainTextSize % plainTextBlockSize);
                            }

                            encoder.WriteByte(null, (byte)padding);
                            for (int ii = 0; ii < padding; ii++)
                            {
                                encoder.WriteByte(null, (byte)padding);
                            }
                        }
                        else
                        {
                            // need to reserve one byte for the padding.
                            plainTextSize++;
                            // need to reserve one byte for the extrapadding.
                            plainTextSize++;

                            if (plainTextSize % plainTextBlockSize != 0)
                            {
                                padding = plainTextBlockSize - (plainTextSize % plainTextBlockSize);
                            }

                            byte paddingSize      = (byte)(padding & 0xff);
                            byte extraPaddingByte = (byte)((padding >> 8) & 0xff);

                            encoder.WriteByte(null, paddingSize);
                            for (int ii = 0; ii < padding; ii++)
                            {
                                encoder.WriteByte(null, (byte)paddingSize);
                            }
                            encoder.WriteByte(null, extraPaddingByte);
                        }

                        // update the plaintext size with the padding size.
                        plainTextSize += padding;
                    }

                    // calculate the number of block to encrypt.
                    int encryptedBlocks = plainTextSize / plainTextBlockSize;

                    // calculate the size of the encrypted data.
                    int cipherTextSize = encryptedBlocks * cipherTextBlockSize;

                    // put the message size after encryption into the header.
                    UpdateMessageSize(buffer, 0, cipherTextSize + headerSize);

                    // write the signature.
                    byte[] signature = Sign(new ArraySegment <byte>(buffer, 0, encoder.Position), senderCertificate);

                    if (signature != null)
                    {
                        encoder.WriteRawBytes(signature, 0, signature.Length);
                    }

                    int messageSize = encoder.Close();

                    // encrypt the data.
                    ArraySegment <byte> encryptedBuffer = Encrypt(
                        new ArraySegment <byte>(buffer, headerSize, messageSize - headerSize),
                        header,
                        receiverCertificate);

                    // check for math errors due to code bugs.
                    if (encryptedBuffer.Count != cipherTextSize + headerSize)
                    {
                        throw new InvalidDataException("Actual message size is not the same as the predicted message size.");
                    }

                    // save chunk.
                    chunksToSend.Add(encryptedBuffer);

                    bytesToWrite -= payloadSize;
                    startOfBytes += payloadSize;

                    // reset the encoder to write the plaintext for the next chunk into the same buffer.
                    if (bytesToWrite > 0)
                    {
                        MemoryStream ostrm = new MemoryStream(buffer, 0, SendBufferSize);
                        ostrm.Seek(header.Count, SeekOrigin.Current);
                        encoder = new BinaryEncoder(ostrm, Quotas.MessageContext);
                    }
                }

                // ensure the buffers don't get clean up on exit.
                success = true;
                return(chunksToSend);
            }
            catch (Exception ex)
            {
                throw new Exception("Could not write async message", ex);
            }
            finally
            {
                BufferManager.ReturnBuffer(buffer, "WriteAsymmetricMessage");

                if (!success)
                {
                    chunksToSend.Release(BufferManager, "WriteAsymmetricMessage");
                }
            }
        }