Question

And again I ran into trouble while porting a .NET Desktop App to a Windows Store App... Long story short, I have a ZIP File with an encrypted and signed XML File and a certificate in it. The decryption is working (more or less) but now I have to "unsign" the XML and I'm stuck.

In the .NET App the unsigning is done with System.Security.Cryptography.Pkcs.SignedCms but that class does not exist in WinRt (as always...)

Is there any alternative in WinRT?

here is some of the code used in the .NET App:

public static byte[] CheckAndRemoveSignature(byte[] data,  X509Certificate2Collection certStore, out SignedCms out_signature)
    {
        SignedCms signedMessage = new SignedCms();
        signedMessage.Decode(data);

        if ((certStore != null) && (certStore.Count > 0))
            signedMessage.CheckSignature(certStore, true);
        else
            signedMessage.CheckSignature(true);

        out_signature = signedMessage;

        // return data without signature
        return signedMessage.ContentInfo.Content;
    }

I already searched a lot, but the only thing I found, that could have helped me was this post. Unfortunately the marked answer does not provide any helpful information :(

Windows 8 Metro cryptography - using SignedCms Pkcs7

I would really appreciate some help here :)


Edit

The essential problem is to get the original xml data out of the signed byte array. Or, more specifically I need the functionality of these few lines of code in WinRT

SignedCms signedMessage = new SignedCms();
signedMessage.Decode(data);
byte[] result = signedMessage.ContentInfo.Content;

I tried the example from pepo, but I get a MalformedContent Exception

private byte[] CheckAndRemoveSignature(byte[] data)
    {
        try
        {
            // load using bouncyCastle
            CmsSignedData sig = new CmsSignedData(data);
            // var allSigsValid = VerifySignatures(sig);
            byte[] content = sig.SignedContent.GetContent() as byte[];
            return content;
        }
        catch (Exception ex)
        {
            cryptOutput.Text += "Error removing Signature: " + ex;
            return data;
        }

I get this exception:

Org.BouncyCastle.Cms.CmsException: Malformed content. ---> System.ArgumentException: unknown object in factory: DerApplicationSpecific
at Org.BouncyCastle.Asn1.Cms.ContentInfo.GetInstance(Object obj)
at Org.BouncyCastle.Cms.CmsUtilities.ReadContentInfo(Asn1InputStream aIn)
   --- End of inner exception stack trace ---
at Org.BouncyCastle.Cms.CmsUtilities.ReadContentInfo(Asn1InputStream aIn)
at Org.BouncyCastle.Cms.CmsUtilities.ReadContentInfo(Stream input)
at Org.BouncyCastle.Cms.CmsSignedData..ctor(Byte[] sigBlock)
at TestApp.MainPage.CheckAndRemoveSignature(Byte[] data)

Code from Desktop App where the XML-File is signed:

  private byte[] signInternal(byte[] data, X509Certificate2 signatureCert, bool signatureOnly)
    {
        CAPICOM.SignedData signedData = new CAPICOM.SignedDataClass();
        CAPICOM.Utilities u = new CAPICOM.UtilitiesClass();
        signedData.set_Content(u.ByteArrayToBinaryString(data));
        GC.Collect();

        CAPICOM.Signer signer = new CAPICOM.Signer();
        signer.Options = CAPICOM.CAPICOM_CERTIFICATE_INCLUDE_OPTION.CAPICOM_CERTIFICATE_INCLUDE_END_ENTITY_ONLY;

        CAPICOM.CertificateClass certClass = new CAPICOM.CertificateClass();
        certClass.Import(Convert.ToBase64String(signatureCert.Export(X509ContentType.SerializedCert)));
        signer.Certificate = certClass;

        GC.Collect();

        if (this.validateCert(signatureCert))
            return (byte[])Convert.FromBase64String(signedData.Sign(signer, signatureOnly, CAPICOM.CAPICOM_ENCODING_TYPE.CAPICOM_ENCODE_BASE64));
        else
            return new byte[] { };
    }

Solution

In the end it turned out that there was a big problem with encoding, that I did not think was noteworthy. The answer from pepo is working, however I will post my version, to show how it works if you get the file from a zip folder:

// get bytes from zip
byte[] data = getFileContentAsByteArray(zipBytes, ze.FileName);
var dataString = Encoding.UTF8.GetString(data, 0, data.Length);

// check and remove signature
bool isValid;
byte[] withoutSig = CheckAndRemoveSignature(dataString, out isValid);


    private byte[] CheckAndRemoveSignature(string data, out bool isValid)
    {
        isValid = false;

        // using bouncyCastle
        try
        {
            var bytes = Convert.FromBase64String(data);

            // assign data to CmsSignedData                  
            CmsSignedData sig = new CmsSignedData(bytes);

            // check if signature is valid
            var allSigsValid = VerifySignaturesBC(sig);
            if (allSigsValid.Equals(true)) { isValid = true; }

            // get signature from cms
            byte[] content = sig.SignedContent.GetContent() as byte[];

            return content;
        }
        catch (Exception ex) { cryptOutput.Text += "Error in 'BouncyCastle unsign' " + ex; return null; }
    }
Was it helpful?

Solution

Based on comments I understand that you have a PKCS#7 structure (SignedCms) and the content of that structure is XmlDocument.

Because there is no SignedCms in WinRT API you have two choices. Either use some ASN.1 library and parse PKCS#7 manually looking for content or use i.e. BouncyCastle which has SignedCms implemented and can parse that strusture. You asked for an example using bouncyCastle. Here it is.

using Org.BouncyCastle.Cms;
using Org.BouncyCastle.X509.Store;
using System.Collections;
using System.Security.Cryptography.Pkcs;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            // make some pkcs7 signedCms to work on
            SignedCms p7 = new SignedCms(new System.Security.Cryptography.Pkcs.ContentInfo(new byte[] { 0x01, 0x02 }));
            p7.ComputeSignature(new CmsSigner(), false);

            // encode to get signedCms byte[] representation
            var signedCms = p7.Encode();

            // load using bouncyCastle
            CmsSignedData sig = new CmsSignedData(signedCms);

            var allSigsValid = VerifySignatures(sig);

            byte[] content = sig.SignedContent.GetContent() as byte[];
        }

        // taken from bouncy castle SignedDataTest.cs
        private static bool VerifySignatures(
            CmsSignedData sp)
        {
            var signaturesValid = true;
            IX509Store x509Certs = sp.GetCertificates("Collection");
            SignerInformationStore signers = sp.GetSignerInfos();

            foreach (SignerInformation signer in signers.GetSigners())
            {
                ICollection certCollection = x509Certs.GetMatches(signer.SignerID);

                IEnumerator certEnum = certCollection.GetEnumerator();
                certEnum.MoveNext();
                Org.BouncyCastle.X509.X509Certificate cert = (Org.BouncyCastle.X509.X509Certificate)certEnum.Current;

                signaturesValid &= signer.Verify(cert);
            }
            return signaturesValid;
        }
    }
}

As for the ASN.1 library, I have only worked with bouncyCastle which has ASN.1 parser or ASN.1 editor which is a very useful GUI application for showing structure of PKCS#7, certificates etc. So I can recommend only those two.

OTHER TIPS

You’re probably looking for something like Windows.Security.Cryptography.Certificates.CmsAttachedSignature.VerifySignature() and it's 'Content' property See here

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top