none
Erro no certificado ao publicar no IIS

    Question

  • Olá,

    Acabei de publicar o meu serviço no IIS, este serviço está utilizando um certificado e autenticação e autorização personalizadas. O probleme é que quando meu serviço está rodando pelo VisualStudio tudo estava funcionando perfeitamente, mas após publicar o serviço no IIS é lançado uma CryptographicException dizendo que o conjunto de chaves não existe.

    Esta é a pilha de rastreamento do erro:

    [CryptographicException: O conjunto de chaves não existe
    ]
       System.Security.Cryptography.Utils.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer) +7714910
       System.Security.Cryptography.Utils.GetKeyPairHelper(CspAlgorithmType keyType, CspParameters parameters, Boolean randomKeyContainer, Int32 dwKeySize, SafeProvHandle& safeProvHandle, SafeKeyHandle& safeKeyHandle) +67
       System.Security.Cryptography.RSACryptoServiceProvider.GetKeyPair() +83
       System.Security.Cryptography.RSACryptoServiceProvider..ctor(Int32 dwKeySize, CspParameters parameters, Boolean useDefaultKeySize) +226
       System.Security.Cryptography.RSACryptoServiceProvider..ctor(CspParameters parameters) +9
       System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey() +202
       System.ServiceModel.Security.SecurityUtils.EnsureCertificateCanDoKeyExchange(X509Certificate2 certificate) +69

    [ArgumentException: O certificado 'CN=CertificadoWCF' deve ter uma chave privada que seja capaz de trocar chaves. O processo deve ter direito de acesso à chave privada.]
       System.ServiceModel.Security.SecurityUtils.EnsureCertificateCanDoKeyExchange(X509Certificate2 certificate) +11599526
       System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateServerX509TokenProvider() +36
       System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateLocalSecurityTokenProvider(RecipientServiceModelSecurityTokenRequirement recipientRequirement) +63
       System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateSecurityTokenProvider(SecurityTokenRequirement requirement) +54
       System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateTlsnegoServerX509TokenProvider(RecipientServiceModelSecurityTokenRequirement recipientRequirement) +140
       System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateTlsnegoSecurityTokenAuthenticator(RecipientServiceModelSecurityTokenRequirement recipientRequirement, Boolean requireClientCertificate, SecurityTokenResolver& sctResolver) +466
       System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, SecurityTokenResolver& outOfBandTokenResolver) +619
       System.ServiceModel.Security.SessionRenewSecurityTokenManager.CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, SecurityTokenResolver& outOfBandTokenResolver) +85
       System.ServiceModel.Security.SymmetricSecurityProtocolFactory.OnOpen(TimeSpan timeout) +11303659
       System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout) +21
       System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +261
       System.ServiceModel.Security.SecurityProtocolFactory.Open(Boolean actAsInitiator, TimeSpan timeout) +23
       System.ServiceModel.Security.SecurityListenerSettingsLifetimeManager.Open(TimeSpan timeout) +80
       System.ServiceModel.Channels.SecurityChannelListener`1.OnOpen(TimeSpan timeout) +204
       System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +261
       System.ServiceModel.Dispatcher.ChannelDispatcher.OnOpen(TimeSpan timeout) +72
       System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +261
       System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout) +107
       System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +261
       System.ServiceModel.Security.SecuritySessionSecurityTokenAuthenticator.OnOpen(TimeSpan timeout) +129
       System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout) +21
       System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +261
       System.ServiceModel.Security.CommunicationObjectSecurityTokenAuthenticator.Open(TimeSpan timeout) +20
       System.ServiceModel.Security.SecurityUtils.OpenTokenAuthenticatorIfRequired(SecurityTokenAuthenticator tokenAuthenticator, TimeSpan timeout) +34
       System.ServiceModel.Security.SecuritySessionServerSettings.OnOpen(TimeSpan timeout) +664
       System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout) +21
       System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +261
       System.ServiceModel.Security.SecurityListenerSettingsLifetimeManager.Open(TimeSpan timeout) +133
       System.ServiceModel.Channels.SecurityChannelListener`1.OnOpen(TimeSpan timeout) +204
       System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +261
       System.ServiceModel.Dispatcher.ChannelDispatcher.OnOpen(TimeSpan timeout) +72
       System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +261
       System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout) +107
       System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +261
       System.ServiceModel.HostingManager.ActivateService(String normalizedVirtualPath) +121
       System.ServiceModel.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath) +479

    [ServiceActivationException: Não é possível ativar o serviço '/FotoRevelacao/Servico.svc' devido a uma exceção durante a compilação. A mensagem de exceção é: O certificado 'CN=CertificadoWCF' deve ter uma chave privada que seja capaz de trocar chaves. O processo deve ter direito de acesso à chave privada..]
       System.ServiceModel.AsyncResult.End(IAsyncResult result) +11536522
       System.ServiceModel.Activation.HostedHttpRequestAsyncResult.End(IAsyncResult result) +194
       System.ServiceModel.Activation.HostedHttpRequestAsyncResult.ExecuteSynchronous(HttpApplication context, Boolean flowContext) +176
       System.ServiceModel.Activation.HttpModule.ProcessRequest(Object sender, EventArgs e) +278
       System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +68
       System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +75



    O que pode estar acontecendo??
    Marcos Latchuk
    Friday, November 06, 2009 11:04 AM

