Vou mostrar nesse post como utilizar as classes do CAPICOM em Delphi para acessar o Certificate Store do Windows já que esse passo precede a assinatura de documentos - é preciso recuperar um Certificado Digital para realizar a assinatura. No post sobre como trabalhar com o Certificate Store com .NET há mais informações e terminologia sobre o assunto.
Como o CAPICOM é uma biblioteca COM, o primeiro passo é extrair as interfaces e colocá-las na forma de fontes do Delphi. Se ainda não tem os fontes, veja aqui como fazer essa extração e gerá-los. Para usar as classes exportadas, o seu programa deve incluir a unit gerada:
uses CAPICOM_TLB;
Da mesma forma que no .NET, há três classes básicas no CAPICOM para acessar o Store:


O trecho de código Delphi abaixo abre o Certificate Store "My" da máquina local com privilégio apenas para leitura, recupera todos os certificados encontrados e percorre a lista pesquisando os certificados que são válidos.
var i : integer;
store : TStore;
cert : TCertificate;
certs : TCertificates;
ov : OleVariant;
begin
store := TStore.Create (self);
store.Open (CAPICOM_LOCAL_MACHINE_STORE, 'My', CAPICOM_STORE_OPEN_READ_ONLY);
certs := TCertificates.Create(self);
certs.ConnectTo(store.Certificates as ICertificates2);
cert := TCertificate.Create(self);
for i := 1 to certs.Count do begin
ov := (certs.Item [i]);
cert.ConnectTo (IDispatch (ov) as ICertificate2);
if cert.HasPrivateKey And
(cert.ValidFromDate <= Now) And
(cert.ValidToDate >= Now) then begin
{ Use o certificado aqui para, por exemplo, assinar um documento. }
end;
end;
store.Close;
cert.Free;
certs.Free;
store.Free;
end;
store : TStore;
cert : TCertificate;
certs : TCertificates;
ov : OleVariant;
begin
store := TStore.Create (self);
store.Open (CAPICOM_LOCAL_MACHINE_STORE, 'My', CAPICOM_STORE_OPEN_READ_ONLY);
certs := TCertificates.Create(self);
certs.ConnectTo(store.Certificates as ICertificates2);
cert := TCertificate.Create(self);
for i := 1 to certs.Count do begin
ov := (certs.Item [i]);
cert.ConnectTo (IDispatch (ov) as ICertificate2);
if cert.HasPrivateKey And
(cert.ValidFromDate <= Now) And
(cert.ValidToDate >= Now) then begin
{ Use o certificado aqui para, por exemplo, assinar um documento. }
end;
end;
store.Close;
cert.Free;
certs.Free;
store.Free;
end;
Algumas observações a respeito desse código:






