Runtime: Bring back System.IO.Packaging API

Created on 29 May 2018  路  12Comments  路  Source: dotnet/runtime

Much of the functionality was previously subsetted from System.IO.Packaging. We need to bring this back for .NETCore 3.0. We also need to decide if it goes in System.IO.Packaging or if we merely provide a means for extending System.IO.Packaging by some other assembly. We also have to consider the implementation of below API and what's required. I believe some was directly interacting with ZLIB in the past. If we want to facilitate that as public API we may need to expose more from System.IO.Compression.

Here's the full set of API:
c# namespace System.IO.Packaging { public enum CertificateEmbeddingOption { InCertificatePart = 0, InSignaturePart = 1, NotEmbedded = 2, } public class EncryptedPackageEnvelope : IDisposable { public FileAccess FileOpenAccess { get; } public PackageProperties PackageProperties { get; } public RightsManagementInformation RightsManagementInformation { get; } public StorageInfo StorageInfo { get; } public void Close(); public static EncryptedPackageEnvelope Create(Stream envelopeStream, PublishLicense publishLicense, CryptoProvider cryptoProvider); public static EncryptedPackageEnvelope Create(string envelopeFileName, PublishLicense publishLicense, CryptoProvider cryptoProvider); public static EncryptedPackageEnvelope CreateFromPackage(Stream envelopeStream, Stream packageStream, PublishLicense publishLicense, CryptoProvider cryptoProvider); public static EncryptedPackageEnvelope CreateFromPackage(string envelopeFileName, Stream packageStream, PublishLicense publishLicense, CryptoProvider cryptoProvider); public void Dispose(); protected virtual void Dispose(bool disposing); public void Flush(); public Package GetPackage(); public static bool IsEncryptedPackageEnvelope(Stream stream); public static bool IsEncryptedPackageEnvelope(string fileName); public static EncryptedPackageEnvelope Open(Stream envelopeStream); public static EncryptedPackageEnvelope Open(string envelopeFileName); public static EncryptedPackageEnvelope Open(string envelopeFileName, FileAccess access); public static EncryptedPackageEnvelope Open(string envelopeFileName, FileAccess access, FileShare sharing); } public delegate void InvalidSignatureEventHandler(object sender, SignatureVerificationEventArgs e); public abstract class Package : IDisposable { // Existing type, below are added members protected Package(FileAccess openFileAccess, bool streaming); } public class PackageDigitalSignature { public CertificateEmbeddingOption CertificateEmbeddingOption { get; } public Signature Signature { get; set; } public PackagePart SignaturePart { get; } public string SignatureType { get; } public byte[] SignatureValue { get; } public ReadOnlyCollection<Uri> SignedParts { get; } public ReadOnlyCollection<PackageRelationshipSelector> SignedRelationshipSelectors { get; } public X509Certificate Signer { get; } public DateTime SigningTime { get; } public string TimeFormat { get; } public List<string> GetPartTransformList(Uri partName); public VerifyResult Verify(); public VerifyResult Verify(X509Certificate signingCertificate); } public sealed class PackageDigitalSignatureManager { public PackageDigitalSignatureManager(Package package); public CertificateEmbeddingOption CertificateOption { get; set; } public static string DefaultHashAlgorithm { get; } public string HashAlgorithm { get; set; } public bool IsSigned { get; } public IntPtr ParentWindow { get; set; } public Uri SignatureOrigin { get; } public static string SignatureOriginRelationshipType { get; } public ReadOnlyCollection<PackageDigitalSignature> Signatures { get; } public string TimeFormat { get; set; } public Dictionary<string, string> TransformMapping { get; } public event InvalidSignatureEventHandler InvalidSignatureEvent; public PackageDigitalSignature Countersign(); public PackageDigitalSignature Countersign(X509Certificate certificate); public PackageDigitalSignature Countersign(X509Certificate certificate, IEnumerable<Uri> signatures); public PackageDigitalSignature GetSignature(Uri signatureUri); public void RemoveAllSignatures(); public void RemoveSignature(Uri signatureUri); public PackageDigitalSignature Sign(IEnumerable<Uri> parts); public PackageDigitalSignature Sign(IEnumerable<Uri> parts, X509Certificate certificate); public PackageDigitalSignature Sign(IEnumerable<Uri> parts, X509Certificate certificate, IEnumerable<PackageRelationshipSelector> relationshipSelectors); public PackageDigitalSignature Sign(IEnumerable<Uri> parts, X509Certificate certificate, IEnumerable<PackageRelationshipSelector> relationshipSelectors, string signatureId); public PackageDigitalSignature Sign(IEnumerable<Uri> parts, X509Certificate certificate, IEnumerable<PackageRelationshipSelector> relationshipSelectors, string signatureId, IEnumerable<DataObject> signatureObjects, IEnumerable<Reference> objectReferences); public static X509ChainStatusFlags VerifyCertificate(X509Certificate certificate); public VerifyResult VerifySignatures(bool exitOnFailure); } public static class PackUriHelper { // Existing type, below are added members public static int ComparePackUri(Uri firstPackUri, Uri secondPackUri); public static Uri Create(Uri packageUri); public static Uri Create(Uri packageUri, Uri partUri); public static Uri Create(Uri packageUri, Uri partUri, string fragment); public static Uri GetPackageUri(Uri packUri); public static Uri GetPartUri(Uri packUri); } public class RightsManagementInformation { public CryptoProvider CryptoProvider { get; set; } public void DeleteUseLicense(ContentUser userKey); public IDictionary<ContentUser, UseLicense> GetEmbeddedUseLicenses(); public PublishLicense LoadPublishLicense(); public UseLicense LoadUseLicense(ContentUser userKey); public void SavePublishLicense(PublishLicense publishLicense); public void SaveUseLicense(ContentUser userKey, UseLicense useLicense); } public class SignatureVerificationEventArgs : EventArgs { public PackageDigitalSignature Signature { get; } public VerifyResult VerifyResult { get; } } public class StorageInfo { public string Name { get; } public StreamInfo CreateStream(string name); public StreamInfo CreateStream(string name, CompressionOption compressionOption, EncryptionOption encryptionOption); public StorageInfo CreateSubStorage(string name); public void DeleteStream(string name); public void DeleteSubStorage(string name); public StreamInfo GetStreamInfo(string name); public StreamInfo[] GetStreams(); public StorageInfo GetSubStorageInfo(string name); public StorageInfo[] GetSubStorages(); public bool StreamExists(string name); public bool SubStorageExists(string name); } public class StreamInfo { public CompressionOption CompressionOption { get; } public EncryptionOption EncryptionOption { get; } public string Name { get; } public Stream GetStream(); public Stream GetStream(FileMode mode); public Stream GetStream(FileMode mode, FileAccess access); } public enum VerifyResult { CertificateRequired = 2, InvalidCertificate = 3, InvalidSignature = 1, NotSigned = 5, ReferenceNotFound = 4, Success = 0, } public sealed class ZipPackagePart : PackagePart { protected override Stream GetStreamCore(FileMode modestreamFileMode, FileAccess accessstreamFileAccess); } }

