• R/O
  • SSH
  • HTTPS

akdf: コミット


コミットメタ情報

リビジョン524 (tree)
日時2020-10-26 01:34:51
作者derekwildstar

ログメッセージ

KRK.Rtl.Win.WinCrypt.Utilities.pas
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

Concluída a função XMLVerifySign

UDAMOPrincipal.dfm
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

Ajustes na caixa de diálogo de abrir arquivo a assinar

UFRAMAssinaturaEmXML.dfm e UFRAMAssinaturaEmXML.pas
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

Regras básicas para abertura de arquivos a assinar implementadas
Implementado o validador de assinatura em XML

Diversos
¯¯¯¯¯¯¯¯

Adicionados arquivos de exemplo para realização de assinatura digital usando XML

変更サマリ

差分

--- trunk/rtp/src/Rtl/Win/KRK.Rtl.Win.WinCrypt.Utilities.pas (revision 523)
+++ trunk/rtp/src/Rtl/Win/KRK.Rtl.Win.WinCrypt.Utilities.pas (revision 524)
@@ -115,6 +115,18 @@
115115 WriteKeyInfos: TWriteKeyInfos;
116116 end;
117117
118+ TXMLVerifySignParams = record
119+ XMLFileName: String;
120+ XMLContents: String;
121+ UseKeyValue: Boolean;
122+ end;
123+
124+ TXMLVerifySignReturn = record
125+ Certificate: PCCERT_CONTEXT;
126+ ErrorCode: HRESULT;
127+ ErrorMessage: String;
128+ end;
129+
118130 TXMLSignReturn = record
119131 Content: String;
120132 Encoding: String;
@@ -133,7 +145,7 @@
133145 //: tenha sido escolhido ou false em caso contrário)
134146 function SelectCertificate(const AParams: TSelectCertificateParams; out ACertContext: PCCERT_CONTEXT): Boolean; deprecated {$IF RTLVersion > 20}'Esta função faz uso de algumas funções depreciadas do WinCrypt. Por favor use a versão recomendada em KRK.Lib.Rtl.Win.CNG.Utilities'{$IFEND};
135147 //: Função auxiliar que, alem de liberar o contexto do certificado, atribui como
136-//: nil a refer?ncia passada em seu parâmetro. Internamente esta função utiliza
148+//: nil a referência passada em seu parâmetro. Internamente esta função utiliza
137149 //: a função da CryptoAPI CertFreeCertificateContext
138150 function CertFreeAndNilCertificateContext(var APCertContext: PCCERT_CONTEXT): Boolean;
139151 function GetStringCheckSum(const AInputString: UTF8String; AHashAlgorithms: array of THashAlgorithm; AFinalHashAlgorithm: THashAlgorithm = haIgnore): String;
@@ -228,12 +240,13 @@
228240 //: https://www.w3.org/TR/xmldsig-core2/. Esta função requer que a biblioteca
229241 //: MSXML5 esteja registrada
230242 function XMLSign(const AParams: TXMLSignParams): TXMLSignReturn;
243+function XMLVerifySign(const AParams: TXMLVerifySignParams; out AReturn: TXMLVerifySignReturn): Boolean;
231244
232245 implementation
233246
234247 uses
235248 KRK.Rtl.Win.CryptUIApi, KRK.Rtl.Win.WinCrypt.PasswordPrompt,
236- KRK.Rtl.Win.Windows, KRK.Rtl.Common.FileUtils;
249+ KRK.Rtl.Win.Windows, KRK.Rtl.Common.FileUtils, ComObj;
237250
238251 resourcestring
239252 // Para a NFE bizarra, é necessário haver o URI apontando pra algo. Esse algo
@@ -1734,6 +1747,85 @@
17341747 end;
17351748 end;
17361749
1750+
1751+(*
1752+
1753+função que valida um certificado olhando sua cadeia. Dá pra implementar?
1754+use como parametro PCCERT_CONTEXT diretamente
1755+VARIANT_BOOL IsCertificateValid(IXMLDSigKeyExPtr pKey)
1756+{
1757+ if (pKey == NULL) {
1758+ printf("invalid key object.\n");
1759+ return VARIANT_FALSE;
1760+ }
1761+
1762+ // Retrieve the ceritificate from the verifying key.
1763+ PCCERT_CONTEXT pCert=NULL;
1764+ pCert = (PCCERT_CONTEXT)pKey->getVerifyingCertificateContext();
1765+ if (pCert == NULL) {
1766+ printf ("Can't get verifying certificate context\n");
1767+ return VARIANT_FALSE;
1768+ }
1769+
1770+ // Use CryptoAPI to verify the certificate.
1771+ CERT_CHAIN_ENGINE_CONFIG chainConfig;
1772+ PCCERT_CHAIN_CONTEXT pChainContext;
1773+ CERT_CHAIN_PARA chainPara;
1774+
1775+ HCERTCHAINENGINE hChainEngine=NULL; // Use the default chain engine.[HCCE_CURRENT_USER]
1776+
1777+ // Initialize chainPara:
1778+ chainPara.cbSize = sizeof(CERT_CHAIN_PARA);
1779+ chainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
1780+ chainPara.RequestedUsage.Usage.cUsageIdentifier =0;
1781+ chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = NULL;
1782+
1783+ // Initialize chainConfig:
1784+ chainConfig.cbSize = sizeof(CERT_CHAIN_ENGINE_CONFIG);
1785+ chainConfig.hRestrictedRoot = NULL;
1786+ chainConfig.hRestrictedTrust = NULL;
1787+ chainConfig.hRestrictedOther = NULL;
1788+ chainConfig.cAdditionalStore = 0;
1789+ chainConfig.rghAdditionalStore = NULL;
1790+ chainConfig.dwFlags = CERT_CHAIN_CACHE_END_CERT;
1791+ chainConfig.dwUrlRetrievalTimeout = 0 ;
1792+ chainConfig.MaximumCachedCertificates = 0 ;
1793+ chainConfig.CycleDetectionModulus = 0;
1794+
1795+ // Creates a certificate chain engine to build a certificate trust chain.
1796+ if( !CertCreateCertificateChainEngine(&chainConfig, &hChainEngine) )
1797+ {
1798+ printf("Can't create certificate chain engine. Error Code= %d\n", GetLastError());
1799+ return VARIANT_FALSE;
1800+ }
1801+
1802+ // Build a certificate chain from the chain engine.
1803+ if( !CertGetCertificateChain(hChainEngine, // chain engine handle
1804+ pCert, // Pointer to the end certificate.
1805+ NULL, // Use the default time.
1806+ NULL, // Search no additional stores.
1807+ &chainPara, // Use AND logic, and enhanced key usage as indicated in the ChainPara data structure.
1808+ CERT_CHAIN_REVOCATION_CHECK_END_CERT,
1809+ NULL, // Currently reserved.
1810+ &pChainContext)) // Return a pointer to the chain created.
1811+ {
1812+ printf("Failed to complete the trust chain of the certificate. ");
1813+ printf("Error code = %d\n", GetLastError());
1814+ CertFreeCertificateChain(pChainContext);
1815+ CertFreeCertificateChainEngine(hChainEngine);
1816+ CertFreeCertificateContext(pCert);
1817+ return VARIANT_FALSE;
1818+ }
1819+
1820+ // Verification successful.
1821+ CertFreeCertificateChain(pChainContext);
1822+ CertFreeCertificateChainEngine(hChainEngine);
1823+ CertFreeCertificateContext(pCert);
1824+
1825+ return VARIANT_TRUE;
1826+}
1827+*)
1828+
17371829 function GetKeyContainerInfo(ACertificateContext: PCCERT_CONTEXT; out ACryptKeyProvInfo: PCryptKeyProvInfo): Boolean;
17381830 var
17391831 CryptKeyProvInfoSize: DWORD;
@@ -2088,4 +2180,92 @@
20882180 end;
20892181 end;
20902182
2183+// O url abaixo tem um exemplo que valida a cadeia de certificados. Isso pode
2184+// ser útil de forma genérica. Veja depois
2185+// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms762311(v=vs.85)
2186+// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms765497(v=vs.85)
2187+function XMLVerifySign(const AParams: TXMLVerifySignParams; out AReturn: TXMLVerifySignReturn): Boolean;
2188+var
2189+ XMLDigitalSignature: IXMLDigitalSignature;
2190+ InputXML: IXMLDOMDocument3;
2191+ KeyValueNode: IXMLDOMNode;
2192+ InputKey: IXMLDSigKey;
2193+ OutputKey: IXMLDSigKey;
2194+ CertificateContext: Pointer;
2195+begin
2196+ Result := False;
2197+ ZeroMemory(@AReturn,SizeOf(TXMLVerifySignReturn));
2198+ // Cria o objeto de assinatura
2199+ XMLDigitalSignature := CoMXDigitalSignature50.Create;
2200+ // Cria o objeto para carregar o XML
2201+ InputXML := CoDOMDocument50.Create;
2202+ InputXML.async := False;
2203+ InputXML.validateOnParse := False;
2204+ InputXML.preserveWhiteSpace := True;
2205+ InputXML.resolveExternals := False;
2206+ // Carrega o XML a partir da fonte especificada
2207+ if AParams.XMLFileName <> '' then
2208+ InputXML.load(AParams.XMLFileName)
2209+ else
2210+ InputXML.loadXML(AParams.XMLContents);
2211+ // Define o namespace de seleção atribuind a ele o alias "ds"
2212+ InputXML.setProperty('SelectionNamespaces','xmlns:ds="http://www.w3.org/2000/09/xmldsig#"');
2213+ // Informa ao objeto de assinatura o nó de assinatura
2214+ XMLDigitalSignature.signature := InputXML.selectSingleNode('//ds:Signature');
2215+ if not Assigned(XMLDigitalSignature.signature) then
2216+ raise Exception.Create('Não foi possível obter nó <ds:Signature>');
2217+
2218+ InputKey := nil;
2219+
2220+ if AParams.UseKeyValue then
2221+ begin
2222+ // Obtém o nó que contém o valor da chave
2223+ KeyValueNode := InputXML.selectSingleNode('//ds:KeyInfo/ds:KeyValue');
2224+
2225+ if not Assigned(KeyValueNode) then
2226+ raise Exception.Create('Não foi possível obter o nó <ds:KeyValue>')
2227+ else
2228+ // createKeyFromNode recebe como parâmetro um nó filho de <ds:KeyInfo> e
2229+ // retorna uma "objeto chave". Aqui, ele obtém a chave pública contida
2230+ // em <ds:KeyValue>, obtido anteriormente
2231+ InputKey := XMLDigitalSignature.createKeyFromNode(KeyValueNode);
2232+ end;
2233+ // Verifica a assinatura utilizando o valor de Key, que pode ser ou uma
2234+ // chave pública contida no próprio arquivo (AParams.UseKeyValue = true) ou
2235+ // nil, quando AParams.UseKeyValue = false. Ao usar nil, o método verify
2236+ // automaticamente tenta obter a chave a partir do nó KeyValue ou do nó
2237+ // X509Data, ou seja, caso haja apenas o certificado no arquivo xml sendo
2238+ // verificado, ainda é possível validá-lo. ATENÇÃO!! Nos exemplos do MSDN,
2239+ // como aquele contido em ms762311(v=vs.85), a obtenção da chave foi feita
2240+ // manualmente, buscando pelos elementos <ds:KeyValue> ou <ds:X509Data>. Para
2241+ // o primeiro, funcionava, para o segundo a verificação falhava e não sei ao
2242+ // certo o porquê. No caso aqui apenas funcionou quando eu usei estritamente
2243+ // aquilo que há no manual de XMLDigitalSignature.verify, eu simplesmente
2244+ // passo nil em seu parâmetro e automaticamente a verificação é feita usando o
2245+ // certificado
2246+ try
2247+ OutputKey := XMLDigitalSignature.verify(InputKey);
2248+
2249+ if Assigned(OutputKey) then
2250+ begin
2251+ // Quando a verificação usa KeyValue, InputKey não foi criada a partir de
2252+ // um certificado, mas sim a partir do elemento <ds:KeyValue>, portanto,
2253+ // um certificado só poderá ser obtido quando AParams.UseKeyValue = False.
2254+ if not AParams.UseKeyValue then
2255+ begin
2256+ IXMLDSigKeyEx(OutputKey).getVerifyingCertificateContext(CertificateContext);
2257+ AReturn.Certificate := CertificateContext;
2258+ end;
2259+
2260+ Result := True;
2261+ end;
2262+ except
2263+ on EOE: EOleException do
2264+ begin
2265+ AReturn.ErrorCode := EOE.ErrorCode;
2266+ AReturn.ErrorMessage := EOE.Message;
2267+ end;
2268+ end;
2269+end;
2270+
20912271 end.
--- trunk/utl/TESTADOR/bin/ISO-8859-1.xml (nonexistent)
+++ trunk/utl/TESTADOR/bin/ISO-8859-1.xml (revision 524)
@@ -0,0 +1,12 @@
1+<?xml version="1.0" encoding="iso-8859-1"?>
2+<averbacao_registro>
3+ <cns>089805</cns>
4+ <matricula>08980501552020100014148000672540</matricula>
5+ <data_averbacao_alt>10/10/2020</data_averbacao_alt>
6+ <motivo_averbacao>ALTERAÇÃO NOME ACRÉSCIMO</motivo_averbacao>
7+ <data_motivo_averb>10/10/2020</data_motivo_averb>
8+ <processo_judicial/>
9+ <data_sentenca_judicial>10/10/2020</data_sentenca_judicial>
10+ <dados_complementares>À noite, vovô Kowalsky vê o ímã cair no pé do pinguim queixoso e vovó põe açúcar no chá de tâmaras do jabuti feliz</dados_complementares>
11+ <tipo_registro>N</tipo_registro>
12+</averbacao_registro>
\ No newline at end of file
--- trunk/utl/TESTADOR/bin/UTF-8-BOM.xml (nonexistent)
+++ trunk/utl/TESTADOR/bin/UTF-8-BOM.xml (revision 524)
@@ -0,0 +1,12 @@
1+<?xml version="1.0" encoding="utf-8"?>
2+<averbacao_registro>
3+ <cns>089805</cns>
4+ <matricula>08980501552020100014148000672540</matricula>
5+ <data_averbacao_alt>10/10/2020</data_averbacao_alt>
6+ <motivo_averbacao>ALTERAÇÃO NOME ACRÉSCIMO</motivo_averbacao>
7+ <data_motivo_averb>10/10/2020</data_motivo_averb>
8+ <processo_judicial/>
9+ <data_sentenca_judicial>10/10/2020</data_sentenca_judicial>
10+ <dados_complementares>À noite, vovô Kowalsky vê o ímã cair no pé do pinguim queixoso e vovó põe açúcar no chá de tâmaras do jabuti feliz</dados_complementares>
11+ <tipo_registro>N</tipo_registro>
12+</averbacao_registro>
\ No newline at end of file
--- trunk/utl/TESTADOR/bin/UTF-8.xml (nonexistent)
+++ trunk/utl/TESTADOR/bin/UTF-8.xml (revision 524)
@@ -0,0 +1,12 @@
1+<?xml version="1.0" encoding="utf-8"?>
2+<averbacao_registro>
3+ <cns>089805</cns>
4+ <matricula>08980501552020100014148000672540</matricula>
5+ <data_averbacao_alt>10/10/2020</data_averbacao_alt>
6+ <motivo_averbacao>ALTERAÇÃO NOME ACRÉSCIMO</motivo_averbacao>
7+ <data_motivo_averb>10/10/2020</data_motivo_averb>
8+ <processo_judicial/>
9+ <data_sentenca_judicial>10/10/2020</data_sentenca_judicial>
10+ <dados_complementares>À noite, vovô Kowalsky vê o ímã cair no pé do pinguim queixoso e vovó põe açúcar no chá de tâmaras do jabuti feliz</dados_complementares>
11+ <tipo_registro>N</tipo_registro>
12+</averbacao_registro>
\ No newline at end of file
--- trunk/utl/TESTADOR/src/UFRAMAssinaturaEmXML.pas (revision 523)
+++ trunk/utl/TESTADOR/src/UFRAMAssinaturaEmXML.pas (revision 524)
@@ -3,7 +3,7 @@
33 interface
44
55 uses
6- Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
6+ Winapi.Windows, Winapi.Messages, System.Variants,
77 System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
88 Vcl.StdCtrls, Vcl.ExtCtrls, UFRAMSelecionarCertificado, UClasses;
99
@@ -11,7 +11,6 @@
1111 TFRAMAssinaturaEmXML = class(TFrame)
1212 LAEDArquivoAAssinar: TLabeledEdit;
1313 BUTNSelecionarArquivo: TButton;
14- BUTNAssinar: TButton;
1514 GRBXOpcoesDeAssinatura: TGroupBox;
1615 RAGRFormatoAssinatura: TRadioGroup;
1716 GRBXOpcoesDeSelecaoDeCertificado: TGroupBox;
@@ -25,8 +24,13 @@
2524 CHBXCertificates: TCheckBox;
2625 CHBXPurge: TCheckBox;
2726 FRAMSelecionarCertificado: TFRAMSelecionarCertificado;
27+ Panel1: TPanel;
28+ BUTNValidar: TButton;
29+ BUTNAssinar: TButton;
2830 procedure BUTNSelecionarArquivoClick(Sender: TObject);
2931 procedure BUTNAssinarClick(Sender: TObject);
32+ procedure RAGRFormatoAssinaturaClick(Sender: TObject);
33+ procedure BUTNValidarClick(Sender: TObject);
3034 private
3135 { Private declarations }
3236 public
@@ -43,7 +47,8 @@
4347
4448 uses
4549 UDAMOPrincipal, KRK.Rtl.Common.FileUtils, KRK.Rtl.Win.Cng.Utilities,
46- KRK.Rtl.Win.WinCrypt.Utilities;
50+ KRK.Rtl.Win.WinCrypt.Utilities, KRK.Rtl.Win.CryptUIApi, System.SysUtils,
51+ KRK.Rtl.Win.WinCrypt;
4752
4853 procedure TFRAMAssinaturaEmXML.BUTNAssinarClick(Sender: TObject);
4954 var
@@ -83,12 +88,55 @@
8388
8489 procedure TFRAMAssinaturaEmXML.BUTNSelecionarArquivoClick(Sender: TObject);
8590 begin
86- //Enveloped só pode ser usado por XML
87-// Enveloping pode ser usado por qualquer tipo de arquivo
8891 if DAMOPrincipal.OPDICarregarArquivoAAssinar.Execute then
89- LAEDArquivoAAssinar.Text := DAMOPrincipal.OPDICarregarArquivoAAssinar.FileName;
92+ begin
93+ LAEDArquivoAAssinar.Clear;
94+
95+ if (RAGRFormatoAssinatura.ItemIndex = 0) and (ExtractFileExt(DAMOPrincipal.OPDICarregarArquivoAAssinar.FileName) <> '.xml') then
96+ raise Exception.Create('No formato "enveloped", apenas arquivos .xml podem ser selecionados')
97+ else
98+ LAEDArquivoAAssinar.Text := DAMOPrincipal.OPDICarregarArquivoAAssinar.FileName;
99+ end;
90100 end;
91101
102+procedure TFRAMAssinaturaEmXML.BUTNValidarClick(Sender: TObject);
103+var
104+ Params: TXMLVerifySignParams;
105+ Return: TXMLVerifySignReturn;
106+begin
107+ if not FileExists(LAEDArquivoAAssinar.Text) then
108+ raise Exception.Create('O arquivo "' + LAEDArquivoAAssinar.Text + '" não existe')
109+ else
110+ begin
111+ ZeroMemory(@Params,SizeOf(TXMLVerifySignParams));
112+
113+ Params.XMLFileName := LAEDArquivoAAssinar.Text;
114+ Params.UseKeyValue := Application.MessageBox('Deseja forçar a utilização do nó <ds:KeyValue>?','E aí?',MB_ICONQUESTION or MB_YESNO) = IDYES;
115+
116+ if XMLVerifySign(Params,Return) then
117+ begin
118+ if Assigned(Return.Certificate) then
119+ begin
120+ if Application.MessageBox('A assinatura foi validada com sucesso! Deseja exibir o certificado associado?','Assinatura válida',MB_ICONQUESTION or MB_YESNO) = IDYES then
121+ CryptUIDlgViewContext(CERT_STORE_CERTIFICATE_CONTEXT
122+ ,Return.Certificate
123+ ,Self.Handle
124+ ,'Informações sobre o certificado usado para assinar'
125+ ,0
126+ ,nil);
127+ end
128+ else
129+ Application.MessageBox('A assinatura foi validada com sucesso!','Assinatura válida',MB_ICONINFORMATION);
130+
131+ // É obrigatória a liberação da memória do contexto de certificado
132+ if Assigned(Return.Certificate) then
133+ CertFreeAndNilCertificateContext(Return.Certificate);
134+ end
135+ else
136+ Application.MessageBox(PChar('Não foi possível validar o XML. A mensagem e o código de erro originais foram:'#13#10#13#10 + Return.ErrorMessage + ' (0x' + IntToHex(Return.ErrorCode,8) + ')'),'Assinatura inválida',MB_ICONERROR);
137+ end;
138+end;
139+
92140 procedure TFRAMAssinaturaEmXML.LoadAll;
93141 begin
94142 LoadDigestAlgorithms;
@@ -133,4 +181,18 @@
133181 CBBXSignatureAlgorithm.ItemIndex := 2;
134182 end;
135183
184+procedure TFRAMAssinaturaEmXML.RAGRFormatoAssinaturaClick(Sender: TObject);
185+begin
186+ case RAGRFormatoAssinatura.ItemIndex of
187+ 0: begin // Enveloped só pode ser usado por XML
188+ DAMOPrincipal.OPDICarregarArquivoAAssinar.Title := 'Selecione um arquivo XML a ser assinado';
189+ DAMOPrincipal.OPDICarregarArquivoAAssinar.Filter := 'Extensible Markup Language (*.xml)|*.xml';
190+ end;
191+ 1: begin // Enveloping pode ser usado por qualquer tipo de arquivo
192+ DAMOPrincipal.OPDICarregarArquivoAAssinar.Title := 'Selecione um arquivo a ser assinado';
193+ DAMOPrincipal.OPDICarregarArquivoAAssinar.Filter := 'Todos os arquivos (*.*)|*.*';
194+ end;
195+ end;
196+end;
197+
136198 end.
旧リポジトリブラウザで表示