• R/O
  • SSH
  • HTTPS

akdf: Commit


Commit MetaInfo

Revisão530 (tree)
Hora2020-11-24 05:14:15
Autorderekwildstar

Mensagem de Log

Documentação atualizada
XmlDSigCreate concluída!

Mudança Sumário

Diff

--- trunk/rtp/src/Rtl/Sys/KRK.Rtl.Sys.System.pas (revision 529)
+++ trunk/rtp/src/Rtl/Sys/KRK.Rtl.Sys.System.pas (revision 530)
@@ -33,7 +33,12 @@
3333 //: @Param(ADestination é um PByte que vai receber o conteúdo de AOrigin em seu
3434 //: final. ADestination pode ser um PByte de tamanho zero (zero elementos))
3535 //: @Param(ADestinationSize é a quantidade de bytes que ADestination está
36-//: apontando antes deste procedure ser executado)
36+//: apontando antes deste procedure ser executado. Chamadas subsequentes a
37+//: AppendBytes utilizarão este parâmetro para saber a quantidade de bytes
38+//: existentes em ADestination e assim poder realizar a concatenação
39+//: corretamente, portanto é responsabilitade do programador incrementar uma
40+//: variável que será usada neste parâmetro com a quantidade de bytes escritos
41+//: em uma chamada anterior a AppendBytes)
3742 procedure AppendBytes(const AOrigin: PByte; AOriginSize: DWORD; var ADestination: PByte; ADestinationSize: DWORD); overload;
3843
3944 //: Anexa os bytes contidos em AOrigin ao final dos bytes contidos em
--- trunk/rtp/src/Xml/KRK.Xml.CryptXml.Utilities.pas (revision 529)
+++ trunk/rtp/src/Xml/KRK.Xml.CryptXml.Utilities.pas (revision 530)
@@ -18,25 +18,29 @@
1818
1919 TXDSCreateArguments = record
2020 SignatureType: TXDSSignatureType;
21+ CertificateContext: PCCERT_CONTEXT;
22+ AddCertificate: TXDSAddCertificate;
2123 InputFileName: String;
2224 InputData: PByte;
2325 InputSize: DWORD;
2426 InputCharSet: TXDSCharSet;
25- ReferenceUri: String;
26- ReferenceId: String;
27+ OutputCharSet: TXDSCharSet;
28+ SignatureId: String;
2729 SignatureLocation: String;
28- SignatureId: String;
29- CertificateContext: PCCERT_CONTEXT;
3030 SignatureCanonicalizationMethod: TXDSCanonicalizationMethod;
31+ SignatureHash: TXDSHash;
32+ ReferenceId: String;
33+ ReferenceUri: String;
3134 ReferenceCanonicalizationMethod: TXDSCanonicalizationMethod;
32- SignatureHash: TXDSHash;
33- DigestHash: TXDSHash;
34- AddCertificate: TXDSAddCertificate;
35+ ReferenceHash: TXDSHash;
36+ KeyInfoId: String;
3537 AddKeyValue: Boolean;
36- KeyInfoId: String;
3738 end;
3839
3940 TXDSCreateResults = record
41+ OutputData: PByte;
42+ OutputSize: DWORD;
43+ OutputCharSet: TXDSCharSet;
4044 ErrorCode: HRESULT;
4145 ErrorMessage: String;
4246 end;
@@ -59,6 +63,15 @@
5963 SysUtils, Math, KRK.Rtl.Win.NCrypt, KRK.Rtl.Win.BCrypt, KRK.Rtl.Common.FileUtils,
6064 KRK.Rtl.Sys.System;
6165
66+// Esta é a função de callback usada para consolidar o resultado da assinatura
67+// digital.
68+function WriteXml(pvCallbackState: PVOID; const pbData: PBYTE; cbData: ULONG): HRESULT; WINAPI;
69+begin
70+ AppendBytes(pbData,cbData,TXDSCreateResults(pvCallbackState^).OutputData,TXDSCreateResults(pvCallbackState^).OutputSize);
71+ TXDSCreateResults(pvCallbackState^).OutputSize := TXDSCreateResults(pvCallbackState^).OutputSize + cbData;
72+ Result := S_OK;
73+end;
74+
6275 function XmlDSigCreate(const AArguments: TXDSCreateArguments; out AResults: TXDSCreateResults): Boolean;
6376 const
6477 ENVELOPED_TRANSFORM: CRYPT_XML_ALGORITHM = (cbSize: SizeOf(CRYPT_XML_ALGORITHM);
@@ -66,6 +79,7 @@
6679 Encoded: (dwCharset: CRYPT_XML_CHARSET_AUTO;
6780 cbData: 0;
6881 pbData: nil));
82+ TRUEBOOL: BOOL = True;
6983 var
7084 Signature: HCRYPTXML;
7185 Reference: HCRYPTXML;
@@ -103,10 +117,10 @@
103117 SignatureCanonicalizationMethod: CRYPT_XML_ALGORITHM;
104118 ReferenceCanonicalizationMethod: CRYPT_XML_ALGORITHM;
105119 SignatureHashAlgorithm: CRYPT_XML_ALGORITHM;
106- DigestHashAlgorithm: CRYPT_XML_ALGORITHM;
120+ ReferenceHashAlgorithm: CRYPT_XML_ALGORITHM;
107121 OIDInformation: PCCRYPT_OID_INFO;
108122 CNGAlgorithmId: array [0..1] of LPCWSTR;
109- OpenToEncodeProperties: array [0..0] of CRYPT_XML_PROPERTY;
123+ EncodeProperties: array [0..0] of CRYPT_XML_PROPERTY;
110124 ReferenceFlags: DWORD;
111125 ReferenceUri: PChar;
112126 TransformationAlgorithms: array [0..1] of CRYPT_XML_ALGORITHM;
@@ -120,17 +134,32 @@
120134 Result := False;
121135 ZeroMemory(@AResults,SizeOf(TXDSCreateResults));
122136
137+ // Abaixo estão as variáveis que são usadas em CleanUp. Elas precisam ser
138+ // inicializadas aqui antes de qualquer chamada a CleanUp
139+ Signature := nil;
140+ ZeroMemory(@InputFile,SizeOf(CRYPT_XML_BLOB));
141+ FreeInputFile := False;
142+ MustFreePrivateKey := False;
143+ PrivateKey := 0;
144+ CertificateChainContext := nil;
145+
146+ if AArguments.OutputCharSet = csNone then
147+ begin
148+ AResults.ErrorCode := 1;
149+ AResults.ErrorMessage := 'XmlDSigCreate: O charset de saída não foi informado';
150+ CleanUp; // Não vai fazer nada, mas vamos manter o padrão pra deixar claro.
151+ Exit;
152+ end;
153+
123154 if not Assigned(AArguments.CertificateContext) then
124155 begin
125- AResults.ErrorCode := 1;
156+ AResults.ErrorCode := 2;
126157 AResults.ErrorMessage := 'XmlDSigCreate: Um certificado não foi informado. Não é possível continuar';
127158 CleanUp; // Não vai fazer nada, mas vamos manter o padrão pra deixar claro.
128159 end
129160 else
130161 begin
131- PrivateKey := 0;
132162 PrivateKeyType := 0;
133- MustFreePrivateKey := False;
134163 // Obtém a chave privada e o tipo da chave do certificado. Neste ponto,
135164 // ainda não há solicitação de senha ou pin. Isso só será solicitado no
136165 // momento em que a chave privada for efetivamente usada, isto é, ao chamar
@@ -150,8 +179,6 @@
150179 begin
151180 // Neste ponto já temos PrivateKey e PrivateKeyType preenchidas!
152181
153- CertificateChainContext := nil;
154-
155182 // Essa variável seve para configurar os critérios de pesquisa que
156183 // CertGetCertificateChain usa para encontrar os certificados da cadeia de
157184 // certificados. Aqui, não adicionamos nenhum critério especial, tudo está
@@ -197,7 +224,7 @@
197224 cmExclusiveC14NC: SignatureCanonicalizationMethod.wszAlgorithm := wszURI_CANONICALIZATION_EXSLUSIVE_C14NC;
198225 else
199226 begin
200- AResults.ErrorCode := 2;
227+ AResults.ErrorCode := 3;
201228 AResults.ErrorMessage := 'XmlDSigCreate: O método de canonicalização da assinatura digital não foi escolhido';
202229 CleanUp;
203230 Exit;
@@ -245,7 +272,7 @@
245272 hSHA512: CNGAlgorithmId[0] := BCRYPT_SHA512_ALGORITHM;
246273 else
247274 begin
248- AResults.ErrorCode := 3;
275+ AResults.ErrorCode := 4;
249276 AResults.ErrorMessage := 'XmlDSigCreate: O algoritmo de hash da assinatura digital não foi informado';
250277 CleanUp;
251278 Exit;
@@ -269,14 +296,14 @@
269296
270297 // Configurando o algoritmo de hash do resumo criptográfico (digest) da
271298 // referência. Isso consiste em complementar a definição da variável
272- // DigestHashAlgorithm, selecionando o algorítmo adequado de acordo com
273- // o argumento escolhido
274- ZeroMemory(@DigestHashAlgorithm,SizeOf(CRYPT_XML_ALGORITHM));
275- DigestHashAlgorithm.cbSize := SizeOf(CRYPT_XML_ALGORITHM);
299+ // ReferenceHashAlgorithm, selecionando o algorítmo adequado de acordo
300+ // com o argumento escolhido
301+ ZeroMemory(@ReferenceHashAlgorithm,SizeOf(CRYPT_XML_ALGORITHM));
302+ ReferenceHashAlgorithm.cbSize := SizeOf(CRYPT_XML_ALGORITHM);
276303
277304 // Aqui eu reaproveitei CNGAlgorithmId, já que é um array de LPCWSTR.
278305 // Usei o primeiro item deste array
279- case AArguments.DigestHash of
306+ case AArguments.ReferenceHash of
280307 hSHA1: CNGAlgorithmId[0] := BCRYPT_SHA1_ALGORITHM;
281308 hSHA256: CNGAlgorithmId[0] := BCRYPT_SHA256_ALGORITHM;
282309 hSHA384: CNGAlgorithmId[0] := BCRYPT_SHA384_ALGORITHM;
@@ -283,8 +310,8 @@
283310 hSHA512: CNGAlgorithmId[0] := BCRYPT_SHA512_ALGORITHM;
284311 else
285312 begin
286- AResults.ErrorCode := 4;
287- AResults.ErrorMessage := 'XmlDSigCreate: O algoritmo de hash do resumo criptográfico (digest) não foi informado';
313+ AResults.ErrorCode := 5;
314+ AResults.ErrorMessage := 'XmlDSigCreate: O algoritmo de hash do resumo criptográfico (digest) da referência não foi informado';
288315 CleanUp;
289316 Exit;
290317 end;
@@ -303,11 +330,8 @@
303330 Exit;
304331 end;
305332
306- DigestHashAlgorithm.wszAlgorithm := AlgorithmInfo.wszAlgorithmURI;
333+ ReferenceHashAlgorithm.wszAlgorithm := AlgorithmInfo.wszAlgorithmURI;
307334
308- ZeroMemory(@InputFile,SizeOf(CRYPT_XML_BLOB));
309- FreeInputFile := False;
310-
311335 case AArguments.InputCharSet of
312336 csNone: InputFile.dwCharset := CRYPT_XML_CHARSET_AUTO;
313337 csUTF8: InputFile.dwCharset := CRYPT_XML_CHARSET_UTF8;
@@ -329,7 +353,7 @@
329353 end
330354 else
331355 begin
332- AResults.ErrorCode := 5;
356+ AResults.ErrorCode := 6;
333357 AResults.ErrorMessage := 'XmlDSigCreate: Não foi informado um arquivo de entrada ou dados a serem assinados. Não é possível continuar';
334358 CleanUp;
335359 Exit;
@@ -355,7 +379,6 @@
355379 ZeroMemory(@TransformationAlgorithms,SizeOf(TransformationAlgorithms));
356380 TransformationAlgorithmsCount := 0;
357381
358- Signature := nil;
359382 ReferenceUri := '';
360383
361384 case AArguments.SignatureType of
@@ -370,7 +393,7 @@
370393 stEnveloped: begin
371394 if InputFile.dwCharset = CRYPT_XML_CHARSET_AUTO then
372395 begin
373- AResults.ErrorCode := 6;
396+ AResults.ErrorCode := 7;
374397 AResults.ErrorMessage := 'XmlDSigCreate: O charset do xml de entrada não foi informado. Não é possível continuar';
375398 CleanUp;
376399 Exit;
@@ -384,16 +407,16 @@
384407 // ignoro, caso a memória do array não seja inicializada desta
385408 // forma, a função CryptXmlOpenToEncode mais adiante não funciona
386409 // de jeito nenhum, retornando o código de erro para "parâmetro
387- // inválido".
388- ZeroMemory(@OpenToEncodeProperties,SizeOf(OpenToEncodeProperties));
410+ // incorreto".
411+ ZeroMemory(@EncodeProperties,SizeOf(EncodeProperties));
389412
390- OpenToEncodeProperties[0].dwPropId := CRYPT_XML_PROPERTY_SIGNATURE_LOCATION;
391- OpenToEncodeProperties[0].pvValue := @AArguments.SignatureLocation;
392- OpenToEncodeProperties[0].cbValue := SizeOf(LPCWSTR);
413+ EncodeProperties[0].dwPropId := CRYPT_XML_PROPERTY_SIGNATURE_LOCATION;
414+ EncodeProperties[0].pvValue := @AArguments.SignatureLocation;
415+ EncodeProperties[0].cbValue := SizeOf(LPCWSTR);
393416 end
394417 else
395418 begin
396- AResults.ErrorCode := 7;
419+ AResults.ErrorCode := 8;
397420 AResults.ErrorMessage := 'XmlDSigCreate: Não foi informado o nó onde o nó de assinatura deverá ser incluído. Não é possível continuar';
398421 CleanUp;
399422 Exit;
@@ -401,22 +424,28 @@
401424
402425 // A função CryptXmlOpenToEncode cria o elemento de assinatura
403426 // <Signature> com o id indicado em AArguments.SignatureId,
404- // utilizando as propriedades incluídas no array
405- // OpenToEncodeProperties no arquivo xml contido em InputFile. No
406- // final da execução, Signature conterá um ponteiro para o elemento
407- // de assinatura, o qual pode ser usado em várias outras funções
408- // CryptXML
427+ // utilizando as propriedades incluídas no array EncodeProperties no
428+ // arquivo xml contido em InputFile. No final da execução, Signature
429+ // conterá um ponteiro para o elemento de assinatura, o qual pode
430+ // ser usado em várias outras funções CryptXML
409431
410- // Abaixo OpenToEncodeProperties é o array de propriedades, que no
411- // caso só tem um elemento. O endereço de um array estático é o
412- // endereço de seu primeiro elemento. CryptXmlOpenToEncode pode
413- // falhar quando em OpenToEncodeProperties.pvValue não existe o
414- // indicador do nó onde deve ser colocado o nó <Signature> ou quando
415- // aquilo que foi informado não existe no xml de entrada
432+ // Abaixo EncodeProperties é o array de propriedades, que no caso só
433+ // tem um elemento. O endereço de um array estático é o endereço de
434+ // seu primeiro elemento. CryptXmlOpenToEncode pode falhar quando em
435+ // EncodeProperties.pvValue não existe o indicador do nó onde deve
436+ // ser colocado o nó <Signature> ou quando aquilo que foi informado
437+ // não existe no xml de entrada. Se você estiver se perguntando
438+ // porque eu usei um array de uma posição ao invéz de usar uma
439+ // variável simples, a explicação é também simples: para deixar
440+ // claro que ali podem ser usadas mais de uma propriedade e que para
441+ // isso basta usar um array estático com mais posições, indicando no
442+ // parâmetro cProperty a quantidade de elementos neste array que
443+ // contém propriedades válidas, em suma, é apenas para que eu me
444+ // lembre no futuro que isso é possível
416445 AResults.ErrorCode := CryptXmlOpenToEncode(nil
417446 ,0
418447 ,PChar(AArguments.SignatureId)
419- ,@OpenToEncodeProperties
448+ ,@EncodeProperties
420449 ,1
421450 ,@InputFile
422451 ,@Signature);
@@ -442,7 +471,7 @@
442471 stEnveloping: begin
443472 if AArguments.ReferenceUri = '' then
444473 begin
445- AResults.ErrorCode := 8;
474+ AResults.ErrorCode := 9;
446475 AResults.ErrorMessage := 'XmlDSigCreate: Não foi informado o URI da referência. Ele é obrigatório no formato ENCODING. Não é possível continuar';
447476 CleanUp;
448477 Exit;
@@ -488,7 +517,7 @@
488517 end;
489518 else
490519 begin
491- AResults.ErrorCode := 9;
520+ AResults.ErrorCode := 10;
492521 AResults.ErrorMessage := 'XmlDSigCreate: O tipo de assinatura a realizar não foi informado';
493522 CleanUp;
494523 Exit;
@@ -530,7 +559,7 @@
530559 ,PChar(AArguments.ReferenceId)
531560 ,ReferenceUri
532561 ,nil
533- ,@DigestHashAlgorithm
562+ ,@ReferenceHashAlgorithm
534563 ,TransformationAlgorithmsCount
535564 ,@TransformationAlgorithms
536565 ,@Reference);
@@ -560,7 +589,7 @@
560589
561590 // Cria em formato binário uma string que contém a codificação em
562591 // Base64 dentro do nó <Base64>
563- StringToBinary('<Base64>' + BytesToString(BTSP) + '</Base64>',InputFileBase64.pbData,InputFileBase64.cbData);
592+ StringToBinary('<Base64 xmlns="http://www.w3.org/2000/09/xmldsig#">' + BytesToString(BTSP) + '</Base64>',InputFileBase64.pbData,InputFileBase64.cbData);
564593 try
565594 // O CharSet precisa corresponder a aquilo que está em
566595 // InputFileBase64.pbData. Se o conteudo for ANSI, use UTF8, se for
@@ -607,7 +636,10 @@
607636 ZeroMemory(@CertificateChain,SizeOf(CertificateChain));
608637
609638 ZeroMemory(@KeyInfoParam,SizeOf(CRYPT_XML_KEYINFO_PARAM));
610- KeyInfoParam.wszId := PChar(AArguments.KeyInfoId);
639+
640+ if AArguments.KeyInfoId <> '' then
641+ KeyInfoParam.wszId := PChar(AArguments.KeyInfoId);
642+
611643 KeyInfoParam.rgCertificate := @CertificateChain;
612644
613645 // De acordo com a opção de adição de certificados, devemos adicionar
@@ -646,32 +678,43 @@
646678 ,@SignatureHashAlgorithm
647679 ,@SignatureCanonicalizationMethod);
648680
681+ if Failed(AResults.ErrorCode) then
682+ begin
683+ AResults.ErrorMessage := 'CryptXmlSign: Erro 0x' + IntToHex(AResults.ErrorCode,8) + 'h ao executar';
684+ CleanUp;
685+ Exit;
686+ end;
649687
650- ontem passou em stEnveloped! continue
688+ // Agora vamos salvar o resultado em AResults. A propriedade abaixo
689+ // serve para produzir o cabeçalho no topo do xml.
690+ // <?xml version="1.0" encoding="utf-8" standalone="yes"?>
651691
652-{
653- Result := CryptXmlSign(hSig
654- ,hCryptProvOrNCryptKey
655- ,dwKeySpec
656- ,dwSignFlags
657- ,CRYPT_XML_KEYINFO_SPEC_PARAM
658- ,@KeyInfoParam
659- ,@xmlAlg_SignatureMethod
660- ,@xmlAlg_CanonicalizationMethod);
661-}
692+ ZeroMemory(@EncodeProperties,SizeOf(EncodeProperties));
693+ EncodeProperties[0].dwPropId := CRYPT_XML_PROPERTY_DOC_DECLARATION;
694+ EncodeProperties[0].pvValue := @TRUEBOOL;
695+ EncodeProperties[0].cbValue := SizeOf(BOOL);
662696
697+ AResults.ErrorCode := CryptXmlEncode(Signature
698+ ,CRYPT_XML_CHARSET(AArguments.OutputCharSet)
699+ ,@EncodeProperties
700+ ,1
701+ ,@AResults
702+ ,WriteXml);
703+
663704 if Failed(AResults.ErrorCode) then
664705 begin
665- AResults.ErrorMessage := 'CryptXmlSign: Erro 0x' + IntToHex(AResults.ErrorCode,8) + 'h ao executar';
706+ AResults.ErrorMessage := 'CryptXmlEncode: Erro 0x' + IntToHex(AResults.ErrorCode,8) + 'h ao executar';
666707 CleanUp;
667708 Exit;
668709 end;
669-
670710 end;
671711 end;
672712
673- // Limpa tudo que tiver sobrado na memória
713+ // Caso o fluxo chegue neste ponto, significa que tudo ocorreu bem, neste
714+ // caso limpa tudo que tiver sobrado na memória e configura o resultado como
715+ // true
674716 CleanUp;
717+ Result := True;
675718 end;
676719 end;
677720
Show on old repository browser