Leonardo,
Acho que isso pode resolver o seu problema.
Tem que passar corretamente os dados do certificado para a função.
Senão dá pau mesmo.
O que fiz foi apresentar a caixa de seleção do certificado para o usuário.
Depois de selecionado, basta pegar os dados do objeto Certificate.
Código:
function TfrmMain.SignXML(fwWriteKeyInfo: _XMLDSIG_WRITEKEYINFO): Boolean;
const
sCert: array [Boolean] of string = ('s', '');
sDisp: array [Boolean] of string = ('is', 'l');
var
h, i: Cardinal;
Sett, Cert, Certs, Store: OleVariant;
s1, s2, sProvider, sContainer: String;
eType: CAPICOM_PROV_TYPE;
pKey, pKeyOut: IXMLDSigKey;
sSigs, sX509: IXMLDOMNodeList;
sSig: IXMLDOMNode;
begin
Try
Sett := CoSettings.Create;
Sett.EnablePromptForCertificateUI := True;
Store := CoStore.Create;
Store.Open(CAPICOM_CURRENT_USER_STORE, 'My', CAPICOM_STORE_OPEN_EXISTING_ONLY);
//Remove os Certificados que não são Signature da lista
Certs := Store.Certificates;
for i := Store.Certificates.Count downto 1 do
begin
Cert := IInterface(Certs.Item[i]) as ICertificate2;
if (not Cert.HasPrivateKey) then
Certs.Remove(i);
end;
Try
Cert := Null;
s1 := sCert[Certs.Count = 0];
s2 := sDisp[Certs.Count = 0];
//---> Usuário Seleciona o Certificado
Certs := Certs.Select(Format('Certificado%s disponíve%s', [s1, s2]),
'Selecione o Certificado Digital para uso', FALSE);
Cert := IInterface(Certs.Item[1]) as ICertificate2;
Except
on E: EOleException do
begin
i := e.ErrorCode;
//---> Usuário abortou a seleção
if i = $80880902 then
Abort()
else
Raise;
end;
End;
if not VarIsNull(Cert) then
begin
// Encopntra os <ds:Signature> nodes.
FXMLDoc.setProperty('SelectionNamespaces', DSIGNS);
sSigs := FXMLDoc.selectNodes('.//ds:Signature');
if (sSigs.length = 0) then
Exception.Create('A Tag Signature não foi encontrada no XML.');
//É necessário ter a 1ª assinatura
FXMLDSig.signature := sSigs.item[0];
FXMLDSig.store := Store;
//---> Dados necessários para gerar a assinatura
eType := Cert.PrivateKey.ProviderType;
sProvider := Cert.PrivateKey.ProviderName;
sContainer := Cert.PrivateKey.ContainerName;
// assina cada TAG Signature
for i := 0 to (sSigs.length-1) do
begin
// próximo <ds:Signature> DOM node.
FXMLDSig.signature := sSigs.item[i];
// ---> Assinatura do campo Signature do XML
pKey := FXMLDSig.createKeyFromCSP(eType, sProvider, sContainer, 0);
if (pKey = nil) then
Exception.Create('Invalid key.\n');
pKeyOut := FXMLDSig.sign(pKey, fwWriteKeyInfo);
// deixa no KeyInfo somente um X509Data
sX509 := FXMLDoc.selectNodes('//ds:X509Data[position() > 1]');
if sX509.length > 0 then
for h := (sX509.length-1) downto 0 do
begin
sSig := sX509.item[h].parentNode;
sX509.item[h].text := '';
sSig.removeChild(sX509.item[h]);
end;
end;
end;
if (pKeyOut = nil) then
Exception.Create('Falha durante assinatura.\n');
finally
end;
Result := true;
end;
Resumindo, você está passando errado o conjunto de informações para a função createKeyFromCSP.
Qualquer coisa, pegue meu exemplo no:
http://opennfse.googlecode.com/files/NFSeSigner.zip
OBS.: Repare que usei OleVariant e não a interface em algumas variáveis.
Isso é importante, porque força o windows a usar a versão certa do Objeto na CAPICOM e XMLDOC.
Atenciosamente,
Luiz Vaz