35 comentários :
Muito bom!
Mas tenho uma duvida, o procedimento acima é capaz de assinar arquivos executaveis!?
Vamos por partes:
1) O trecho de código mostrado neste post apenas localiza os certificados, ainda não faz a assinatura em si - será assunto para um outro post.
2) Para assinar um executável, planeje antes e tome alguns cuidados pois inserir nele uma assinatura poderá danificá-lo.
Se o quê você quer é garantir que o conteúdo do executável não foi modificado, pode gerar um DigestValue externo e distribuí-lo junto com o programa (e a chave pública para validação).
Salvou minha vida, vou recomendar o seu blog para muitos amigos meus, parabéns.
cara, fiz do jeito que vc descreveu e no meu d7 nao funcionou, ou seja, ele trouxe count de zero e na verdade tenho 3 certificados instalados na maquina (um deles tem data de validade até 2011).
sabe o que poderia ser?
vlw
danilo.
O exemplo recupera os certificados da área 'My' do Certificate Store. Verifique se os certificados que você tem estão instalados nessa área. Se não for, mude o nome para a área correta.
luíz desculpe minha ignorância, mas onde consigo ver essa área? Atualmente eu vou em "opções da internet", aba conteúdo, botao certificados, e os certificados que tenho na maquina aparecem na aba Pessoal,seria isso?
vlw
Danilo
Danilo
A forma que vc citou para ver seus certificados é valida - outra seria através do Iniciar -> Executar no windows e digitar mmc \windows\system32\certmgr.msc. A aba Personal é realmente mapeada para o local 'My'.
Outras coisas que você pode verificar: se o certificado está instalado só pra seu usuário no windows ou se todos no computador têm acesso a eles. Veja a documentação da função Open do objeto store para ver quais valores são válidos. O exemplo do post extrai apenas o que estiver instalado para todos os usuários (valor LOCAL_MACHINE); tente outros valores.
Certifique-se também que há uma chave privada em seu certificado pois o código está validando isto.
ai luiz, olhei a guia de documentação e comparei com o codigo que eu ja tinha em c# e descobri que era mesmo configuração, ou seja, no meu funcionou assim...
store.Open (CAPICOM_CURRENT_USER_STORE, 'MY', CAPICOM_STORE_OPEN_READ_WRITE);
brigadão pela dica, vlw.
Luís,
Qual seria o nome do repositório para pegar o certificado do usuário. Pois a NFSe de salvador exige assinatura e segundo instruções da dll utilizada por ele (Sefaz Salvador), tenho que copiar o certificado da conta usuário para conta local. Mesmo colocando o repositório "My" para que a dll busque do usuário. Sabe algo sobre a prefeitura? ou qual nome de repositório devo por para que a dll busque do lugar certo.
Há 2 parâmetros que determinam o local onde um certificado está instalado: a localização (que pode ser do usuário ou do computador) e o nome do repositório (que pode ser MY, CA, ROOT, etc.)
Você diz que colocou o seu no repositório MY mas não diz em qual localização. Pelo jeito, no seu caso o certificado tem que estar no MY do Local Computer não no do Current User.
Veja a documentação do Store.Open para mais detalhes.
Boa noite meu amigo, teria mais alguma coisa específica, porque já utilizei todas as opções que você mencionou mas mesmo assim não consigo acessar os meus certificados
Realmente não há nada além do exposto no post ou nos comentários. Encontrar um certificado que tenha sido importado para o Certificate Store é uma questão de abrir a localização (normalmente CAPICOM_CURRENT_USER_STORE ou CAPICOM_LOCAL_MACHINE_STORE) e a pasta correta dentro desse local (My, por exemplo).
Você consegue enxergar seu certificado pelo gerenciador de certificados do Windows ?
Você consegue listar algum certificado via programação ? Dá mensagem de erro ou a lista simplesmente vem vazia ?
Não consegui utilizar no C++ Builder XE. Conseguir fazer a instalação normalmente mas não consigo instanciar o objeto (new). Alguem já utilizou no C++ Builder?
Grato
Thiago
O C++ Builder encapsula de maneira diferente as classes quando você importa um OCX como o do CAPICOM. Você pode usar essas estruturas - chamadas Co Classes - sem necessidade de dar new :
CoStore s;
ICertificates2Ptr certs;
store = s.Create();
store->Open(CAPICOM_CURRENT_USER_STORE,
WideString ("My"),
CAPICOM_STORE_OPEN_READ_ONLY);
certs = store->Certificates;
{ ... }
As coclasses trabalham com ponteiros inteligentes; com isso, também não é preciso se preocupar em destruí-los. A memória associada a eles é liberada automaticamente quando a variável sai de escopo.
Luís,
Estou desenvolvendo uma aplicação para enviar CT-e para validação no site da receita, bem semelhante ao processo da NF-e. Estou pesquisando muito no seu blog mas fiquei com uma dúvida no procedimento acima: Neste código você exemplifica como "ler" todos os certificados existentes na máquina local no repositório "My". Mas como eu vou saber quais destes é o certificado que eu necessito para assinar meu XML?
Desde já agradeço e parabéns pelo blog, realmente muito útil.
Valter
Valter
Sua aplicação terá que ser configurável para que o usuário tenha a chance de indicar ao menos uma vez o certificado que deve ser usado.
Na solução da ABC71, optamos por salvar em banco de dados o número serial do certificado. Com isso, acrescentamos às comparações mostradas no post uma outra pra ver se o cert.SerialNumber corresponde ao certificado indicado pelo usuário.
[]s
Luis,
Obrigado mais uma vez pela atenção. Tenho só mais uma dúvida: Como o usuário pode ver esse número de série do certificado? porque eu acessei os certificados da minha máquina, por exemplo, e em todos o número de série vem apenas "00". (Note que ainda estou estudando o assunto "Certificados digitais")
Grato.
Valter
Se consultar pelo aplicativo de certificados do Windows, o Serial aparecerá como uma sequência de bytes mas o SubjectName, que é uma descrição mais fácil de ser compreendida, estará no mesmo formato retornado pelo CAPICOM. Essa variável, no entanto, pode não ser única. Então, exiba para seu usuário o SubjectName mas armazene o Serial retornado pelo CAPICOM e o use em seu programa localizar o certificado correto.
[]s
Luiz, boa tarde.
Estou precisando pegar o certificado do token USB e assinar um documento .DOCX pelo Browser. Até conseguir fazer, mas quando publiquei no servidor não funcionou porque o browser não consegue acessar a máquina pra pegar o certificado.
Quando rodo a aplicação local funciona.
Você tem alguma sugestão de como fazer isso via browser????
Imagino que vc esteja usando uma tecnologia Web pra gerar as páginas HTML de sua solução - algo como o ASP.Net. Nesse caso, dê uma olhada na propriedade ClientCertificate. Ela é membro da classe HttpRequest, que representa no programa ASP as requisições enviadas pelo browser.
[]s
Muito Obrigado pela Resposta. Estou usando c# + MVC. Quando estou usando a aplicação em Localhost aparece uma janela para escolher o certificado. Aí escolho o certificado do token e continuo na aplicação. Agora quando publiquei no servidor a janela para escolher o certificado não aparece e aparece a mensagem "A Sessão atual não é interativa". Obrigado
Alcimar
Você terá que configurar seu site no IIS para que ele exija o uso de um certificado. Assim, todas as requisições feitas ao servidor enviarão junto os dados do certificado escolhido e o seu programa poderá recuperá-lo via Request.ClientCertificate.Certificate.
Há uma discussão sobre esse assunto nesse fórum.
Não sei como vc implementou para que dê o erro reportado mas, como sua aplicação roda no servidor, ela não poderá exibir diretamente a tela para o usuário selecionar um certificado. Isto é, não faz sentido usar a função de UI da classe de certificados no servidor.
[]s
Luiz, agradeço a sua resposta. Ja li a discussão nesse fórum. Mas la também ninguém conseguiu fazer isso. O que me chama a´atenção é que o site da receita federal é em aspx e pede o certificado sem usar nenhum applet, só acessando o link ja pede o certificado. Veja o link https://cav.receita.fazenda.gov.br/eCAC/publico/Login/Certificado.aspx
Alcimar
Exigir que o usuário forneça um certificado para acessar um site é uma configuração do servidor. Veja o roteiro para isso no artigo Specify Whether to Use Client Certificates (IIS 7) do Technet.
Não sei se há algum código HTML ou javascript que possa ser inserido na página para exibir já de início a lista dos certicados instalados no Client para seleção.
[]s
Gostaria de saber se tem como pegar a chave publica que esta dentro do certificado?
Marcos
O TCertificate implementa a interface ICertificate2, que possui a propriedade PublicKey para representar a informação que você quer. Veja a documentação dessa interface no endereço http://msdn.microsoft.com/en-us/library/aa376092%28v=vs.85%29.aspx
[]s
Gostaria de saber tb se tem jeito de pegar a o valor da assinatura contido no certicado digital. Se tiver como mandar algum fonte feito em delphi agradeço. Colo abaixo aqui o meu :
OE_Util:=createoleobject('CAPICOM.Utilities.1');
store := TStore.Create (self);
store.Open( CAPICOM_SMART_CARD_USER_STORE, 'Root', CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED);
certs := TCertificates.Create(self);
certs.ConnectTo(store.Certificates as ICertificates2);
cert := TCertificate.Create(self);
for i := 1 to certs.Count do
begin
ov := (certs.Item [i]);
cert.ConnectTo (IDispatch (ov) as ICertificate2);
Memo1.Lines.Add('Versão do certificado--->' + IntToStr(cert.Version));
Memo1.Lines.Add('Nº serial do certificado--->' + cert.SerialNumber);
Memo1.Lines.Add('Autoridade certificadora--->' + cert.SubjectName);
Memo1.Lines.Add('Nº remoto--->' + cert.RemoteMachineName);
Memo1.Lines.Add('ThumbPrint--->' + cert.Thumbprint);
Memo1.Lines.Add('IssuerName--->' + cert.IssuerName);
Memo1.Lines.Add('Informação1--->' + cert.GetInfo(CAPICOM_CERT_INFO_SUBJECT_SIMPLE_NAME));
Memo1.Lines.Add('Informação2--->' + cert.GetInfo(CAPICOM_CERT_INFO_SUBJECT_EMAIL_NAME));
Memo1.Lines.Add('Informação3--->' + cert.GetInfo(CAPICOM_CERT_INFO_ISSUER_DNS_NAME));
Memo1.Lines.Add('HASH CODE---->' +IntToStr(cert.GetHashCode));
Memo1.Lines.Add('Tamanho da chave publica--->' + IntToStr(cert.PublicKey.Length));
Memo1.Lines.Add('Algoritimo de validação--->' + cert.PublicKey.Algorithm.Value);
strstring:= OE_Util.BinaryToHex(Cert.PublicKey.EncodedKey.Value[CAPICOM_ENCODE_BINARY]);
Memo1.Lines.Add('Valor da Chave Privada em string---->' + strstring);
Memo1.Lines.Add('-------------------------------------');
Memo1.Lines.Add('CHAVE PUBLICA BASE 64---->' + cert.PublicKey.EncodedKey.Value[CAPICOM_ENCODE_BASE64]);
Memo1.Lines.Add('-------------------------------------');
// Memo1.Lines.Add('conteudo do cerificado');
// Memo1.Lines.Add(cert.Export(CAPICOM_ENCODE_ANY));
if cert.HasPrivateKey then
begin
Memo1.Lines.add('CERTIFICADO COM CHAVE PRIVADA');
if (cert.IsValid.Result) then
begin
Memo1.Lines.Add('CERTIFICADO VALIDO');
//validar no web service
end
else
Memo1.Lines.Add('CERTIFICADO INVÁLIDO')
end
else
begin
Memo1.Lines.add('CERTIFICADO SEM CHAVE PRIVADA');
if (cert.IsValid.Result) then
Memo1.Lines.Add('CERTIFICADO VALIDO')
else
Memo1.Lines.Add('CERTIFICADO INVÁLIDO');
end;
Memo1.Lines.Add('-------------------------------------------------------');
Marcos
O que vc chama de "pegar a assinatura" do certificado? Se vc quer usar o certificado para assinar um documento, dê uma olhada no link http://balaiotecnologico.blogspot.com.br/2009/07/assinando-documentos-com-capicom.html
Todos os posts do blog envolvendo CAPICOM estão agrupados no endereço http://balaiotecnologico.blogspot.com.br/search/label/CAPICOM.
[]s
Eu gerei a CAPICOM_TLB.pas, importei em um projeto de teste e copiei o código para recuperar o certificado digital, porem na CAPICOM_TLB não tem essas classes (TStore, TCertificate e TCertificates) sendo assim, não consegui compilar o projeto, estou usando o Delphi XE2, tem algo que estou fazendo de errado?
Diego
Provavelmente você não marcou a caixa "Generate Component Wrappers" quando pediu pra gerar o CAPICOM_TLB.
Você pode gerar de novo com essa caixa marcada ou usar as classes CoXXXXXXX geradas no fim da unit (Ex: CoStore) em conjunto com as interfaces (Ex: IStore3). O efeito é o mesmo de se usar o TStore.
[]s
Muito obrigado Luís, refiz os processo marcando a opção "Generate Component Wrappers" e funcionou perfeito, agora só preciso estudar um pouco as possibilidades da capicom. Abraços.
estou com problema na parte cert.ConnectTo(IDispatch (ov) as ICertificate2);
ele nao retorna nada. Nao aparece erro nenhum e simplismente sai do evento. Se puder me ajudar agradeço.
Talles
Experimente envolver seu código num bloco try / Except para tentar capturar a exceção que possivelmente está sendo levantada. Este tipo de comportamento normalmente ocorre quando alguma variável não está corretamente atribuída (alocada). O tratamento de exceção pode ajudar a descobrir a causa.
Att.
Como poderia capturar as informações de em certificado específico, sem passar pela seleção.
Você pode carregar as informações a partir do arquivo do certicado com a função Load ou importar um texto representando o certificado codificado usando a função Import.
Ambas são expostas no objeto TCertificate.
[]s
Postar um comentário
OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.
Observação: somente um membro deste blog pode postar um comentário.