リビジョン | 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
@@ -115,6 +115,18 @@ | ||
115 | 115 | WriteKeyInfos: TWriteKeyInfos; |
116 | 116 | end; |
117 | 117 | |
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 | + | |
118 | 130 | TXMLSignReturn = record |
119 | 131 | Content: String; |
120 | 132 | Encoding: String; |
@@ -133,7 +145,7 @@ | ||
133 | 145 | //: tenha sido escolhido ou false em caso contrário) |
134 | 146 | 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}; |
135 | 147 | //: 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 | |
137 | 149 | //: a função da CryptoAPI CertFreeCertificateContext |
138 | 150 | function CertFreeAndNilCertificateContext(var APCertContext: PCCERT_CONTEXT): Boolean; |
139 | 151 | function GetStringCheckSum(const AInputString: UTF8String; AHashAlgorithms: array of THashAlgorithm; AFinalHashAlgorithm: THashAlgorithm = haIgnore): String; |
@@ -228,12 +240,13 @@ | ||
228 | 240 | //: https://www.w3.org/TR/xmldsig-core2/. Esta função requer que a biblioteca |
229 | 241 | //: MSXML5 esteja registrada |
230 | 242 | function XMLSign(const AParams: TXMLSignParams): TXMLSignReturn; |
243 | +function XMLVerifySign(const AParams: TXMLVerifySignParams; out AReturn: TXMLVerifySignReturn): Boolean; | |
231 | 244 | |
232 | 245 | implementation |
233 | 246 | |
234 | 247 | uses |
235 | 248 | 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; | |
237 | 250 | |
238 | 251 | resourcestring |
239 | 252 | // Para a NFE bizarra, é necessário haver o URI apontando pra algo. Esse algo |
@@ -1734,6 +1747,85 @@ | ||
1734 | 1747 | end; |
1735 | 1748 | end; |
1736 | 1749 | |
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 | + | |
1737 | 1829 | function GetKeyContainerInfo(ACertificateContext: PCCERT_CONTEXT; out ACryptKeyProvInfo: PCryptKeyProvInfo): Boolean; |
1738 | 1830 | var |
1739 | 1831 | CryptKeyProvInfoSize: DWORD; |
@@ -2088,4 +2180,92 @@ | ||
2088 | 2180 | end; |
2089 | 2181 | end; |
2090 | 2182 | |
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 | + | |
2091 | 2271 | end. |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -3,7 +3,7 @@ | ||
3 | 3 | interface |
4 | 4 | |
5 | 5 | uses |
6 | - Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, | |
6 | + Winapi.Windows, Winapi.Messages, System.Variants, | |
7 | 7 | System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, |
8 | 8 | Vcl.StdCtrls, Vcl.ExtCtrls, UFRAMSelecionarCertificado, UClasses; |
9 | 9 |
@@ -11,7 +11,6 @@ | ||
11 | 11 | TFRAMAssinaturaEmXML = class(TFrame) |
12 | 12 | LAEDArquivoAAssinar: TLabeledEdit; |
13 | 13 | BUTNSelecionarArquivo: TButton; |
14 | - BUTNAssinar: TButton; | |
15 | 14 | GRBXOpcoesDeAssinatura: TGroupBox; |
16 | 15 | RAGRFormatoAssinatura: TRadioGroup; |
17 | 16 | GRBXOpcoesDeSelecaoDeCertificado: TGroupBox; |
@@ -25,8 +24,13 @@ | ||
25 | 24 | CHBXCertificates: TCheckBox; |
26 | 25 | CHBXPurge: TCheckBox; |
27 | 26 | FRAMSelecionarCertificado: TFRAMSelecionarCertificado; |
27 | + Panel1: TPanel; | |
28 | + BUTNValidar: TButton; | |
29 | + BUTNAssinar: TButton; | |
28 | 30 | procedure BUTNSelecionarArquivoClick(Sender: TObject); |
29 | 31 | procedure BUTNAssinarClick(Sender: TObject); |
32 | + procedure RAGRFormatoAssinaturaClick(Sender: TObject); | |
33 | + procedure BUTNValidarClick(Sender: TObject); | |
30 | 34 | private |
31 | 35 | { Private declarations } |
32 | 36 | public |
@@ -43,7 +47,8 @@ | ||
43 | 47 | |
44 | 48 | uses |
45 | 49 | 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; | |
47 | 52 | |
48 | 53 | procedure TFRAMAssinaturaEmXML.BUTNAssinarClick(Sender: TObject); |
49 | 54 | var |
@@ -83,12 +88,55 @@ | ||
83 | 88 | |
84 | 89 | procedure TFRAMAssinaturaEmXML.BUTNSelecionarArquivoClick(Sender: TObject); |
85 | 90 | begin |
86 | - //Enveloped só pode ser usado por XML | |
87 | -// Enveloping pode ser usado por qualquer tipo de arquivo | |
88 | 91 | 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; | |
90 | 100 | end; |
91 | 101 | |
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 | + | |
92 | 140 | procedure TFRAMAssinaturaEmXML.LoadAll; |
93 | 141 | begin |
94 | 142 | LoadDigestAlgorithms; |
@@ -133,4 +181,18 @@ | ||
133 | 181 | CBBXSignatureAlgorithm.ItemIndex := 2; |
134 | 182 | end; |
135 | 183 | |
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 | + | |
136 | 198 | end. |