validateAllSignatures method
Future<PdfSignatureValidationReport>
validateAllSignatures(
- Uint8List pdfBytes, {
- List<
String> ? trustedRootsPem, - TrustedRootsProvider? trustedRootsProvider,
- List<
TrustedRootsProvider> ? trustedRootsProviders, - bool strictRevocation = false,
- bool fetchCrls = false,
- bool fetchOcsp = false,
- bool validateTemporal = false,
- bool temporalUseSigningTime = false,
- DateTime? validationTime,
- bool temporalExpiredNeedsLtv = true,
- PdfRevocationDataProvider? revocationDataProvider,
- PdfHttpFetcherBase? certificateFetcher,
- bool includeCertificates = false,
- bool includeSignatureFields = true,
- PdfSignaturePreparedContext? preparedContext,
Valida todas as assinaturas do PDF.
Implementation
Future<PdfSignatureValidationReport> validateAllSignatures(
Uint8List pdfBytes, {
List<String>? trustedRootsPem,
TrustedRootsProvider? trustedRootsProvider,
List<TrustedRootsProvider>? trustedRootsProviders,
bool strictRevocation = false,
bool fetchCrls = false,
bool fetchOcsp = false,
bool validateTemporal = false,
bool temporalUseSigningTime = false,
DateTime? validationTime,
bool temporalExpiredNeedsLtv = true,
PdfRevocationDataProvider? revocationDataProvider,
PdfHttpFetcherBase? certificateFetcher,
bool includeCertificates = false,
bool includeSignatureFields = true,
PdfSignaturePreparedContext? preparedContext,
}) async {
final roots = await _collectTrustedRoots(
trustedRootsPem: trustedRootsPem,
trustedRootsProvider: trustedRootsProvider,
trustedRootsProviders: trustedRootsProviders,
);
final parseCache = _resolveParseCache(
pdfBytes,
includeSignatureFields: includeSignatureFields,
includeSignatureContents: false,
baseContext: preparedContext,
);
final quickInfo = parseCache.quickInfo;
final permissionP = quickInfo.docMdpPermissionP;
final fieldByRange = parseCache.fieldByRange;
final ranges = parseCache.ranges;
final results = <PdfSignatureInfoReport>[];
for (var i = 0; i < ranges.length; i++) {
final range = ranges[i];
final fieldInfo =
includeSignatureFields ? fieldByRange[_byteRangeKey(range)] : null;
final intact = _isValidByteRange(pdfBytes.length, range);
final byteRangeCoverage = _computeByteRangeCoverage(pdfBytes, range);
var cmsValid = false;
var digestValid = false;
bool? certValid;
var revocation = const PdfSignatureRevocationInfo();
String? message;
DateTime? signingTime;
String? signaturePolicyOid;
List<String>? signedAttrsOids;
PdfSignatureSignedAttrsReport? signedAttrsReport;
if (!intact) {
results.add(PdfSignatureInfoReport(
signatureIndex: i,
cmsValid: false,
digestValid: false,
intact: false,
docMdp: _buildDocMdpInfo(i, permissionP),
revocation: revocation,
signatureField: fieldInfo,
signatureDictionaryPresent: fieldInfo?.signatureDictionaryPresent,
signingTime: null,
signaturePolicyOid: null,
signedAttrsOids: null,
signedAttrsReport: null,
certificates: null,
signerCertificate: null,
chainTrusted: null,
chainErrors: null,
certValid: null,
validationStatus: PdfSignatureValidationStatus.rejected,
message: 'ByteRange inconsistente.',
byteRangeCoverage: byteRangeCoverage,
));
continue;
}
final contents = parseCache.signatureContentsAt(i) ??
_extractContentsFromByteRange(pdfBytes, range);
if (contents == null || contents.isEmpty) {
results.add(PdfSignatureInfoReport(
signatureIndex: i,
cmsValid: false,
digestValid: false,
intact: true,
docMdp: _buildDocMdpInfo(i, permissionP),
revocation: revocation,
signatureField: fieldInfo,
signatureDictionaryPresent: fieldInfo?.signatureDictionaryPresent,
signingTime: null,
signaturePolicyOid: null,
signedAttrsOids: null,
signedAttrsReport: null,
certificates: null,
signerCertificate: null,
chainTrusted: null,
chainErrors: null,
certValid: null,
validationStatus: PdfSignatureValidationStatus.rejected,
message: 'Conteúdo de assinatura ausente ou inválido.',
byteRangeCoverage: byteRangeCoverage,
));
continue;
}
final certInfos =
includeCertificates ? _extractCertificatesInfo(contents) : null;
final signerCertInfo =
includeCertificates ? _extractSignerCertificateInfo(contents) : null;
cmsValid = await _verifyCmsSignature(contents);
signingTime = _extractSigningTimeFromCms(contents);
if (signingTime == null && fieldInfo?.signingTimeRaw != null) {
signingTime = _parsePdfDate(fieldInfo!.signingTimeRaw!);
}
if (signingTime == null) {
final raw = _scanSigningTimeNearByteRange(pdfBytes, range);
if (raw != null) {
signingTime = _parsePdfDate(raw);
}
}
signaturePolicyOid = _extractSignaturePolicyOid(contents);
signedAttrsOids = _extractSignedAttrsOids(contents);
signedAttrsReport = _buildSignedAttrsReport(signedAttrsOids);
final isAdbePkcs7Sha1 = _isAdbePkcs7Sha1(fieldInfo?.subFilter);
final digestOid =
isAdbePkcs7Sha1 ? '1.3.14.3.2.26' : _extractDigestOid(contents);
final contentDigest =
_computeByteRangeDigestForOid(pdfBytes, range, digestOid);
final messageDigest = _extractMessageDigest(contents);
if (isAdbePkcs7Sha1) {
// ISO 32000-1 §12.8.3.3: em /adbe.pkcs7.sha1 o conteúdo encapsulado
// (eContent) do PKCS#7 é o resumo SHA-1 do ByteRange assinado. A
// integridade do documento exige dois vínculos:
// 1) SHA1(ByteRange) == eContent (documento ↔ conteúdo encapsulado);
// 2) quando há atributos assinados, o messageDigest autentica o
// eContent e deve ser SHA1(eContent) (eContent ↔ assinatura).
// Sem o vínculo 2 o eContent não estaria amarrado à assinatura e um
// documento adulterado (ByteRange + eContent trocados juntos) passaria
// como íntegro.
final encapsulatedDigest = _extractCmsEncapsulatedContent(contents);
if (encapsulatedDigest == null) {
message =
'Conteúdo SHA-1 encapsulado não encontrado no PKCS#7 legado.';
} else if (!_listEquals(contentDigest, encapsulatedDigest)) {
message =
'Resumo criptográfico do ByteRange incompatível com o conteúdo assinado.';
} else if (messageDigest != null &&
!_listEquals(PdfCrypto.sha1(encapsulatedDigest), messageDigest)) {
message =
'Atributo messageDigest não corresponde ao conteúdo SHA-1 encapsulado.';
} else {
digestValid = true;
}
} else if (messageDigest != null) {
digestValid = _listEquals(contentDigest, messageDigest);
} else {
message = 'Atributo messageDigest não encontrado no CMS.';
}
_ChainResult? chainResult;
if (roots.isNotEmpty) {
chainResult = await _buildCertificateChainFromCms(
cmsBytes: contents,
roots: roots,
fetcher: certificateFetcher,
);
certValid = chainResult.trusted;
if (includeCertificates && certInfos != null) {
_mergeCertificateInfos(certInfos, chainResult.chain);
}
}
var chainTrusted = certValid;
if (chainTrusted == false &&
signaturePolicyOid != null &&
cmsValid &&
digestValid) {
chainTrusted = true;
}
final chainErrors = certValid == false
? const <String>['Signer certificate not trusted.']
: null;
if (revocationDataProvider != null && (fetchCrls || fetchOcsp)) {
revocation = await _checkRevocation(
cmsBytes: contents,
roots: roots,
fetchCrls: fetchCrls,
fetchOcsp: fetchOcsp,
provider: revocationDataProvider,
);
if (revocation.crlRevoked || revocation.ocspRevoked) {
certValid = false;
message = 'Certificado revogado.';
} else if (strictRevocation && revocation.revocationUnknown) {
certValid = false;
message = 'Revogação não comprovada.';
}
}
final temporalResult = _evaluateTemporalStatus(
validateTemporal: validateTemporal,
temporalUseSigningTime: temporalUseSigningTime,
validationTime: validationTime,
temporalExpiredNeedsLtv: temporalExpiredNeedsLtv,
signerCertInfo: signerCertInfo,
signingTime: signingTime,
);
if (temporalResult.certValidOverride != null) {
certValid = temporalResult.certValidOverride;
}
final status = _deriveValidationStatus(
intact: true,
cmsValid: cmsValid,
digestValid: digestValid,
chainTrusted: chainTrusted,
certValid: certValid,
temporalStatusOverride: temporalResult.statusOverride,
);
if (temporalResult.messageOverride != null) {
message = temporalResult.messageOverride;
}
results.add(PdfSignatureInfoReport(
signatureIndex: i,
cmsValid: cmsValid,
digestValid: digestValid,
intact: true,
docMdp: _buildDocMdpInfo(i, permissionP),
revocation: revocation,
signatureField: fieldInfo,
signatureDictionaryPresent: fieldInfo?.signatureDictionaryPresent,
signingTime: signingTime,
signaturePolicyOid: signaturePolicyOid,
signedAttrsOids: signedAttrsOids,
signedAttrsReport: signedAttrsReport,
certificates: certInfos,
signerCertificate: signerCertInfo,
chainTrusted: chainTrusted,
chainErrors: chainErrors,
certValid: certValid,
validationStatus: status,
message: message,
byteRangeCoverage: byteRangeCoverage,
));
}
return PdfSignatureValidationReport(signatures: results);
}