Jump to content

Assinatura digital PDF


Morfas3

Recommended Posts

reznor

Screenshot-2021-03-17-111144.png

@Labreu
Deves estar parado no mesmo sitio que eu. Estou a pesquisar algumas coisas a ver se chego a alguma conclusão e na esperança que, mais uma vez, @Morfas3 me dê alguma luz daquilo que possa estar errado ou menos bem... Se tu validares manualmente a assinatura passa a ser válida, só que não é isso que se pretende... estou um bocado sem soluções para ser sincero

Link to post
Share on other sites
  • Replies 60
  • Created
  • Last Reply

Top Posters In This Topic

  • Morfas3

    24

  • reznor

    23

  • Labreu

    10

  • gatpereira

    1

Top Posters In This Topic

Popular Posts

Boas. Se alguém tiver interesser, criei uma demo online:  https://github.com/luisabreu/AmaCmdSigning

Bom dia  @reznor , Primeiro tem de ter o ao serviço soap que ama Disponibiliza, já tem isso? Faça uns testes a invocar isso, eles têm um manual que explicam como invocar os serviços. Is

têm de usar o singdeferred using (PdfReader reader = new PdfReader("_nome_ficheiro"))             {                 using (FileStream fileStream = new FileStream($"_nome_ficheiro_assinado_ouput",

Labreu

O teu está ok! da erro pq a chain de certificados nao e confiavel (presumo q se passares producao a coisa funciona sem erros).

No meu caso, estou a gerar mal o hash do documento, mas ainda ainda nao percebi o erro....

Link to post
Share on other sites
reznor

@Labreu
 

public static byte[] GetBytesToSign(string unsignedPdf, string tempPdf, string signatureFieldName, List<X509Certificate> clientCertificate. byte[] prefix)
	{
		using (PdfReader reader = new PdfReader(unsignedPdf))
        {
        	using (FileStream os = File.OpenWrite(tempPdf))
            {
            	PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0');
            	PdfSignatureAppearance appearance = stamper.SignatureAppearance;
            	//invisivel  
            	appearance.SetVisibleSignature(new Rectangle(0, 0, 0, 0), 1, signatureFieldName);

            	IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
            	PdfSignature pdfSignature = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
                   
            	appearance.SignDate = DateTime.Now;
            	appearance.CryptoDictionary = pdfSignature;
            	appearance.Certificate = clientCertificate[0];

				var level = reader.GetCertificationLevel();
            	if (level != PdfSignatureAppearance.NOT_CERTIFIED)
            	{
            		throw new Exception("Could not apply -certlevel option. At most one certification per pdf is allowed, but source pdf contained already a certification.");
            	}
	            appearance.CertificationLevel = level;

    	        MakeSignature.SignExternalContainer(appearance, external, 8192);

        	    byte[] array = SHA256.Create().ComputeHash(appearance.GetRangeStream());

				return prefix.Concat(array).ToArray();
		}
	}
}

 

O meu método de assinatura é este, estou com o campo de assinatura invisível, de resto acho que é o que temos falado.Valida, eu estou a usar itextsharp  

 

Em relação ao meu resultado, nao sei se esta OK sinceramente, deveria ser suposto validar mesmo que em ambiente de testes...

Edited by reznor
Link to post
Share on other sites
Labreu

Ok, progresso...afinal n posso gerar o hash a partir do ficheiro com assinatura vazia, mas sim so dos bytes do ficheiro...aha.....agora ver a parte do certificado.

Link to post
Share on other sites
reznor

Exacto @Labreu tens de gerar um bloco "vazio" no pdf onde queres assinar e enviar essa hash, tal como o @Morfas3 indicou.
Entretanto consegui esclarecer com a AMA e eles dizem que em PP é normal que a assinatura não esteja validada, em PROD sim.

 

Entretanto alguma dúvida diz-me.

Link to post
Share on other sites
Morfas3
8 horas atrás, reznor disse:

@Labreu
 


public static byte[] GetBytesToSign(string unsignedPdf, string tempPdf, string signatureFieldName, List<X509Certificate> clientCertificate. byte[] prefix)
	{
		using (PdfReader reader = new PdfReader(unsignedPdf))
        {
        	using (FileStream os = File.OpenWrite(tempPdf))
            {
            	PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0');
            	PdfSignatureAppearance appearance = stamper.SignatureAppearance;
            	//invisivel  
            	appearance.SetVisibleSignature(new Rectangle(0, 0, 0, 0), 1, signatureFieldName);

            	IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
            	PdfSignature pdfSignature = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
                   
            	appearance.SignDate = DateTime.Now;
            	appearance.CryptoDictionary = pdfSignature;
            	appearance.Certificate = clientCertificate[0];

				var level = reader.GetCertificationLevel();
            	if (level != PdfSignatureAppearance.NOT_CERTIFIED)
            	{
            		throw new Exception("Could not apply -certlevel option. At most one certification per pdf is allowed, but source pdf contained already a certification.");
            	}
	            appearance.CertificationLevel = level;

    	        MakeSignature.SignExternalContainer(appearance, external, 8192);

        	    byte[] array = SHA256.Create().ComputeHash(appearance.GetRangeStream());

				return prefix.Concat(array).ToArray();
		}
	}
}

O meu método de assinatura é este, estou com o campo de assinatura invisível, de resto acho que é o que temos falado.Valida, eu estou a usar itextsharp  

Em relação ao meu resultado, nao sei se esta OK sinceramente, deveria ser suposto validar mesmo que em ambiente de testes...

Olá @reznor, em pre prod tem de dar assinaturas validas, deve ser algum nabo a responder do lado da ama para te calar e não o chateares mais, eu lembro que assinar e dar sucesso

Acho que tas a fazer ai umas coisas mal, tas a fazer o hash do do appearance e não do doc todo.

Eu essa parte tenho dentro do metodo que implementa o sign do externalblanckcontainer, que recebe uma stream e ai sim faço a concatenação com o prefixo.

depois de fazer o signexternalcontainer é so fazeres (blanckExternalContainer)external).DocumentHash; e tens o hash

Link to post
Share on other sites
Morfas3
10 horas atrás, reznor disse:

@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?

É isso mesmo, esta parte ta correcta

@reznor @Labreu só de referir que têm de ter dois "externalcontainers" um para extrair o hash com a assinatura em branco e o outro para cravar a assinatura

Link to post
Share on other sites
Labreu

Infelizmente, só consegui responder hoje (limitação número de posts?)...

Nesta altura, produzo o mm resultado "visual" que o @reznor. A questão é que parece que a assinatura efetuada não está 100% correta pq ainda deveria incluir os "atributos" do documento (ver resposta e comentários desta entrada)...

Link to post
Share on other sites
Morfas3
Posted (edited)
43 minutes ago, Labreu said:

Infelizmente, só consegui responder hoje (limitação número de posts?)...

Nesta altura, produzo o mm resultado "visual" que o @reznor. A questão é que parece que a assinatura efetuada não está 100% correta pq ainda deveria incluir os "atributos" do documento (ver resposta e comentários desta entrada)...

public class ExternalEmptySignatureContainer : IExternalSignatureContainer {

public void ModifySigningDictionary(PdfDictionary signDic) {

signDic.Put(PdfName.Filter, PdfName.Adobe_PPKLite); signDic.Put(PdfName.SubFilter, PdfName.Adbe_pkcs7_detached); }

public byte[] Sign(Stream data) { // Store the data to sign and return an empty array

Data = DigestAlgorithms.Digest(data, DigestAlgorithms.SHA256); return new byte[0]; }

public byte[] Data; }

No metodo sign tens de adicionar aqui o prefixo de bytes que ama indica na documentação.

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

@Morfas3Sim, essa parte está ok. A questão aqui é outra: a forma como geramoso  CMS para colocar a assinatura não está correta (na verdade, os mues conhecimentos limitados na área não me permitem aferir bem qual a solução correta, mas parece q apesar de o adobe mostrar corretamente o certificado, a assinatura como está implementada não é totalmente correta...)

Link to post
Share on other sites
reznor

@Morfas3 Olá!
Obrigado pela resposta, eu estou a faze exatamente isso e o resultado que tenho é o mesmo:

public byte[] Sign(Stream data)
{ 
	byte[] sha256SigPrefix = new byte[] {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,0x60, 0x86, 0x48, 
										0x01, 0x65, 0x03, 0x04, 0x02, 0x01,0x05, 0x00, 0x04, 0x20};
    Data = sha256SigPrefix.Concat(DigestAlgorithms.Digest(data, DigestAlgorithms.SHA256)).ToArray();
    return new byte[0];
}

Este meu método Sign para a Assinatura em branco condiz agora com o que transmitiste!

é chamado aqui (ao fazer a assinatura em braco)

(...)
MakeSignature.SignExternalContainer(appearance, external, 8192);
var ssa = ((ExternalBlankSignatureContainer)external).Data;
(...)

E tenho de retorno um byte[51] limpinho para a minha variável "ssa".

 

Ao fazer embed da minha assinatura de retorno da AMA:
 

public static void EmbedSignature(string tempPdf, string signedPdf, string signatureFieldName, 
                                  byte[] signature, List<X509Certificate> certificates)
{
	using (PdfReader reader = new PdfReader(signedPdf))
	{
    	using (FileStream os = new FileStream(tempPdf, FileMode.Create, FileAccess.ReadWrite))
        {
        	IExternalSignatureContainer external = new MyExternalSignatureContainer(signature, certificates);
            MakeSignature.SignDeferred(reader, signatureFieldName, os, external);
        }	
	}
}

Screenshot-2021-03-18-182450.png

e cá está o bicho...

Entretanto(e sem querer) cliquei no botão "Show Issuer Policy"... e não há página sequer
https://www.scee.gov.pt/pcert?lang=ENU

Sinceramente já não sei qual o resultado ou o que podemos esperar daqui, muitas questões e pouca documentação e informações vagas... a AMA devia disponibilizar mais info acerca do assunto. 

@Labreu pois, lá está a situação... eu também não sou o CR7 das assinaturas digitais e não consigo dizer que solução é a mais correta... um belo serviço aqui de verdade 🥴🥴🥴

Link to post
Share on other sites
Morfas3
Posted (edited)
34 minutes ago, reznor said:

@Morfas3 Olá!
Obrigado pela resposta, eu estou a faze exatamente isso e o resultado que tenho é o mesmo:

Este meu método Sign para a Assinatura em branco condiz agora com o que transmitiste!

é chamado aqui (ao fazer a assinatura em braco)



(...)
MakeSignature.SignExternalContainer(appearance, external, 8192);
var ssa = ((ExternalBlankSignatureContainer)external).Data;
(...)

E tenho de retorno um byte[51] limpinho para a minha variável "ssa".

 

Ao fazer embed da minha assinatura de retorno da AMA:
 



public static void EmbedSignature(string tempPdf, string signedPdf, string signatureFieldName, 
                                  byte[] signature, List<X509Certificate> certificates)
{
	using (PdfReader reader = new PdfReader(signedPdf))
	{
    	using (FileStream os = new FileStream(tempPdf, FileMode.Create, FileAccess.ReadWrite))
        {
        	IExternalSignatureContainer external = new MyExternalSignatureContainer(signature, certificates);
            MakeSignature.SignDeferred(reader, signatureFieldName, os, external);
        }	
	}
}

Screenshot-2021-03-18-182450.png

e cá está o bicho...

Entretanto(e sem querer) cliquei no botão "Show Issuer Policy"... e não há página sequer
https://www.scee.gov.pt/pcert?lang=ENU

Sinceramente já não sei qual o resultado ou o que podemos esperar daqui, muitas questões e pouca documentação e informações vagas... a AMA devia disponibilizar mais info acerca do assunto. 

@Labreu pois, lá está a situação... eu também não sou o CR7 das assinaturas digitais e não consigo dizer que solução é a mais correta... um belo serviço aqui de verdade 🥴🥴🥴

public byte[] Sign(Stream data)
{ 
	byte[] sha256SigPrefix = new byte[] {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,0x60, 0x86, 0x48, 
										0x01, 0x65, 0x03, 0x04, 0x02, 0x01,0x05, 0x00, 0x04, 0x20};
    Data = sha256SigPrefix.Concat(DigestAlgorithms.Digest(data, DigestAlgorithms.SHA256)).ToArray();
    return new byte[0];
}

@reznorpode nao ter nada haver mas retorna o resultado da concatenação no sign, tas a retornar um array de bytes vazio, não sei se implica alguma coisa

Acho que tens o racicionio correcto, a unica diferença é usares o itextsharp em vez do itext 7

Edited by Morfas3
Link to post
Share on other sites
Morfas3
Posted (edited)

@reznor

provavelmente deve ser a mesma coisa, no sign faz isto

 

   SHA256 Sha256 = SHA256.Create();

            byte[] docHash = Sha256.ComputeHash(data);

 

Aqui EmbedSignature(string tempPdf, string signedPdf,

o signedPdf é o teu pdf com a assinatura em branco certo?

Edited by Morfas3
Link to post
Share on other sites
reznor

@Morfas3
Em relação ao teu primeiro comentário, acho que não faz nada de qualquer forma troquei o retorno e tudo fica igual! Já começo a duvidar do itextsharp...
No segundo comentário, sim é o PDF com a assinatura em branco e fazer o ComputeHash retorna-me exatamente o mesmo byte[] que o que já estou a  fazer...

Que bico de obra...

Link to post
Share on other sites
Labreu

Boas pessoal.

O código que tenho aqui está produzir mm resultado. O aviso no pdf faz sentido (acho) pq os certificados enviados não pertencem a uma chain fidedigna. No meu caso, ainda estou a tentar perceber o que é que o comentário que tive num post do stackoverflow quer dizer:
 

Quote

 No. To use a PdfPKCS7 pkcs7 properly, you have to call pkcs7.GetAuthenticatedAttributeBytes with the document hash (without the sha256SigPrefix) to get the signed attributes to be, hash and sign them, set the resulting signature bytes using pkcs7.SetExternalDigest, and eventually call pkcs7.GetEncodedPKCS7 with the same parameters as the original GetAuthenticatedAttributeBytes call. – mkl yesterday

 

Basicamente, e de acordo com este user, a forma de criar o container para injetar a assinatura da AMA está errada. Eu já mudei o código para tentar seguir as ideias dele, mas a verdade é que nao esta a funcionar. Já agora, e por curiosidade, o código que estou a usar no método Sign do IExternalSignatureContainer que preparar o hash passou a ser:

public override byte[] Sign(Stream data) {
    // create PCKS7 for getting byte attributes
    var sgn = new PdfPKCS7(null, _certificates.ToArray(), DigestAlgorithms.SHA256, false);
    DocumentDigest = DigestAlgorithms.Digest(data, DigestAlgorithms.SHA256);
    var docBytesHash = sgn.GetAuthenticatedAttributeBytes(DocumentDigest, PdfSigner.CryptoStandard.CMS, null, null);

    var totalHash = new byte[_sha256SigPrefix.Length + docBytesHash.Length];
    _sha256SigPrefix.CopyTo(totalHash, 0);
    docBytesHash.CopyTo(totalHash, _sha256SigPrefix.Length);
    DataToSend = totalHash;


    return new byte[0];
}

 

Como ele diz que depois o GetEncodedPKCS7 tem de ser chamado com os mesmos parametros que o GetAuthenticatedAttributeBytes, enstao esou a guarda o hash e o digest originais. Depois, alterei ainda o Sign do outro IExternalSignatureContainer que é usado para injetar a assinatura recebida da AMA para:

public byte[] Sign(Stream data) {
    var sgn = new PdfPKCS7(null, _certificates.ToArray(), DigestAlgorithms.SHA256, false);
    sgn.SetExternalDigest(_signature, null, "RSA");

    var encodedSig = sgn.GetEncodedPKCS7(_documentHash,
                                         PdfSigner.CryptoStandard.CMS,
                                         null,
                                         null,
                                         null);

    return encodedSig;
}

 

Infelizmente, os resultados sao negativos :(

Link to post
Share on other sites
Labreu

Boas pessoal!

FInalmente, está a funcionar como deve: https://stackoverflow.com/questions/66671008/itext7-deferred-signing-not-producing-a-valid-signed-pdf?noredirect=1#comment117931277_66671008

btw, parece q a assinatura inicial (so com o CMS PdfPCKS7) so e adequado em algumas situacoes. Nos comentarios, o mkl explica a diferenca entre as duas formas de assinar...Agora, ver como faco o timestamp disto! que aventura...

 

Link to post
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.