The Windows code supports reading a PKCS#7 signed or PKCS#7 signed-and-enveloped structure; but in single certificate mode it doesn't return certs[0], it returns the certificate which signed the structure.
My test files (produced by Windows (certmgr.msc and X509Certificate2Collection::Export)) aren't signed, so Windows emits an exception (new CryptographicException(ErrorCode.CRYPT_E_SIGNER_NOT_FOUND)).
Without a sample to see what's going on here, it's hard to make compatible behavior. So, for now, the Unix implementation will just throw, even if it could have worked.
Marking Future explicitly because we have decided thisi s not necessary for 2.0.
Moving to Future, it's just getting a bit late to plumb this down (and a lot of tests need to be defined to ensure that we're behaving the same way as in our Windows black-box implementation... zero signers, multiple signers, igner-cert-not-included, hybridized values, etc)
The workaround for caller code which knows it's getting a PKCS7 is to just load it into the SignedCms class. This is the naive approach (only works for one signer, when the signer cert is present)
```C#
SignedCms cms = new SignedCms();
cms.Decode(bytes);
SignerInfoCollection signers = cms.SignerInfos;
if (signers.Count != 1)
{
throw new CryptographicException("Wrong number of signers.");
}
return signers[0].Certificate ?? throw new CryptograpicException("Signer cert was not present");
```
I was playing with decoding Authenticode, which is also supposed to be supported. Turns out most of the plumbing code is already present in CoreFX (no error handling, just PoC):
var peReader = new PEReader(new MemoryStream(smallspcexe));
// TODO: Check for non-PE files
var certDirectory = peReader.PEHeaders.PEHeader.CertificateTableDirectory;
// TODO: Check for no certificate table
var certData = peReader.GetEntireImage().GetContent(certDirectory.RelativeVirtualAddress, certDirectory.Size);
// TODO: Handle different certificate types (X.509, PKCS#7) and read the length (ie. multiple certificates)
var certData2 = new byte[certData.Length - 8];
certData.CopyTo(8, certData2, 0, certData2.Length);
var certType = X509Certificate2.GetCertContentType(certData2);
var x509 = new X509Certificate2(certData2);
and it boils down to reading PKCS#7 certificates. The last constructor fails with an exception.
We need to be able to verify Authenticode signatures on managed DLLs in Linux for licensing enforcement.
Hello, my project based on linux relying on reading cert from PKCS#7 signed file, which is not working atm. Is this going to be fixed in upcoming .NET Core versions (3.0) or what is the situation around this? I would greatly appreciate any info about this issue and its future.
@Kalyxt Maybe https://github.com/secana/PeNet can do what you want (I'm the author). The PeNet library is able to extract the cert from signed files on Windows, Linux and Mac.
@Kalyxt Maybe https://github.com/secana/PeNet can do what you want (I'm the author). The PeNet library is able to extract the cert from signed files on Windows, Linux and Mac.
Thanks! This is exactly what I was looking for. I've tested it on win and linux-arm.
Is this going to be fixed in upcoming .NET Core versions (3.0)
No, there's not a lot of time left in the 3.0 release and I'm already over committed. (Though PRs are welcome if someone wants to contribute a fix...)
Any updates on the issue regarding the version it will most likely be fixed in? It's not fixed in 3.1 and was already an issue in 2.0.
If someone points me to a starting point, I could try to implement it myself and do PR. Never worked with the dotnet repo, so any hints are welcome.
I'll go ahead and mark it as 5 (aka "next version"). If you want to give it a try, https://github.com/dotnet/runtime/blob/4f9ae42d861fcb4be2fcd5d3d55d5f227d30e723/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/PkcsFormatReader.cs#L226-L236 is where things need to get smarter. Largely what's needed is a test suite. e.g. what happens on Windows with
[OuterLoop] or just a manual test.If you just want the functionality but aren't too keen on playing with pointers, that's fine... it'll still get taken care of this release.
_TODO(2910): Figure out how to extract the signing certificate, when it's present._
Getting the signing certificate on Linux from a PE file (taken from PeNet) is pretty straight forward.
Get the serial number of the signing certificate:
private string? GetSigningSerialNumber()
{
var asn1 = _contentInfo?.Content;
if (asn1 is null) return null;
var x = (Asn1Integer)asn1.Nodes[0].Nodes[4].Nodes[0].Nodes[1].Nodes[1]; // ASN.1 Path to signer serial number: /1/0/4/0/1/1
return x.Value.ToHexString().Substring(2).ToUpper();
}
Get the signing certificate from the PE file with the SignerSerialNumber from above.
private X509Certificate2 GetSigningCertificateNonWindows(PeFile peFile)
{
var collection = new X509Certificate2Collection();
collection.Import(peFile.WinCertificate?.BCertificate.ToArray());
return collection.Cast<X509Certificate2>().FirstOrDefault(cert =>
string.Equals(cert.SerialNumber, SignerSerialNumber, StringComparison.CurrentCultureIgnoreCase));
}
But I'm not sure on how to get access to the Asn1 of the PE file in the PkcsFormarReader.cs @bartonjs linked, so I'm stuck with any implementation attempt before I even get started :(
@secana There are two different issues.