Which certificates are required for iText.Signatures.OcspClientBouncyCastle GetEncoded - pdf

I want to set the ocsp when signing pdf document using iText
The ocsp is created using the following code:
OCSPVerifier ocspVerifier = new OCSPVerifier(null, null); // can be null based on https://stackoverflow.com/questions/40765907/itextsharp-ocspclientbouncycastle-constructor-is-deprecated-whats-the-replacem
IOcspClient ocspClient = new OcspClientBouncyCastle(ocspVerifier);
var test = ocspClient.GetEncoded(rootCertificate, cert, "http://my-url.com");
The result of this code is null.
I guess it's because i set the wrong certificates.
The first parameter is named checkCert and the second one issuerCert
Note: I'm using iText version 7.1. It seems the names were changed to checkCert/rootCert in version 7.7
What exactly are checkCert and issuerCert?
I tried with the signing root certificate as checkCert and the signature certificate as issuerCert but it doesn't work.
Any idea or explanation which certificates are required here?
Edit:
The following is the code which i use to create the pkcs7 container which should include the ocsp:
OCSPVerifier ocspVerifier = new OCSPVerifier(null, null); // null,null >https://stackoverflow.com/questions/40765907/itextsharp-ocspclientbouncycastle-constructor-is-deprecated-whats-the-replacem
IOcspClient ocspClient = new OcspClientBouncyCastle(ocspVerifier);
var ocsp = ocspClient.GetEncoded(cert, rootCertificate, "http://www.my.com/aia/ocsp");
if (ocsp == null)
Console.WriteLine("oscp is null");
else
Console.WriteLine("ocsp is not null");
//Create the pkcs7 container
PdfPKCS7 sgn = new PdfPKCS7(null, c.ToArray(), HashAlgorithm, false);
byte[] sh = sgn.GetAuthenticatedAttributeBytes(hash, ocsp, null, PdfSigner.CryptoStandard.CMS);
//Load the signature via pkcs11 hardware
byte[] extSignature = GetSignatureFromHashViaPkcs11(sh, pin);
sgn.SetExternalDigest(extSignature, null, DigestEncryptionAlgorithm);
var ret = sgn.GetEncodedPKCS7(hash, tsaClient, ocsp, null, PdfSigner.CryptoStandard.CMS);
No matter how i set the certificates in ocspClient.GetEncoded, the resulting ocsp is always null
Even a more fundamental question: Are the issuer and root certificate the certificates i use for creating the signature or are those the certificates from the ocsp?

Related

Validate EC SHA 256 signature in .net without bouncy castle

