Digitally sign .xlsx file using System.IO.Packaging in c# - digital-signature

Greeings,
I am trying to digitally sign an excel file using System.IO.Packaging but I can't get this working.
The simplified code I am using is as follows:
var packageFile = #"C:\temp\test.xlsx";
var package = Package.Open(packageFile, FileMode.Open);
var packageDigitalSignatureManager = new PackageDigitalSignatureManager(package);
var uriCollection = package.GetParts().ToList().Select(part => part.Uri);
X509Certificate2 cert = new X509Certificate2(#"D:\certs\adrianganea.pfx", "password", X509KeyStorageFlags.Exportable);
var signature = packageDigitalSignatureManager.Sign(uriCollection, cert);
The document appears to be signed but the signature is not valid:
Invalid Signatures
Any ideas?
Many thanks,
Adrian

Nevermind, it seems that the signing certificate needs to be embedded:
packageDigitalSignatureManager .CertificateOption =CertificateEmbeddingOption.InSignaturePart;
This link contains a good example.

Related

XAdES creation with manual signature entry

I am trying to create a digitally signed XML document using the signature from my ID card.
I have two parts of the program. The first one is getting the certificates and signature of the file from the ID.
For that I am using python PKCS11 library with something like this:
with open("input.xml", "rb") as f:
data = f.read()
lib = lib('path/to/pkcs11/lib.dylib')
token = lib.get_token('name of token')
with token.open(PIN) as session:
certificate = None
for obj in session.get_objects({Attribute.CLASS: ObjectClass.CERTIFICATE}):
certificate = obj
der_bytes = certificate[Attribute.VALUE]
with open('certificate.der', "wb") as f:
f.write(der_bytes)
# calculate SHA256 of data
digest = session.digest(data, mechanism=Mechanism.SHA256)
for obj in session.get_objects({Attribute.CLASS: ObjectClass.PRIVATE_KEY}):
private_key = obj
signature = private_key.sign(digest, mechanism=Mechanism.RSA_PKCS)
with open('signature', "wb") as f:
f.write(signature)
That generates the certificate.der and signature files and is working properly (at least I think)
For the XML generation part I am using Europe's DSS library in Java like this:
DSSDocument toSignDocument = new FileDocument("input.xml");
// Preparing parameters for the XAdES signature
XAdESSignatureParameters parameters = new XAdESSignatureParameters();
// We choose the level of the signature (-B, -T, -LT, -LTA).
parameters.setSignatureLevel(SignatureLevel.XAdES_BASELINE_B);
// We choose the type of the signature packaging (ENVELOPED, ENVELOPING, DETACHED).
parameters.setSignaturePackaging(SignaturePackaging.ENVELOPED);
// We set the digest algorithm to use with the signature algorithm. You must use the
// same parameter when you invoke the method sign on the token. The default value is SHA256 parameters.setDigestAlgorithm(DigestAlgorithm.SHA256);
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
InputStream in = new FileInputStream("certificate.der");
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(in);
// We set the signing certificate
parameters.setSigningCertificate(new CertificateToken(cert));
// Create common certificate verifier
CommonCertificateVerifier commonCertificateVerifier = new CommonCertificateVerifier();
// Create XAdES service for signature
XAdESService service = new XAdESService(commonCertificateVerifier);
// Get the SignedInfo XML segment that need to be signed.
ToBeSigned dataToSign = service.getDataToSign(toSignDocument, parameters);
File file = new File("signature");
SignatureValue signatureValue = new SignatureValue(SignatureAlgorithm.RSA_SHA256, Files.readAllBytes(file.toPath()));
// We invoke the service to sign the document with the signature value obtained in
// the previous step.
DSSDocument signedDocument = service.signDocument(toSignDocument, parameters, signatureValue);
File signedFile = new File("output.xml");
signedFile.createNewFile();
signedDocument.writeTo(new FileOutputStream(signedFile, false));
That creates XAdES file, but when I try to validate the signature (e.g. using this) it fails saying the signature is not intact.
What am I doing wrong?
You do not use dataToSign variable at all for signature value creation.
What you should do is by using the private key corresponding to the created certificate to actually sign the digested dataToSign. I.e., instead of:
File file = new File("signature");
SignatureValue signatureValue = new SignatureValue(SignatureAlgorithm.RSA_SHA256, Files.readAllBytes(file.toPath()));
you should do something like this (using your example above):
# calculate SHA256 of data
digest = session.digest(dataToSign, mechanism=Mechanism.SHA256)
for obj in session.get_objects({Attribute.CLASS: ObjectClass.PRIVATE_KEY}):
private_key = obj
signatureValue = private_key.sign(digest, mechanism=Mechanism.RSA_PKCS)
Please pay attention, that you shall sign not the original document, but the dataToSign, as it contains the reference to the original document (its digest), but also signed parameters, required to ensure compliance to AdES format.
I hope this will help you.
Best regards,
Aleksandr.

How can I extract the SAN's inside a .csr file in node.js (Serverless Framework)?

Using Serverless Framework with node.js, I need to read information inside a .csr file received via http POST. Using the node-forge module, and with the following code, I was able to extract the different information that composes the certificate signing request:
const forge = require('node-forge');
...
var csr = forge.pki.certificationRequestFromPem(cert);
console.log(csr.subject.attributes)
if(csr.subject.getField('CN'))
var CN = csr.subject.getField('CN').value
if(csr.subject.getField('SAN'))
var SAN = csr.subject.getField('SAN').value
if(csr.subject.getField('O'))
var O = csr.subject.getField('O').value
if(csr.subject.getField('OU'))
var OU = csr.subject.getField('OU').value
if(csr.subject.getField('C'))
var C = csr.subject.getField('C').value
if(csr.subject.getField('ST'))
var S = csr.subject.getField('ST').value
if(csr.subject.getField('L'))
var L = csr.subject.getField('L').value
if(csr.subject.getField('E'))
var E = csr.subject.getField('E').value
What I need right now is to also extract the SAN's of the CSR if they exist, problem is that after inspecting the x509.js file ("Javascript implementation of X.509 and related components (such as
Certification Signing Requests) of a Public Key Infrastructure") that comes within the module, I dot not think there is a way to extract the SAN's:
// short name OID mappings
var _shortNames = {};
_shortNames['CN'] = oids['commonName'];
_shortNames['commonName'] = 'CN';
_shortNames['C'] = oids['countryName'];
_shortNames['countryName'] = 'C';
_shortNames['L'] = oids['localityName'];
_shortNames['localityName'] = 'L';
_shortNames['ST'] = oids['stateOrProvinceName'];
_shortNames['stateOrProvinceName'] = 'ST';
_shortNames['O'] = oids['organizationName'];
_shortNames['organizationName'] = 'O';
_shortNames['OU'] = oids['organizationalUnitName'];
_shortNames['organizationalUnitName'] = 'OU';
_shortNames['E'] = oids['emailAddress'];
_shortNames['emailAddress'] = 'E';
Am I using a deprecated module or old version? Is there a way for me to achieve this and I'm simply using the incorrect stuff inside node-forge?
Please let me know if anyone had a similar problem or was able to overcome this issue. Regards
So, I was able to solve this with the following code:
var csr = forge.pki.certificationRequestFromPem(cert);
var altNames = csr.attributes.find(t=>t.name === 'extensionRequest').extensions.find(t=>t.name === 'subjectAltName').altNames;
I created a dummy certificate with SANs and Apparently node-forge extracts that information and places them inside extensionRequest -> subjectAltNames. I have these hardcoded since I'm assuming these attribute names will not change, but I can't be 100% sure about it.
Hope this helps someone who might have gone through this issue. Thank you.