Answers

  • Boas Marcos,

    Se você tem o certificado devidamente instalado no servidor, então você precisa dar permissão ao Worker Process para que ele consiga ler a chave privada: http://www.israelaece.com/post/Worker-Process-Accounts-e-Certificados.aspx
    http://www.israelaece.com
    Friday, November 06, 2009 12:38 PM
  • Resolvido:


    1 - Utiliza o FindPrivateKey para encontrar o nome do arquivo que contém a chave privada do certificado:
         C:\>FindPrivateKey.exe TrustedPeople LocalMachine -n "CN=CertificadoWCF" -a

         ele deve retornar o caminho do arquivo da chave privada:
         C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\fe2ee42e384a926c5zzz70ecdb743714_eaca5aa3-0818-40f1-97d2-5120857f0a62
     
    2 - Vá até a pasta C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\ e localize o arquivo
    3 - Clique com o botão direito do mouse no arquivo e vá Propriedades.
    4 - Na aba segurança edite os nomes de Grupo de Usuário, clique em adicionar, selecione o local do seu domínio e adicione o seguinte nome nome de objeto a ser selecionado:
         "NomeDoSeuUsuarioNoDomínio"/IIS_IUSRS no meu caso por exemplo eu coloquei Latchuk/IIS_IUSRS, pois Latchuk é o nome do meu usuário no domínio.
    5 - Selecione as permissões de leitura e execução.
    6 - Clique em ok, verifique que o usuário "NomeDoSeuUsuarioNoDomínio"/IIS_IUSRS foi adicionado, clique em Ok.

    Pronto, agora o IIS tem acesso a chave privada do seu certificado.

    Estou utilizando o Windows Vista 32 bit e IIS 7.0, para outras versões pode ser diferente, mas creio que funcione da mesma maneira. Foi difícil mas consegui fazer funcionar aqui. Créditos ao meu amigo Israel que ajudou na busca pela solução.
    Marcos Latchuk
    • Marked as answer by Marcos Latchuk Wednesday, November 11, 2009 12:49 PM
    Wednesday, November 11, 2009 12:49 PM