I am implementing Apple's App Attestation service.
As part of the process, i receive a EC key and a signature.
Sample key:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEd34IR9wYL76jLyZ148O/hjXo9iaF
z/q/xEMXCwYPy6yxbxYzWDZPegG4FH+snXaXQPYD6QIzZNY/kcMjIGtUTg==
-----END PUBLIC KEY-----
Sample signature:
MEUCIQDXR/22YAi90PUdKrtTHwigrDxWFoiCqPLB/Of1bZPCKQIgNLxFAeUU2x+FSWfhRGX0SOKUIDxPRoigsCHpJxgGXXU=
Sample sha256 hash:
S3i6LAEzew5SDjQbq59/FraEAvGDg9y7fRIfbnhHPf4=
If i put this into a couple of txt files like so:
System.IO.File.WriteAllBytes("/wherever/sig", Convert.FromBase64String(sampleSignature));
System.IO.File.WriteAllBytes("/wherever/hash", Convert.FromBase64String(sampleSha256Hash));
Then i can validate the signature with Openssl like so
openssl dgst -sha256 -verify sampleKey.pem -signature /wherever/sig /wherever/hash
(the above outputs)
Verified OK
I can verify the signature using Bouncy Castle like so:
var bouncyCert = DotNetUtilities.FromX509Certificate(certificate);
var bouncyPk = (ECPublicKeyParameters)bouncyCert.GetPublicKey();
var verifier = SignerUtilities.GetSigner("SHA-256withECDSA");
verifier.Init(false, bouncyPk);
verifier.BlockUpdate(sha256HashByteArray, 0, sha256HashByteArray.Length);
var valid = verifier.VerifySignature(signature); // Happy days, this is true
Since i don't want to share my whole certificate here, the same sample may be achieved as follows:
// these are the values from the sample key shared at the start of the post
// as returned by BC. Note that .Net's Y byte array is completely different.
Org.BouncyCastle.Math.BigInteger x = new Org.BouncyCastle.Math.BigInteger(Convert.FromBase64String("d34IR9wYL76jLyZ148O/hjXo9iaFz/q/xEMXCwYPy6w="));
Org.BouncyCastle.Math.BigInteger y = new Org.BouncyCastle.Math.BigInteger(Convert.FromBase64String("ALFvFjNYNk96AbgUf6yddpdA9gPpAjNk1j+RwyMga1RO"));
X9ECParameters nistParams = NistNamedCurves.GetByName("P-256");
ECDomainParameters domainParameters = new ECDomainParameters(nistParams.Curve, nistParams.G, nistParams.N, nistParams.H, nistParams.GetSeed());
var G = nistParams.G;
Org.BouncyCastle.Math.EC.ECCurve curve = nistParams.Curve;
Org.BouncyCastle.Math.EC.ECPoint q = curve.CreatePoint(x, y);
ECPublicKeyParameters pubkeyParam = new ECPublicKeyParameters(q, domainParameters);
var verifier = SignerUtilities.GetSigner("SHA-256withECDSA");
verifier.Init(false, pubkeyParam);
verifier.BlockUpdate(sha256HashByteArray, 0, sha256HashByteArray.Length);
var valid = verifier.VerifySignature(signature); // again, happy days.
However, i really want to avoid using bouncy castle.
So i am trying to use ECDsa available in .net core:
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
var certificate = new X509Certificate2(cert);
var publicKey = certificate.GetECDsaPublicKey();
var valid = publicKey.VerifyHash(sha256HashByteArray, signature); // FALSE :(
if you want to try to run the above here's the sample that creates the keys without the whole certificate:
using System.Security.Cryptography;
var ecParams = new ECParameters();
ecParams.Curve = ECCurve.CreateFromValue("1.2.840.10045.3.1.7");
ecParams.Q.X = Convert.FromBase64String("d34IR9wYL76jLyZ148O/hjXo9iaFz/q/xEMXCwYPy6w=");
// I KNOW that this is different from BC sample - i got each respective values from
// certificates in respective libraries, and it seems the way they format the coordinates
// are different.
ecParams.Q.Y = Convert.FromBase64String("sW8WM1g2T3oBuBR/rJ12l0D2A+kCM2TWP5HDIyBrVE4=");
var ecDsa = ECDsa.Create(ecParams);
var isValid = ecDsa.VerifyHash(nonce, signature); // FALSE :(
I tried using VerifyData() instead and feeding raw data and HashAlgorithmName.SHA256 with no luck.
I found a response here (https://stackoverflow.com/a/49449863/2057955) that seems to suggest that .net expects the signature as r,s concatenation, so i pulled them out of the DER sequence that i get back from my device (see sample signature) however that had no luck at all, i just can't get that 'true' back.
Question: how can i verify this EC signature using .Net Core on LINUX/MacOs (so unable to use ECDsaCng class)?
SignerUtilities.GetSigner() hashes implicitly, i.e. sha256HashByteArray is hashed again. Therefore instead of ECDsa#VerifyHash() (does not hash implicitly) the method ECDsa#VerifyData() (hashes implicitly) must be used.
Also, SignerUtilities.GetSigner() returns a signature in ASN.1 format, and ECDsa#VerifyData() expects a signature in r|s format (as you already figured out).
If both are taken into account, the verification is successful:
byte[] publicKey = Convert.FromBase64String("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEd34IR9wYL76jLyZ148O/hjXo9iaFz/q/xEMXCwYPy6yxbxYzWDZPegG4FH+snXaXQPYD6QIzZNY/kcMjIGtUTg==");
byte[] sha256HashByteArray = Convert.FromBase64String("S3i6LAEzew5SDjQbq59/FraEAvGDg9y7fRIfbnhHPf4=");
byte[] signatureRS = Convert.FromBase64String("10f9tmAIvdD1HSq7Ux8IoKw8VhaIgqjywfzn9W2Twik0vEUB5RTbH4VJZ+FEZfRI4pQgPE9GiKCwIeknGAZddQ==");
var ecDsa = ECDsa.Create();
ecDsa.ImportSubjectPublicKeyInfo(publicKey, out _);
var isValid = ecDsa.VerifyData(sha256HashByteArray, signatureRS, HashAlgorithmName.SHA256);
Console.WriteLine(isValid); // True
Regarding the signature formats:
The posted signature in ASN.1 format
MEUCIQDXR/22YAi90PUdKrtTHwigrDxWFoiCqPLB/Of1bZPCKQIgNLxFAeUU2x+FSWfhRGX0SOKUIDxPRoigsCHpJxgGXXU=
is hex encoded
3045022100d747fdb66008bdd0f51d2abb531f08a0ac3c56168882a8f2c1fce7f56d93c229022034bc4501e514db1f854967e14465f448e294203c4f4688a0b021e92718065d75
From this, the signature in r|s format can be derived as (s. here)
d747fdb66008bdd0f51d2abb531f08a0ac3c56168882a8f2c1fce7f56d93c22934bc4501e514db1f854967e14465f448e294203c4f4688a0b021e92718065d75
or Base64 encoded:
10f9tmAIvdD1HSq7Ux8IoKw8VhaIgqjywfzn9W2Twik0vEUB5RTbH4VJZ+FEZfRI4pQgPE9GiKCwIeknGAZddQ==

Use Custom CNG provider to get Private key from the HSM

I have our own CNG provider. Using c# with .net framework 4.6.1 with window 7. I am using clrsecurity.
string fp = "223298a5c7c9f78a42d83a5ffbxxxxxxxx";
//string fp = "331ffa497d90d19446171f85xxxxxxxx"; //MS
// Load the certificate with the specified serial number
X509Store store = new X509Store(StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certificates = store.Certificates.Find(X509FindType.FindBySerialNumber, fp, false);
// check if at least one certificate has been found
if (certificates.Count != 1)
{
throw new Exception("The certificate with the serial number " + fp + " could not be found.");
}
X509Certificate2 cert = certificates[0];
CngKey cngKey = null;
if (cert.HasCngKey())
{
//rsa = cert.GetRSAPrivateKey();
cngKey = cert.GetCngPrivateKey();
}
Property of cngKey:
The problem is I am not able to set the provider name into CngKey object.
So how to use the clrsecurity dll for non Microsoft KSP.

placing the separately signed hash to Multiple places in PDF using itextsharp

I want to place same externally signed hash (signature value) at multiple places in a PDF.
I have referred the page 'how-to-place-the-same-digital-signatures-to-multiple-places-in-pdf-using-itextsh' and tried to implement the work around provided by mkl (please refer this How to place the Same Digital signatures to Multiple places in PDF using itextsharp.net).
And it works. I ported it to get the signer bytes signed externally using web service/ api and it also works. Now due to one of the requirement I changed the way hash is being calculated.
now instead of (old one):
byte[] hash = DigestAlgorithms.Digest(data, "SHA256");
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
I am trying to use (new one):
int contentEstimated=8192;
HashAlgorithm sha = new SHA256CryptoServiceProvider();
int read = 0;
byte[] buff = new byte[contentEstimated];
while ((read = data.Read(buff, 0, contentEstimated)) > 0)
{
sha.TransformBlock(buff, 0, read, buff, 0);
}
sha.TransformFinalBlock(buff, 0, 0);
byte[] hash = Org.BouncyCastle.Utilities.Encoders.Hex.Encode(sha.Hash);
string hashtext = Encoding.UTF8.GetString(hash, 0, hash.Length); //for writing it to file or sharing it to another api
byte[] hash1 = StringToByteArray(hashtext);
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash1, null, null, CryptoStandard.CMS); or
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS); //tried both
and if I try to use it in existing implementation, the signature gets invalid with an error "Document has been altered or corrupt since it was signed". Can you please tell me where I am doing it wrong?
At most of the referred pages, they have used this hash generation method with an embed function where the calculated hash is getting embedded in the pdf,
byte[] paddedSig = new byte[csize];
System.Array.Copy(pk, 0, paddedSig, 0, pk.Length);
PdfDictionary dic2 = new PdfDictionary();
dic2.Put(PdfName.CONTENTS, new PdfString(paddedSig).SetHexWriting(true));
appearance.Close(dic2);
Thank you.
- Tanmay
In comments the OP clarified that he wants to adapt the solution here to using an external signing service which accepts a document hash (more exactly a SHA256 hash value of the document in Hex format) and returns a full-fledged CMS signature container.
In this case the original AllPagesSignatureContainer method Sign
public byte[] Sign(Stream data)
{
String hashAlgorithm = externalSignature.GetHashAlgorithm();
PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);
IDigest messageDigest = DigestUtilities.GetDigest(hashAlgorithm);
byte[] hash = DigestAlgorithms.Digest(data, hashAlgorithm);
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
byte[] extSignature = externalSignature.Sign(sh);
sgn.SetExternalDigest(extSignature, null, externalSignature.GetEncryptionAlgorithm());
return sgn.GetEncodedPKCS7(hash, null, null, null, CryptoStandard.CMS);
}
has to be changed to not itself create a CMS container using the PdfPKCS7 sgn but merely calculate the document hash, send it to the service and use the container returned by the service:
public byte[] Sign(Stream data)
{
String hashAlgorithm = externalSignature.GetHashAlgorithm();
IDigest messageDigest = DigestUtilities.GetDigest(hashAlgorithm);
byte[] hash = DigestAlgorithms.Digest(data, hashAlgorithm);
byte[] hexHash = Org.BouncyCastle.Utilities.Encoders.Hex.Encode(hash);
string hexHashString = Encoding.UTF8.GetString(hexHash , 0, hexHash.Length);
var response = [... call service with document hash hexHashString ...];
byte[] signatureContainer = [... extract binary CMS container from response ...];
return signatureContainer;
}
The OP has not mentioned anything about the response format, so I cannot say more about the extract binary CMS container from response part. It may include selecting one attribute of a larger response structure, it may include decoding an encoded value (probably a Hex encoded string), ...

