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); } }
/// <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); }
/// <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); }
/// <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)); }
/// <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"); } } }