A standardized approach to digital file signatures via .NET.

I am creating a system for distributing packages (.zip archives) created by different organizations. I would like to make sure that the publisher of the package is really the one they claim and that the file has not been tampered with.

To verify the publisher, you need a system similar to that used by web browsers - for example, my application contacts the root certification authorities that verify the identity. In other words, the "green bar" :)

I assume that creating a package will work as follows:

  • The author creates a zip package
  • Author hash package and sign hash
  • It is repackaged, with:
    • A header containing a signed hash and a public certificate
    • The body containing the contents of the zip file

Opening a package will work as follows:

  • Take a data body
  • Hash it using the same algorithm.
  • Decrypt the hash of the packet using the public key from the certificate
  • Compare two hashes - now we have integrity
  • Contact root certification authorities to verify identity

Thus, I checked the identifier and also checked the content (the content itself does not need to be encrypted - the goal is verification, not privacy).

So my questions are:

  • Is this right for him?
  • What hashing algorithm do people usually use? I suppose this should be one-way. Would you just choose one of them (MD5, SHA1, SHA2?) Or more normal to support diversity, and let the package author tell you which one he used (for example, the document title contains the name of the hash function).
  • ? X509Store ?
  • ? , .NET? ( ?)

, , , ( ) ( : " XYZ Co. (Unverified)". ?

, X509 RSACryptoServiceProvider, , , , , , .

+5
2

API ZIP-.

System.IO.Packaging OPS ( ) .

+15

aku System.IO.Packaging , (-, ). , , , - .

, "", PFX, . :

, PFX. .cer, , GUI, pvkimport Microsoft.

, (: OpenSSL - . ):

makecert -r -n "CN=Paul Stovell" -b 01/01/2000 -e 01/01/2099 -eku 1.3.6.1.5.5.7.3.3 -sv PaulStovell.pvk PaulStovell.cer
cert2spc PaulStovell.cer PaulStovell.spc
pvkimprt -pfx PaulStovell.spc PaulStovell.pvk

. , , PFX. "". , " ", "".

PFX, .

, , .

private const string _digitalSignatureUri = "/package/services/digital-signature/_rels/origin.psdsor.rels";

static void Main(string[] args)
{
    var certificate = new X509Certificate2(@"T:\Sample\Input\PaulStovell.pfx", "password");
    using (var package = Package.Open("T:\\Sample\\MyPackage.zip", FileMode.Create, FileAccess.ReadWrite, FileShare.None))
    {
        CreatePart(package, @"/Files/File2.dll", @"T:\Sample\Input\File2.dll");
        CreatePart(package, @"/Files/File2.pdb", @"T:\Sample\Input\File2.pdb");
        CreatePart(package, @"/Files/File2.xml", @"T:\Sample\Input\File2.xml");
        package.PackageProperties.Creator = "Paul Stovell";
        package.PackageProperties.Title = "Paul Stovell Package";
        package.PackageProperties.Description = "My First Package";
        package.PackageProperties.Identifier = "MyPackage";
        package.PackageProperties.Version = "1.0.0.0";

        // Sign the package
        var toSign = package.GetParts().Select(part => part.Uri).ToList();
        var uriPartSignatureOriginRelationship = PackUriHelper.CreatePartUri(new Uri(_digitalSignatureUri, UriKind.Relative));
        toSign.Add(uriPartSignatureOriginRelationship);

        var dsm = new PackageDigitalSignatureManager(package);
        dsm.CertificateOption = CertificateEmbeddingOption.InSignaturePart;
        dsm.Sign(toSign, certificate);

        package.Close();
    }

    Console.WriteLine("Package written");
    Console.WriteLine("Reading package");

    using (var package = Package.Open("T:\\Sample\\MyPackage.zip", FileMode.Open, FileAccess.Read, FileShare.Read))
    {
        Console.WriteLine("  Package name: {0}", package.PackageProperties.Title);
        var dsm = new PackageDigitalSignatureManager(package);
        if (dsm.IsSigned)
        {
            var verificationResult = dsm.VerifySignatures(false);
            var signature = dsm.Signatures[0];
            Console.WriteLine("  Signed by: {0}", signature.Signer.Subject);
            Console.WriteLine("  Issued by: {0}", signature.Signer.Issuer);
            Console.WriteLine("  Verification: {0}", verificationResult);
        }
        else
        {
            Console.WriteLine("  Not signed.");
        }
    }
    Console.ReadKey();
}

private static void CreatePart(Package package, string relativePath, string file)
{
    var packagePartUri = new Uri(relativePath, UriKind.Relative);
    var packagePart = package.CreatePart(packagePartUri, "part/" + Path.GetExtension(file));
    using (var fileContent = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
    {
        CopyStream(fileContent, packagePart.GetStream());
    }
}

private static void CopyStream(Stream source, Stream target)
{
    // It is .NET 3.5, surely this kind of thing has gotten easier by now?
    var bufferSize = 0x1000;
    var buf = new byte[bufferSize];
    int bytesRead = 0;
    while ((bytesRead = source.Read(buf, 0, bufferSize)) > 0)
    {
        target.Write(buf, 0, bytesRead);
    }
}

:

Package written
Reading package
  Package name: Paul Stovell Package
  Signed by: CN=Paul Stovell
  Issued by: CN=Paul Stovell
  Verification: Success

. PackageDigitalSignatureManager.VerifySignatures(). , . , "".

.exe, sample .pfx , , , , "Verify" . , .

. , CA. :

false :

var verified = ((X509Certificate2) dsm.Signatures[0].Signer).Verify();

.cer, .pfx, , . , true. , .

2. . , :

  • - > "mmc"
  • MMC "- > / SnapIn...
  • , →
  • OK

, .cer , , Current User Trusted Root Certification Authority. , .

:

if (dsm.IsSigned)
{
    var verificationResult = dsm.VerifySignatures(false);
    var signature = dsm.Signatures[0];
    var trusted = ((X509Certificate2)dsm.Signatures[0].Signer).Verify();

    Console.WriteLine("  Signed by: {0}", signature.Signer.Subject);
    Console.WriteLine("  Issued by: {0}", signature.Signer.Issuer);
    Console.WriteLine("  Verified: {0}", verificationResult == VerifyResult.Success);
    Console.WriteLine("  Trusted: {0}", trusted);
}
else
{
    Console.WriteLine("  Not signed.");
}

( ), , .

3. . PFX PKCS 12, RSA. , , , - OpenSSL, Windows.

OpenSSL / . , , .

openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout PaulStovell.pem -out PaulStovell.cer

7 . PKCS 12. :

openssl pkcs12 -export -out PaulStovell.pfx -in PaulStovell.pem -name "Paul Stovell"

.

System.IO.Packaging PFX , . .cer, , :

openssl x509 -in PaulStovell.pem -out PaulStovell.cer

, .

+16

All Articles