the signature includes en embedded timeStamp but it could not be verified

I've just added timestamp to my pdf. signature is valid. timestamp token is correct too (I checked already). but adobe reader tells me that "signature includes an embedded timestamp but it could not be veridied".
AttributeTable unsigned = signerInformation.getUnsignedAttributes();
Hashtable<ASN1ObjectIdentifier, Attribute> unsignedAttrHash = null;
if (unsigned == null) {
unsignedAttrHash = new Hashtable<ASN1ObjectIdentifier, Attribute>();
} else {
unsignedAttrHash = signerInformation.getUnsignedAttributes().toHashtable();
}
unsignedAttrHash.put(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken, signatureTimeStamp);
SignerInformation newSignertInformation = SignerInformation.replaceUnsignedAttributes(si, new AttributeTable(unsignedAttrHash));
I fount this code at stackowerflow. it works. it's really correct code. finally I have new SignerInformationStore and new CMS Signed Data like this
CMSSignedData.replaceSigners(oldCMSSignedData, newSignerStore);
but maybe something is missing in my PDF? certificate or something like that?
that's sample pdf
The message imprint in the signature-time-stamp seems to be not correct. It is expected to have the SHA256 of the signature value in this message imprint.
SHA256 of the signature value:
1b4532052d612ca32ae96b9a8e7aa6d64ae6c69dc00e1b7b31394ac3b54c4049
The message imprint in the time-stamp token:
E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855