Here's the source: https://referencesource.microsoft.com/#WindowsBase/Base/System/IO/Packaging/EncryptedPackage.cs

To unblock myself I ported that codebase to .NETCore, but didn't attempt to reconcile it with System.IO.Packaging.

area-System.IO.Compression enhancement

All 12 comments

@ericstj it seems this is WPF owned. Do you have a github ID for them?

@vatsan-madhavan and @rladuca

Can I take this one or does it need further discussion?

Are you open to considering new API for this or should that be a separate issue? I've hit issues with public PackageDigitalSignature Sign(IEnumerable<Uri> parts, X509Certificate certificate);

Basically there's no way to account for an external private key. There should be some overload that takes an RSA object (or something else), along with a public cert, and does the signing.

@onovotny please open a separate issue for that. Let's let this issue track reconciling the WPF System.IO.Packaging API and behavior with that which already exists in CoreFx.

@stevenbrix has done this reconciliation. Some API has been added back to System.IO.Packaging.dll in corefx, the rest remains in WindowsBase in WPF.

I need to sign and validate packages in .NETCore 3.1. Can you tell me which files I need to add by myself from the WPF code base?

/cc @stevenbrix @vatsan-madhavan

You should be able to just create a WPF project and go from there. A WPF targeted class-library should look something like this:

<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <UseWPF>true</UseWPF>
  </PropertyGroup>
</Project>

A .NET Core project should be able to access the these API's without having to switch to the WindowsDesktop SDK like this:

  <ItemGroup>
    <FrameworkReference Include="Microsoft.WindowsDesktop.App.WPF" />
  </ItemGroup>

  <PropertyGroup>
    <UseWPF>true</UseWPF>
  </PropertyGroup>

@aorzelskiGH note that there are slight behavior differences in the API that's documented here: https://github.com/dotnet/runtime/issues/28258

If it doesn't affect you that's great, but I just wanted to share this as an FYI in case you ran into any issues

@ericstj @stevenbrix @vatsan-madhavan
Thank you for your answers. Sorry, my question was not precise.

I need to sign and validate packages in .NETCore 3.1 on Linux. WPF is not supported there.

The code I want to use is essentially like this:

    public static VerifyResult VerifySignatures(string packagePath, out Dictionary<string, X509ChainStatusFlags> certificatesStatus)
    {
        VerifyResult vResult;
        certificatesStatus = new Dictionary<string, X509ChainStatusFlags>();
        using (Package package = Package.Open(packagePath, FileMode.Open, FileAccess.Read))
        {
            PackageDigitalSignatureManager dsm = new PackageDigitalSignatureManager(package);

            // Verify the collection of certificates in the package
            foreach (PackageDigitalSignature signature in dsm.Signatures)
            {
                certificatesStatus.Add(signature.Signer.Subject, PackageDigitalSignatureManager.VerifyCertificate(signature.Signer));
            }

            // For this example, if all certificates are valid, verify all signatures in the package.
            vResult = dsm.VerifySignatures(false);
        }
        return vResult;
    }

But "PackageDigitalSignatureManager" and "PackageDigitalSignature" are not supported under Linux.

@aorzelskiGH Did you ever find a solution to your problem? Because I am in the same situation and wonder what I could do.

Was this page helpful?
0 / 5 - 0 ratings