Jump to content

Assinatura digital PDF


Morfas3

Recommended Posts

@Morfas3
a.png

Este print é o resultado de quando valido o codigo SMS que me foi enviado. Perguntei à AMA se o campo "certificate" deveria vir a null e disseram-me que era normal e que o processo da CMD acaba aqui.

Este certificado que falas é suposto vir aqui ou é um outro método que eles disponibilizam chamado GetCertificate que me retorna 3 certificados distintos?
Obrigado

Link to comment
Share on other sites

@reznor sobre o certificado o serviço traz um .pem e é so usares esse ficheiro a construir a chain

sobre a assinatura em branco, tens de implementar outro container  a implementar a IExternalSignatureContainer em que tens de fazer o hash do documento com o prefixo que eles falam na documentação isto na implementação do método byte[] Sign(Stream data),

implementa também este metodo

public void ModifySigningDictionary(PdfDictionary signDic)
        {
            signDic.Put(PdfName.Filter, PdfName.Adobe_PPKLite);
            signDic.Put(PdfName.SubFilter, tPdfName.Adbe_pkcs7_detached);
        }

depois é fazer isto

 PdfSigner signer = new PdfSigner(pdfReader, filestream, new StampingProperties());
                   var chain = Getcertificado_do_user(filpath);


                    signer.SetFieldName("fieldname");
                    this.SetCustomSignature(signer.GetDocument(), signer.GetSignatureAppearance(), chain[0], signatureLocation, signatureReason);


                    IExternalSignatureContainer external = new ExternalContainer(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
                    signer.SignExternalContainer(external, 8192);



hashToSend = ((ExternalContainer)external).DocumentHash;

 

Link to comment
Share on other sites

@reznor esquci-me de um promenor 

 

 this.SetCustomSignature é implementado por nós, mas basicamente é onde se faz a customização da assinatura, mas tem lá este pequeno truque importante

este parametro que passas no metodo,  signer.GetSignatureAppearance() 

serve para fazer isto abaixo

signerappereance .SetCertificate(chain do certificado utilizador);

 

Link to comment
Share on other sites

@reznor eles têm lá um conjunto de byes do documento, agora não encontro a documentação mas penso que vai de encontro a estes bytes abaixo

           byte[] sha256SigPrefix = new byte[] {
                 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,
                 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
                 0x05, 0x00, 0x04, 0x20
                };

Link to comment
Share on other sites

@Morfas3

ok, já percebi, o que não estou a entender é que é dito lá no documento que "O hash enviado para os serviços deve ser o prefixo seguido do hash do documento.", isto vai seguir o EMSA-PKCS1-v1_5, estou com dificuldade em fazer isto, como computar estas duas hash para enviar para a CMD.
É a primeira vez que estou a lidar com crypto, algumas coisas são completamente novas e peço desculpa se parecer demasiado parvo ou non-sense qualquer pergunta da minha parte

Link to comment
Share on other sites

@reznor basicamente tens de fazer   byte[] docHash = Sha256.ComputeHash(array de bytes do doc),  e juntar este bytearray ao array de bytes do prefixo

Deixa lá quando andamos a implementar isto, quase que íamos chegar ao ponto de implementar o algorimto de hash que eles falam na documentação (visto que já não conseguiamos perceber o que estava mal), mas depois confirmei que o .net já tem isto. Foi muita tentativa e erro.

Edited by Morfas3
  • Vote 1
Link to comment
Share on other sites

@Morfas3
Tentativa erra à fartazana, é como levar com brita na cara constantemente!!
Já consegui assinar à partida, tenho alguns warnings no PDF

Screenshot-2021-03-16-150512.png

Aconteceu-te algo do género?
Já agora, eu não sei se estes testes em Pré Produção da AMA devem dar assinaturas válidas atenção, tentei perguntar mas é complicado do lado deles responder...

Link to comment
Share on other sites

Boas pessoal. 

Tb estou a tentar implementar a assinatura digital mas estou a ter o mesmo erro na assinatura que o @reznor estava a ter. 

Na prática, esotu obter certificado sem pin, recuperar a chain de certificados (lista) através do bouncy castle e a utilizar a mm estratégia que estão usar aqui... Acho eu... Mas está mm a me faltar algo simples... @reznor alguma dica para passar da imagem 1 a 2? Já agora, estavas a ter o ber encoding error?

 

Obrigado. 

Link to comment
Share on other sites

@Labreu
Basicamente estás a fazer o mesmo que estamos a fazer, em relação ao hash que envias para a AMA, colocaste o prefixo? Aquela dica que o @Morfas3 deu antes da minha segunda imagem?
Não estou a ter o encoding error, estou na fase em que tenho o doc assinado, se vir os detalhes de assinatura esta tudo ok (à primeira vista) e não sei qual sera o problema seguinte

Edited by reznor
Link to comment
Share on other sites

Sim, penso que estou a fazer corretamente isso. Basicamente, estou a fazer o seguinte (C#):
 

        public byte[] GetDocHashFromPreparedDocToSign(string pathToOriginalPdf, string pathToPreparedToBeSignedPdf, List<X509Certificate> certificates) {
            var pdfSigner = new PdfSigner(new PdfReader(pathToOriginalPdf),
                                          new FileStream(pathToPreparedToBeSignedPdf, FileMode.Create),
                                          new StampingProperties());
            pdfSigner.SetFieldName(_signatureFieldname);


            var appearance = pdfSigner.GetSignatureAppearance();
            appearance.SetPageRect(new Rectangle(144, 144, 200, 100))
                      .SetPageNumber(1)
                      .SetCertificate(certificates[0]);

            var container = new ExternalBlankSignatureContainer(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
            pdfSigner.SignExternalContainer(container, 8192);
            

           byte[] sha256SigPrefix = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,
                                         0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
                                         0x05, 0x00, 0x04, 0x20 };

           // get hash from pdf with reserved space and preprending with specified
            using var stream = File.OpenRead(pathToPreparedToBeSignedPdf);
            var data = DigestAlgorithms.Digest(stream, DigestAlgorithms.SHA256);
            var totalHash = new byte[sha256SigPrefix.Length + data.Length];
            sha256SigPrefix.CopyTo(totalHash, 0);
            data.CopyTo(totalHash, sha256SigPrefix.Length);
            return totalHash;
        }
 

O resultado deste metodo e passado para o servico da CMD:

    var pdfManager = new PdfManager();
    var pathToSigned = "c:\\temp\\doc1.pdf";
    var docHash = pdfManager.GetDocHashFromPreparedDocToSign(pathDocPdf, pathToSigned, certificates);
    var signReq = new SignRequest   {
        ApplicationId = applicationId,
        DocName = "Doc1.pdf",
        Hash = docHash,
        Pin = pinCmdBase64,
        UserId = userIdBase64
    };    
    SignStatus resScmdSign = await client.SCMDSignAsync(signReq);
 

O resultado devolvido esta ok (200), pelo que me limito a chamar o ValidateOptAsync:

    var resValidacaoPin = await client.ValidateOtpAsync(codeBase64, resScmdSign.ProcessId, applicationId);
    if(resValidacaoPin.Status.Code == "200") {
        var pathToSignedWithSiognature = "c:\\temp\\doc1_signed.pdf";
        // valid signature, replace temporary with new one
        pdfManager.SignPreparedToBeSignedDoc(pathToSigned, pathToSignedWithSiognature, resValidacaoPin.Signature);
    }
 

E já agora, o método SignPreparedToBeSignedDoc:

    public void SignPreparedToBeSignedDoc(string pathToPreparedToBeSignedPdf, string pathToSignedFile, byte[] signature) {
        var document = new PdfDocument(new PdfReader(pathToPreparedToBeSignedPdf));
        using var writer = new FileStream(pathToSignedFile, FileMode.Create);

        var container = new ExternalInjectingSignatureContainer(signature);
        PdfSigner.SignDeferred(document, _signatureFieldname, writer, container);
    }
 

    internal class ExternalInjectingSignatureContainer : IExternalSignatureContainer {
        private readonly byte[] _signature;

        public ExternalInjectingSignatureContainer(byte[] signature) {
            _signature = signature;
        }

        public byte[] Sign(Stream data) => _signature;

        public void ModifySigningDictionary(PdfDictionary signDic) {
        }
    }
 

Edição: Relativamente aos certificados, estou a recuperar a chain devolvida pela AMA e a utilizar o primeiro certificado. O código de recuperação dos certificados é o seguinte:

    public static List<X509Certificate> ConvertStringToCertificate(string certificate)    {
        
        byte[] bytes = Encoding.ASCII.GetBytes(certificate);
        var certs = new X509CertificateParser().ReadCertificates(bytes)
                                               .Cast<X509Certificate>()
                                               .ToList();
        return certs;
    }
 

 

Basicamente, faco o SignDeferred e passo o array de bytes devolvido pelo valdateopt da CMD...

À primeira vista, parece-me que isto está certo, mas às tantas estou a falhar algo óbvio...

 

 

Edited by Labreu
Mais informação
Link to comment
Share on other sites

@Labreu
Penso que te falta algo no método Sign do IExternalSignatureContainer implementado:
 

public byte[] Sign(Stream data)
{
	PdfPKCS7 sgn = new PdfPKCS7(null, certificado.ToArray(), "SHA256", false);
    sgn.SetExternalDigest(this.signedBytes, null, "RSA");
    return sgn.GetEncodedPKCS7();
}

 

Onde "certificado" é a lista de certificados do método GetCertificate depois de tratado, podes validar?
 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.