Playing around with certificates in .net5

I am trying to learn about certificates, the need for that is from an idea I had a few days ago to simplify the ordering and delivery of SSL certificates to my collegues. So I startet to investigate "how hard can it be"?
And to large entusiasm, the entire process was "not so hard", but then I came to "generating pfx certificates", with private key...
The code is from a console test app:
Creating keys and storing it.
int keysize = 2048;
CngKeyCreationParameters ckcParams = new CngKeyCreationParameters()
{
ExportPolicy = CngExportPolicies.AllowPlaintextExport,
KeyCreationOptions = CngKeyCreationOptions.None,
KeyUsage = CngKeyUsages.AllUsages,
};
ckcParams.Parameters.Add(new CngProperty("Length", BitConverter.GetBytes(KeySize), CngPropertyOptions.None));
CngKey myCngKey = CngKey.Create(CngAlgorithm.Rsa, KeyName, ckcParams);
byte[] privatePlainTextBlob = myCngKey.Export(CngKeyBlobFormat.Pkcs8PrivateBlob);
string privateblob_string = Convert.ToBase64String(privatePlainTextBlob);
// Now I can save the Pkcs8PrivateBlob "somewhere"
Later I can pick up this up and create a Certificate Request and send it to a certificate service by API.
byte[] cngbytes = Convert.FromBase64String( privateblob_string );
CngKey importedkey = CngKey.Import(cngbytes, CngKeyBlobFormat.Pkcs8PrivateBlob, CngProvider.MicrosoftSoftwareKeyStorageProvider);
RSACng rsa = new RSACng(importedkey);
request = new CertificateRequest(
new X500DistinguishedName(order.CertName),
rsa,
HashAlgorithmName.SHA512,
RSASignaturePadding.Pkcs1);
etc....
Now I can download the issued certificate in various formats from the API.
So far so good. Now I want to create PFX file and KEY file.
Creating PFX:
// My issued certificate from provider
byte[] PublicKeyStr = System.Text.Encoding.ASCII.GetBytes(CER_STRING);
// pfx
var certificate = new X509Certificate2(PublicKeyStr, string.Empty, X509KeyStorageFlags.Exportable);
byte[] certificateData = certificate.Export(X509ContentType.Pfx, "password");
// Now I have the pfx, but no private key
I am obviously is not capable of solving this. I have been on a journey into Bouncy Castle, with no luck (btw: where is their documentation?).
I have noticed that .net5 has a metode that I thought (again) might solve this.
X509Certificate2.CreateFromPem(ReadOnlySpan certpem, ReadOnlySpan keypem)
But then I need to get the keypem "private key pem".
My question is simple: Have I totally misunderstod or is the any way to add the needed private key to the pfx file with the information I have stored from the CngKey?
ANY suggestions, ideas, help, tips will be very welcomed. It is simply so frustrating to be so close and just fail miserably.
You need to associate the public certificate with the private key before exporting it as a PFX.
// Key
byte[] cngbytes = Convert.FromBase64String( privateblob_string );
CngKey importedkey = CngKey.Import(cngbytes, CngKeyBlobFormat.Pkcs8PrivateBlob, CngProvider.MicrosoftSoftwareKeyStorageProvider);
RSACng rsa = new RSACng(importedkey);
// Cert
byte[] PublicKeyStr = System.Text.Encoding.ASCII.GetBytes(CER_STRING);
var certificate = new X509Certificate2(PublicKeyStr);
// Together:
X509Certificate2 certWithKey = certificate.CopyWithPrivateKey(rsa);
// PFX:
byte[] pfx = certWithKey.Export(X509ContentType.Pfx, pwd);