creating pkcs12 return null in c code for mac project

I'm using openSSL to create a pkcs12 file in a mac project.
This method does not return null in my environment, but instead in the customer's environment. I'm unable to reproduce the problem in my environment.
Here is the code, what do you think ? should I install the openSSL library in the customer's environment? I'm new to this library.
thanks.
#include "PKCS12Util.h"
BUF_MEM* createPKCS12File(char* pkcs7_pem, BIO* pkey_bio, char* password, char* name) {
X509 *cert;
EVP_PKEY* pkey;
STACK_OF(X509) *cacert = sk_X509_new_null();
PKCS12 *pk12;
if (BIO_eof(pkey_bio)) {
BIO_reset(pkey_bio);
}
pkey = PEM_read_bio_PrivateKey(pkey_bio, NULL, NULL, NULL);
if (!pkey) {
fprintf(stderr, "Error constructing pkey from pkey_bio\n");
ERR_print_errors_fp(stderr);
}
SSLeay_add_all_algorithms();
ERR_load_crypto_strings();
pkcs7_pem = make_PEM(pkcs7_pem);
BIO *pkcs7_pem_bio = BIO_new_mem_buf((void *)pkcs7_pem, (int)strlen(pkcs7_pem));
PKCS7 *pkcs7 = PEM_read_bio_PKCS7(pkcs7_pem_bio, NULL, NULL, NULL);
if (!pkcs7) {
fprintf(stderr, "Error:\n");
ERR_print_errors_fp(stderr);
}
STACK_OF(X509) *pk7_certs = pkcs7->d.sign->cert;
// the first cert is the ca root cert, the last one is the client cert
cert = sk_X509_value(pk7_certs, sk_X509_num(pk7_certs) - 1);
sk_X509_push(cacert, sk_X509_value(pk7_certs, 0));
pk12 = PKCS12_create(password, name, pkey, cert, cacert, 0,0,0,0,0);
if(!pk12) {
fprintf(stderr, "Error creating PKCS#12 structure\n");
ERR_print_errors_fp(stderr);
return NULL;
}
BIO* pk12_bio = BIO_new(BIO_s_mem());
i2d_PKCS12_bio(pk12_bio, pk12);
// get the BUF_MEM from the BIO to return it
BUF_MEM *bptr;
BIO_get_mem_ptr(pk12_bio, &bptr);
BIO_set_close(pk12_bio, BIO_NOCLOSE); // So BIO_free() leaves BUF_MEM alone
PKCS12_free(pk12);
BIO_free(pkcs7_pem_bio);
BIO_free(pk12_bio);
return bptr;
}
I found the problem.
I was trying to compact a private key with a "MisMatched-Intermediate" certificate in the certificate chain.