SChannel
O pacote de segurança Secure Channel (Schannel), cujo identificador de serviço de autenticação é RPC_C_AUTHN_GSS_SCHANNEL, oferece suporte aos seguintes protocolos baseados em chave pública: SSL (Secure Sockets Layer) versões 2.0 e 3.0, Transport Layer Security (TLS) 1.0 e Private Communication Technology (PCT) 1.0. TLS 1.0 é uma versão padronizada e ligeiramente modificada do SSL 3.0 que foi emitida pela Internet Engineering Task Force (IETF) em janeiro de 1999, no documento RFC 2246. Como o TLS foi padronizado, os desenvolvedores são incentivados a usar TLS em vez de SSL. O PCT está incluído apenas para compatibilidade com versões anteriores e não deve ser usado para novos desenvolvimentos. Quando o pacote de segurança Schannel é usado, o DCOM negocia automaticamente o melhor protocolo, dependendo dos recursos do cliente e do servidor.
Os tópicos a seguir descrevem brevemente o protocolo TLS e como ele funciona com o DCOM.
- Quando usar o TLS
- Breve visão geral de como o TLS funciona
- Certificados X.509
- Usando TLS em COM
- Tópicos relacionados
Observação
Todas as informações sobre o protocolo TLS nestas seções também se aplicam aos protocolos SSL e PCT.
O TLS é a única opção de segurança disponível quando os servidores precisam provar sua identidade para clientes anônimos. Isso é particularmente importante para sites que desejam participar do comércio eletrônico, pois ajuda a proteger a transmissão de informações confidenciais, como números de cartão de crédito. O TLS garante que os clientes de comércio eletrônico possam ter certeza de com quem estão fazendo negócios porque recebem provas da identidade do servidor. Ele também dá ao servidor de comércio eletrônico a eficiência de não ter que se preocupar em autenticar a identidade de cada um de seus clientes.
O TLS exige que todos os servidores provem sua identidade aos clientes. Além disso, o TLS oferece a opção de fazer com que os clientes provem sua identidade aos servidores. Essa autenticação mútua pode ser útil para restringir o acesso de determinadas páginas da Web em uma intranet corporativa grande.
O TLS suporta os níveis de autenticação mais fortes e oferece uma arquitetura aberta que permite que a força da criptografia aumente ao longo do tempo para acompanhar a inovação tecnológica. O TLS é a melhor escolha para ambientes onde o mais alto nível de segurança é desejado para os dados em trânsito.
O TLS é criado com base em uma PKI (infraestrutura de chave pública), que usa pares de chaves pública/privada para habilitar a criptografia de dados e estabelecer a integridade dos dados, e usa certificados X.509 para autenticação.
Muitos protocolos de segurança, como o protocolo Kerberos v5, dependem de uma única chave para criptografar e descriptografar dados. Tais protocolos dependem, portanto, da troca segura de chaves de criptografia; no protocolo Kerberos, isso é feito por meio de tickets obtidos no Centro de Distribuição de Chaves (KDC). Isso requer que todos que usam o protocolo Kerberos sejam registrados no KDC, o que seria uma limitação impraticável para um servidor web de comércio eletrônico que se destina a atrair milhões de clientes de todo o mundo. O TLS, portanto, depende de uma PKI, que usa duas chaves para criptografia de dados - quando uma chave do par criptografa os dados, apenas a outra chave do par pode descriptografá-los. O principal benefício desse projeto é que a criptografia pode ser executada sem a necessidade da troca segura de chaves de criptografia.
Uma PKI usa uma técnica em que uma das chaves é mantida privada e está disponível apenas para a entidade de segurança para quem ela está registrada, enquanto a outra chave é tornada pública para qualquer pessoa acessar. Se alguém quiser enviar uma mensagem privada para o proprietário de um par de chaves, a mensagem pode ser criptografada com a chave pública e somente a chave privada pode ser usada para descriptografar a mensagem.
Os pares de chaves também são usados para verificar a integridade dos dados que estão sendo enviados. Para fazer isso, o proprietário do par de chaves pode anexar uma assinatura digital aos dados antes de enviá-los. Criar uma assinatura digital envolve calcular um hash dos dados e criptografar o hash com a chave privada. Qualquer pessoa que use a chave pública para descriptografar a assinatura digital tem certeza de que a assinatura digital deve ter vindo apenas da pessoa que possui a chave privada. Além disso, o destinatário pode calcular um hash dos dados usando o mesmo algoritmo que o remetente e, se o hash calculado corresponder ao enviado na assinatura digital, o destinatário pode ter certeza de que os dados não foram modificados depois que foram assinados digitalmente.
Uma desvantagem de usar uma PKI para criptografia de dados de alto volume é seu desempenho relativamente lento. Devido à matemática intensiva envolvida, a criptografia e a descriptografia de dados usando uma cifra assimétrica que depende de um par de chaves podem ser até 1.000 vezes mais lentas do que a criptografia e a descriptografia usando uma cifra simétrica que depende apenas de uma única chave. Portanto, o TLS usa uma PKI apenas para gerar assinaturas digitais e para negociar a chave única específica da sessão que será usada pelo cliente e pelo servidor para criptografia e descriptografia de dados em massa. O TLS suporta uma ampla variedade de cifras simétricas de chave única, e cifras adicionais podem ser adicionadas no futuro.
Para obter mais informações sobre o protocolo de handshake TLS, consulte Protocolo de handshake TLS.
Para obter mais detalhes sobre a criptografia por trás do protocolo TLS, consulte Fundamentos de criptografia.
Um problema crítico que deve ser tratado por uma PKI é a capacidade de confiar na autenticidade da chave pública que está sendo usada. Quando você usa uma chave pública emitida para uma empresa com a qual deseja fazer negócios, você quer ter certeza de que a chave realmente pertence à empresa e não a um ladrão que deseja descobrir o número do seu cartão de crédito.
Para garantir a identidade de uma entidade de segurança que detém um par de chaves, a entidade de segurança recebe um certificado X.509 de uma autoridade de certificação (CA). Esse certificado contém informações que identificam a entidade de segurança, contém a chave pública da entidade de segurança e é assinado digitalmente pela autoridade de certificação. Essa assinatura digital indica que a autoridade de certificação acredita que a chave pública contida no certificado realmente pertence à entidade de segurança identificada pelo certificado.
E como você confia no CA? Porque a própria autoridade de certificação possui um certificado X.509 que foi assinado por uma autoridade de certificação de nível superior. Essa cadeia de assinaturas de certificado continua até chegar a uma autoridade de certificação raiz, que é uma autoridade de certificação que assina seus próprios certificados. Se você confiar na integridade da CA raiz de um certificado, poderá confiar na autenticidade do próprio certificado. Portanto, escolher CAs raiz nas quais você está disposto a confiar é um dever importante para um administrador de sistema.
Quando os protocolos de camada de transporte de segurança surgiram, seu principal objetivo era garantir que um cliente estivesse se conectando a um servidor autêntico e ajudar a proteger a privacidade dos dados em trânsito. No entanto, SSL 3.0 e TLS 1.0 também incluem suporte para a transmissão de um certificado de cliente durante o handshake do protocolo. Esse recurso opcional permite a autenticação mútua do cliente e do servidor.
A decisão de usar ou não um certificado de cliente deve ser tomada no contexto do aplicativo. Os certificados de cliente serão desnecessários se o requisito principal for autenticar o servidor. No entanto, se a autenticação do cliente for essencial, o certificado de um cliente poderá ser usado em vez de depender da autenticação personalizada dentro do aplicativo. O uso de certificados de cliente é preferível à autenticação personalizada porque oferece aos usuários um cenário de logon único.
O TLS oferece suporte apenas ao nível de representação (RPC_C_IMP_LEVEL_IMPERSONATE) de representação. Se COM negociar TLS como o serviço de autenticação em um proxy, COM definirá o nível de representação para representar independentemente do padrão do processo. Para que a representação funcione corretamente no TLS, o cliente deve fornecer um certificado X.509 para o servidor e o servidor deve ter esse certificado mapeado para uma conta de usuário específica no servidor. Para obter mais informações, consulte o Guia passo a passo para mapear certificados para contas de usuário.
O TLS não oferece suporte a cloaking. Se um sinalizador de camuflagem e TLS forem especificados em uma chamada CoInitializeSecurity ou IClientSecurity::SetBlanket, E_INVALIDARG será retornado.
O TLS não funciona com o nível de autenticação definido como Nenhum. O handshake entre o cliente e o servidor examina o nível de autenticação definido por cada um e escolhe a configuração de segurança mais alta para a conexão.
Os parâmetros de segurança para TLS podem ser definidos chamando CoInitializeSecurity e CoSetProxyBlanket. As seções a seguir descrevem as nuances envolvidas na realização dessas chamadas.
Se um servidor quiser usar TLS, ele deverá especificar Schannel (RPC_C_AUTHN_GSS_SCHANNEL) como um serviço de autenticação no parâmetro asAuthSvc de CoInitializeSecurity. Para impedir que os clientes se conectem ao servidor usando um serviço de autenticação menos seguro, o servidor deve especificar apenas Schannel como um serviço de autenticação quando chama CoInitializeSecurity. O servidor não pode alterar o cobertor de segurança depois de ter chamado CoInitializeSecurity.
Para usar o TLS, os seguintes parâmetros devem ser especificados quando um servidor chama CoInitializeSecurity:
- pVoid deve ser um ponteiro para um objeto IAccessControl ou um ponteiro para um SECURITY_DESCRIPTOR. Ele não deve ser NULL ou um ponteiro para um AppID.
- cAuthSvc não pode ser 0 ou -1. Os servidores COM nunca escolhem Schannel quando cAuthSvcé -1.
- asAuthSvc deve especificar Schannel como um possível serviço de autenticação. Isso é feito definindo os seguintes parâmetros de SOLE_AUTHENTICATION_SERVICE para o membro Schannel do SOLE_AUTHENTICATION_LIST:
- dwAuthnSvc deve ser RPC_C_AUTHN_GSS_SCHANNEL.
- dwAuthzSvc deve ser RPC_C_AUTHZ_NONE. Atualmente, é ignorado.
- pPrincipalName deve ser um ponteiro para um CERT_CONTEXT, convertido como um ponteiro para OLECHAR, que representa o certificado X.509 do servidor.
- dwAuthnLevel indica o nível mínimo de autenticação que será aceito dos clientes para uma conexão bem-sucedida. Não pode ser RPC_C_AUTHN_LEVEL_NONE.
- dwCapabilities não deve ter o sinalizador EOAC_APPID definido. O sinalizador EOAC_ACCESS_CONTROL deve ser definido se pVoid apontar para um objeto IAccessControl, ele não deve ser definido se pVoid apontar para um SECURITY_DESCRIPTOR. Para outros sinalizadores que podem ser definidos, consulte CoInitializeSecurity.
Para obter mais informações sobre como usar CoInitializeSecurity, consulte Definindo a segurança em todo o processo com CoInitializeSecurity.
Se um cliente quiser usar TLS, ele deverá especificar Schannel (RPC_C_AUTHN_GSS_SCHANNEL) em sua lista de serviços de autenticação no parâmetro pAuthList de CoInitializeSecurity. Se Schannel não for especificado como um possível serviço de autenticação quando CoInitializeSecurity for chamado, uma chamada posterior para CoSetProxyBlanket (ou IClientSecurity::SetBlanket) falhará se ele tentar especificar Schannel como o serviço de autenticação.
Os seguintes parâmetros devem ser especificados quando um cliente chama CoInitializeSecurity:
- dwAuthnLevel especifica o nível de autenticação padrão que o cliente deseja usar. Não pode ser RPC_C_AUTHN_LEVEL_NONE.
- dwImpLevel deve ser RPC_C_IMP_LEVEL_IMPERSONATE.
- pAuthList deve ter os seguintes parâmetros SOLE_AUTHENTICATION_INFO como membro da lista:
- dwAuthnSvc deve ser RPC_C_AUTHN_GSS_SCHANNEL.
- dwAuthzSvc deve ser RPC_C_AUTHZ_NONE.
- pAuthInfo é um ponteiro para um CERT_CONTEXT, convertido como um ponteiro para void, que representa o certificado X.509 do cliente. Se o cliente não tiver um certificado ou não desejar apresentar seu certificado ao servidor, pAuthInfo deverá ser NULL e uma conexão anônima será tentada com o servidor.
- dwCapabilities é um conjunto de sinalizadores que indicam recursos adicionais do cliente. Consulte CoInitializeSecurity para obter informações sobre quais sinalizadores devem ser definidos.
Para obter mais informações sobre como usar CoInitializeSecurity, consulte Definindo a segurança em todo o processo com CoInitializeSecurity.
Se um cliente quiser usar TLS, mas alterar o cobertor de segurança depois de chamar CoInitializeSecurity, ele deverá chamar CoSetProxyBlanket ou IClientSecurity::SetBlanket com parâmetros semelhantes aos usados na chamada para CoInitializeSecurity, com as seguintes diferenças:
- pServerPrincName indica o nome principal do servidor, no formato msstd ou fullsic. Para obter informações sobre esses formatos, consulte Nomes principais. Se o cliente tiver o certificado X.509 do servidor, ele poderá encontrar o nome principal chamando RpcCertGeneratePrincipalName.
- pAuthInfo é um ponteiro para um CERT_CONTEXT, convertido como um ponteiro para RPC_AUTH_IDENTITY_HANDLE, que representa o certificado X.509 do cliente. Se o cliente não tiver um certificado ou não desejar apresentar seu certificado ao servidor, pAuthInfo deverá ser NULL e uma conexão anônima será tentada com o servidor.
- dwCapabilities consiste em sinalizadores que indicam recursos adicionais do cliente. Apenas quatro sinalizadores podem ser usados para alterar as configurações do cobertor de segurança: EOAC_DEFAULT, EOAC_MUTUAL_AUTH, EOAC_ANY_AUTHORITY (esse sinalizador foi preterido) e EOAC_MAKE_FULLSIC. Para obter mais informações, consulte CoSetProxyBlanket.
Para obter mais informações sobre como usar CoSetProxyBlanket, consulte Definindo a segurança no nível de proxy de interface.
O exemplo a seguir demonstra como um cliente pode alterar a cobertura de segurança para acomodar uma solicitação do servidor para que o cliente forneça seu certificado X.509. O código de tratamento de erros é omitido por brevidade.
void ClientChangesSecurity ()
{
HCRYPTPROV provider = 0;
HCERTSTORE cert_store = NULL;
PCCERT_CONTEXT client_cert = NULL;
PCCERT_CONTEXT server_cert = NULL;
WCHAR *server_princ_name = NULL;
ISecret *pSecret = NULL;
MULTI_QI server_instance;
COSERVERINFO server_machine;
SOLE_AUTHENTICATION_LIST auth_list;
SOLE_AUTHENTICATION_INFO auth_info[1];
// Specify all the authentication info.
// The client is willing to connect using SChannel,
// with no client certificate.
auth_list.cAuthInfo = 1;
auth_list.aAuthInfo = auth_info;
auth_info[0].dwAuthnSvc = RPC_C_AUTHN_GSS_SCHANNEL;
auth_info[0].dwAuthzSvc = RPC_C_AUTHZ_NONE;
auth_info[0].pAuthInfo = NULL; // No certificate
// Initialize client security with no client certificate.
CoInitializeSecurity( NULL, -1, NULL, NULL,
RPC_C_AUTHN_LEVEL_PKT,
RPC_C_IMP_LEVEL_IMPERSONATE, &auth_list,
EOAC_NONE, NULL );
// Specify info for the proxy.
server_instance = {&IID_ISecret, NULL, S_OK};
server_machine = {0, L"ServerMachineName", NULL, 0};
// Create a proxy.
CoCreateInstanceEx( CLSID_Secret, NULL, CLSCTX_REMOTE_SERVER,
&server_machine, 1, &server_instance);
pSecret = (ISecret *) server_instance.pItf;
//** The client obtained the server's certificate during the handshake.
//** The server requests a certificate from the client.
// Get the default certificate provider.
CryptAcquireContext( &provider, NULL, NULL, PROV_RSA_SCHANNEL, 0 );
// Open the certificate store.
cert_store = CertOpenSystemStore( provider, L"my" );
// Find the client's certificate.
client_cert =
CertFindCertificateInStore( cert_store,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
0,
CERT_FIND_SUBJECT_STR,
L"ClientName", // Use the principal name
NULL );
// Find the fullsic principal name of the server.
RpcCertGeneratePrincipalName( server_cert, RPC_C_FULL_CERT_CHAIN,
&server_princ_name );
// Change the client's security:
// Increase the authentication level and attach a certificate.
CoSetProxyBlanket( pSecret, RPC_C_AUTHN_GSS_SCHANNEL,
RPC_C_AUTHZ_NONE,
server_princ_name, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_IMP_LEVEL_IMPERSONATE,
(RPC_AUTH_IDENTITY_HANDLE *) client_cert,
EOAC_NONE );
cleanup:
if (server_princ_name != NULL)
RpcStringFree( &server_princ_name );
if (client_cert != NULL)
CertFreeCertificateContext(client_cert);
if (server_cert != NULL)
CertFreeCertificateContext(server_cert);
if (cert_store != NULL)
CertCloseStore( cert_store, CERT_CLOSE_STORE_CHECK_FLAG );
if (provider != 0 )
CryptReleaseContext( provider, 0 );
if (pSecret != NULL)
pSecret->Release();
CoUninitialize();
}