All replies

  • Boas Marcos,

    Se você tem o certificado devidamente instalado no servidor, então você precisa dar permissão ao Worker Process para que ele consiga ler a chave privada: http://www.israelaece.com/post/Worker-Process-Accounts-e-Certificados.aspx
    http://www.israelaece.com
    Friday, November 06, 2009 12:38 PM
  • Israel,

    Voce sabe me dizer onde está localizada a ferramenta FindPrivateKey?
    Porque não encontrei ela na minha máquina, e eu tenho o framework .NET 3.5 instalado.
    Marcos Latchuk
    Friday, November 06, 2009 1:53 PM
  • Friday, November 06, 2009 4:14 PM
  • O que ele quis dizer com essa linha linha?

    "%MSSDK%\bin\FindPrivateKey.exe" My LocalMachine -n CN^=WCFTestCert -a

    Tentei faser o seguinte:

    FindPrivateKey.exe LocalMachine -n CN^="MeuCertificado" -a

    Mas nada acontece...


    E aproveitando, onde posso encontrar a ferramenta icacls.exe??


    Marcos Latchuk
    Friday, November 06, 2009 4:39 PM
  • Boas Marcos,

    Primeiramente você tem que baixar o FindPrivateKey.exe para conseguir extrair o número da chave do certificado, para depois utilizar o icacls.exe para conceder a permissão para o usuário do IIS conseguir ler esta chave.

    O utilitário icacls.exe está acessível a partir do prompt do Visual Studio.
    http://www.israelaece.com
    Friday, November 06, 2009 4:46 PM
  • Sim, eu tenho o FindPrivateKey.exe aqui e executei o seguinte comando no cmd:

    FindPrivateKey.exe LocalMachine -n CN^="MeuCertificado" -a

    Mas nada aconteceu.
    Eu li la no post que o resultado da execução da linha retorna a chave privada, mas como armazeno isso pra depois executar o icacls.exe?
    Marcos Latchuk
    Friday, November 06, 2009 5:02 PM
  • Israel, fiz o seguinte no console:

    C:\>FindPrivateKey.exe TrustedPeople LocalMachine -n "CN=CertificadoWCF" -a

    ele me retornou o caminho da chave privada:
    C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\fe2ee42e384a926c5ab670ecdb743714
    _eaca5aa3-0818-40f1-97d2-5120857f0a62


    ai utilizei o icacls.exe:

    C:\>icacls.exe C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\fe2ee42e384a926c5
    ab670ecdb743714_eaca5aa3-0818-40f1-97d2-5120857f0a62 /grant:r BRAINSOFT\marcos:R

    o retorno foi:
    arquivo processado: C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\fe2ee42e384a
    926c5ab670ecdb743714_eaca5aa3-0818-40f1-97d2-5120857f0a62
    Processados com sucesso 1 arquivos; falha no processamento de 0 arquivos

    Até aí tudo certo, mandei publicar novamente o serviço no IIS e ao tentar acessar o mesmo erro inicial acontece:
    CryptographicException: O conjunto de chaves não existe


    Não sei o que fazer... o que pode estar acontecendo??


    Marcos Latchuk
    Friday, November 06, 2009 5:55 PM
  • Boas Marcos,

    E você mandou para o servidor o mesmo certificado que você colocou na sua aplicação?
    http://www.israelaece.com
    Saturday, November 07, 2009 11:32 AM
  • Sim, estou publicando localmente primeiro, ou seja, na mesma máquina que quando o serviço é iniciado pelo visual studio, funciona!

    Mas depois de publicado no IIS, o erro continua a acontecer...
    Marcos Latchuk
    Wednesday, November 11, 2009 11:14 AM
  • Resolvido:


    1 - Utiliza o FindPrivateKey para encontrar o nome do arquivo que contém a chave privada do certificado:
         C:\>FindPrivateKey.exe TrustedPeople LocalMachine -n "CN=CertificadoWCF" -a

         ele deve retornar o caminho do arquivo da chave privada:
         C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\fe2ee42e384a926c5zzz70ecdb743714_eaca5aa3-0818-40f1-97d2-5120857f0a62
     
    2 - Vá até a pasta C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\ e localize o arquivo
    3 - Clique com o botão direito do mouse no arquivo e vá Propriedades.
    4 - Na aba segurança edite os nomes de Grupo de Usuário, clique em adicionar, selecione o local do seu domínio e adicione o seguinte nome nome de objeto a ser selecionado:
         "NomeDoSeuUsuarioNoDomínio"/IIS_IUSRS no meu caso por exemplo eu coloquei Latchuk/IIS_IUSRS, pois Latchuk é o nome do meu usuário no domínio.
    5 - Selecione as permissões de leitura e execução.
    6 - Clique em ok, verifique que o usuário "NomeDoSeuUsuarioNoDomínio"/IIS_IUSRS foi adicionado, clique em Ok.

    Pronto, agora o IIS tem acesso a chave privada do seu certificado.

    Estou utilizando o Windows Vista 32 bit e IIS 7.0, para outras versões pode ser diferente, mas creio que funcione da mesma maneira. Foi difícil mas consegui fazer funcionar aqui. Créditos ao meu amigo Israel que ajudou na busca pela solução.
    Marcos Latchuk
    • Marked as answer by Marcos Latchuk Wednesday, November 11, 2009 12:49 PM
    Wednesday, November 11, 2009 12:49 PM