Signature is Invalid after attaching certificate to the processed pdf document. i am unable to get if there is an issue in code or certificate

I have a signed pdf I am attaching a certificate(.pfx) to the document through itextsharp. Everything in the code is tested and working fine but when I download and open the pdf in acrobat reader it says the signature is not valid I have changed preferences tried almost every setting since yesterday but there isn't any luck.
two things I noticed in certificate detail that for its "intended" property: the DIGITAL signature is not mentioned whereas encrypt document etc is mentioned is this the reason it is not validating the document for signature.
and the second thing it says: certificate has error: not valid for usage
code for attaching certificate;
var pathCert =
Server.MapPath("..../App_Data/Certificates/.....sdd.pfx");
string Password = "**************";
var pass = Password.ToCharArray();
System.Security.Cryptography.X509Certificates.X509Store store =
new System.Security.Cryptography.X509Certificates.X509Store
(Cryptography.X509Certificates.StoreLocation.CurrentUser);
store.Open(System.Security.
Cryptography.X509Certificates.OpenFlags.ReadOnly);
string PfxFileName = pathCert;
string PfxPassword = Password;
System.Security.Cryptography.X509Certificates.X509Certificate2 cert = new
System.Security.Cryptography.X509Certificates.X509Certificate2
(PfxFileName, PfxPassword, Security.Cryptography.X509Certificates.
X509KeyStorageFlags.MachineKeySet);
string SourcePdfFileName = "(Directory)/Desktop/tetsing/test.pdf";
string DestPdfFileName = "(Directory)/Desktop/tetsing/test_Signed.pdf";
Org.BouncyCastle.X509.X509CertificateParser cp = new
Org.BouncyCastle.X509.X509CertificateParser();
Org.BouncyCastle.X509.X509Certificate[] chain = new
Org.BouncyCastle.X509.X509Certificate[] {
cp.ReadCertificate(cert.RawData) };
iTextSharp.text.pdf.security.IExternalSignature externalSignature = new
iTextSharp.text.pdf.security.X509Certificate2Signature(cert, "SHA-1");
PdfReader pdfReader = new PdfReader(SourcePdfFileName);
FileStream signedPdf = new FileStream(DestPdfFileName, FileMode.Create);
//the output pdf file
PdfStamper pdfStamper = PdfStamper.CreateSignature(pdfReader, signedPdf,
'\0');
PdfSignatureAppearance signatureAppearance =
pdfStamper.SignatureAppearance;
signatureAppearance.Reason = "Signed Document";
signatureAppearance.Location = "Unknown";
signatureAppearance.SignatureRenderingMode =
PdfSignatureAppearance.RenderingMode.DESCRIPTION;
MakeSignature.SignDetached(signatureAppearance, externalSignature,
chain,
null, null, null, 0, CryptoStandard.CMS);
pdfReader.Close();
Adobe acrobat reader is very picky on the certificate key usage and intended purpose (Key Usage and Enhanced Key Usage) and other details of the certificate. Have you tried a certificate with Digital Signature as key usage and Code Signing as intended purpose?
Here is a blog post that shows how to self sign a certificate with that properties for doing signatures if you do not have access to a real publicly trusted signing certificate.
certificate has error: not valid for usage
According to the Adobe Digital Signatures Guide for IT, Adobe Acrobat accepts only
one or more of the following Key usage values (if any)
nonRepudiation
signTransaction (11.0.09 only)
digitalSignature (11.0.10 and later)
and one or more of the following Extended key usage values (if any)
emailProtection
codeSigning
anyExtendedKeyUsage
1.2.840.113583.1.1.5 (Adobe Authentic Documents Trust)
Please check your certificate accordingly and replace it if it does not fulfill this condition.

How to use NodeJS crypto to sign a file?

I want to use nodeJS to sign a file. I got one p12 certificate (which includes the private key), a passphrase and a pem certificate.
This here shows how it is been done in ruby:
https://gist.github.com/de4b602a213b4b264706
Thanks in advance!
You should be able to use createSign in the crypto module (see http://nodejs.org/docs/v0.4.2/api/all.html#crypto) to do what you want. The code will end up looking something like this (from http://chimera.labs.oreilly.com/books/1234000001808/ch05.html#chap7_id35952189):
var crypto = require('crypto');
var fs = require('fs');
var pem = fs.readFileSync('key.pem');
var key = pem.toString('ascii');
var sign = crypto.createSign('RSA-SHA256');
sign.update('abcdef'); // data from your file would go here
var sig = sign.sign(key, 'hex');

Resources