sign-cert.xsh

Sign XSharper script with a certificate

<xsharper>
    <versionInfo title="Sign-Cert" value="Sign XSH script with a given certificate" version="0.0.0.1" />
    <usage options="ifNoArguments default" />
    <param name="data" value="Script file" description="script.xsh" required="true" />
    <param name="certSubject" value="Certificate subject" required="true" />
    <param />
    <param value="Parameters:" />
    <param />
    <param switch="machine" count="none" value="Use machine certificate store" default="0" unspecified="1" />
    <reference name="System.Security" />
    <?h using System;
        using System.Text;
        using System.Security.Cryptography;
        using System.Security.Cryptography.Xml;
        using System.Security.Cryptography.X509Certificates;
        
        
    ?>
    <code><![CDATA[
        X509Store store = new X509Store("My", c.GetBool("machine")?StoreLocation.LocalMachine:StoreLocation.CurrentUser);
        try
        {
            store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
            X509Certificate2Collection coll=store.Certificates.Find(X509FindType.FindBySubjectName, c.GetStr("certSubject"), false);
            if (coll.Count==0)
            {
                c.Error.WriteLine("Certificate not found");
                return -2;
            }
            if (coll.Count!=1)
            {
                c.Error.WriteLine("Too many certificates found");
                return -2;
            }
            
            X509Certificate2 cert=coll[0];
            if (cert.PrivateKey==null)
            {
                c.Error.WriteLine("Certificate does not have a private key");
                return -2;
            }
            
            PrintInfo(cert);
            
            X509ChainPolicy pol = new X509ChainPolicy();
            pol.RevocationMode = X509RevocationMode.NoCheck;
            pol.VerificationFlags = X509VerificationFlags.IgnoreEndRevocationUnknown | X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown | X509VerificationFlags.IgnoreRootRevocationUnknown;
            pol.ApplicationPolicy.Add(new Oid("1.3.6.1.5.5.7.3.3"));
                
            X509Chain chain = new X509Chain(true);
            chain.ChainPolicy = pol;
            if (!chain.Build(cert))
                c.Error.WriteLine("Warning: Certificate is not trusted for CodeSigning");
        

            string filename=(string)c["data"];
            XmlDocument doc = new XmlDocument();
            doc.PreserveWhitespace = true;
            doc.Load(filename);
                
            SignedXml signer=  new SignedXml(doc); 
        
            var el=doc.GetElementsByTagName("Signature");
            if (el!=null && el.Count!=0 && el[0]!=null && el[0] is XmlElement)
                el[0].ParentNode.RemoveChild(el[0]);

            
            signer.KeyInfo = new KeyInfo();
            signer.KeyInfo.AddClause(new KeyInfoX509Data(cert,X509IncludeOption.ExcludeRoot));
            signer.SigningKey = cert.PrivateKey; 
            signer.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NWithCommentsTransformUrl;

            XmlDsigExcC14NWithCommentsTransform canMethod = (XmlDsigExcC14NWithCommentsTransform)signer.SignedInfo.CanonicalizationMethodObject;
            canMethod.InclusiveNamespacesPrefixList = "Sign";


            Reference orderRef = new Reference("");
            orderRef.AddTransform(new XmlDsigEnvelopedSignatureTransform());
            signer.AddReference(orderRef); 
            
            signer.ComputeSignature();
            doc.DocumentElement.AppendChild(signer.GetXml());
            doc.Save(filename); 
            c.Print("Script '${=Path.GetFullPath($data)}' has been signed & saved successfully");
            return 0;
        }
        finally {
            store.Close();
        }

    ]]>
    <sub id="PrintInfo">
        <param name="cert" required="true" />
        <print tr="trim multiline expand" outTo="^info">
            ==== Certificate info ===
            Subject:      ${=$cert.Subject}     
            Expiration:   ${=$cert.GetExpirationDateString()}
            Issuer:       ${=$cert.Subject}     
            Serial:       ${=$cert.SerialNumber}
            Thumbprint:   ${=$cert.Thumbprint}
            
        </print>
        
    </sub>
    </code>
</xsharper>