1
0
mirror of https://github.com/twiglet/cs2j.git synced 2025-01-18 13:15:17 +01:00

228 lines
8.8 KiB
C#
Executable File

/*
Copyright 2010,2011 Kevin Glynn (kevin.glynn@twigletsoftware.com)
*/
using System;
using System.Reflection;
using System.IO;
using System.Xml.Serialization;
using System.Xml;
using System.Text;
using NDesk.Options;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
namespace Twiglet.CS2J.Utility
{
public class TemplateSigner
{
private const string CS2JSIGNER_VERSION = "pre-release";
private int Verbose { get; set; }
public delegate void FileProcessor(string fName);
private List<string> XmlDirs { get; set; }
private List<string> ExcludeXmlDirs { get; set; }
private String KeyFile = null;
RSACryptoServiceProvider RsaKey { get; set; }
public TemplateSigner()
{
Verbose = 0;
XmlDirs = new List<string>();
ExcludeXmlDirs = new List<string>();
RsaKey = null;
}
private static void printUsageAndExit()
{
Console.Out.WriteLine("Usage: " + Path.GetFileNameWithoutExtension(System.Environment.GetCommandLineArgs()[0]) + " <type to dump, if not given dump all>");
Console.Out.WriteLine(" [-version] (show version information)");
Console.Out.WriteLine(" [-help|h|?] (this usage message)");
Console.Out.WriteLine(" [-v] (be [somewhat more] verbose, repeat for more verbosity)");
Console.Out.WriteLine(" [-privkeyfile <path to rsa private key file (xml format)");
Console.Out.WriteLine(" [-xmlpath <a root of xml files to be signed>] (can be multiple directories, separated by semi-colons)");
Console.Out.WriteLine(" [-exxmlpath <directories/files to be excluded from signing>] (can be multiple directories/files, separated by semi-colons)");
Console.Out.WriteLine(" [<root of xml files to be signed>] (can be multiple directories, separated by semi-colons)");
Environment.Exit(0);
}
private static void printVersion()
{
Console.Out.WriteLine(Path.GetFileNameWithoutExtension(System.Environment.GetCommandLineArgs()[0]));
Console.WriteLine("Version: {0}", CS2JSIGNER_VERSION);
}
private static void addDirectories(IList<string> strs, string rawStr)
{
string[] argDirs = rawStr.Split(';');
for (int i = 0; i < argDirs.Length; i++)
strs.Add(Path.GetFullPath(argDirs[i]));
}
public static void Main(string[] args)
{
TemplateSigner templateSigner = new TemplateSigner();
List<string> xmlDirs = new List<string>();
List<string> excludeXmlDirs = new List<string>();
OptionSet p = new OptionSet()
.Add("v", v => templateSigner.Verbose++)
.Add("version", v => printVersion())
.Add("help|h|?", v => printUsageAndExit())
.Add("keyfile=", fname => templateSigner.KeyFile = Path.GetFullPath(fname))
.Add("xmlpath=", dirs => addDirectories(xmlDirs, dirs))
.Add("exxmlpath=", dirs => addDirectories(excludeXmlDirs, dirs))
;
List<string> leftovers = p.Parse(args);
if (leftovers != null)
{
foreach (string d in leftovers)
{
addDirectories(xmlDirs, d);
}
}
templateSigner.XmlDirs = xmlDirs;
templateSigner.ExcludeXmlDirs = excludeXmlDirs;
if (!File.Exists(templateSigner.KeyFile)) {
Console.Out.WriteLine("Error: RSA key at '" + templateSigner.KeyFile + "' not found, aborting.");
Environment.Exit(1);
}
templateSigner.SignXmlFiles();
}
public void SignXmlFiles() {
try
{
XmlReader reader = XmlReader.Create(KeyFile);
reader.MoveToContent();
// Create a new CspParameters object to specify
// a key container.
CspParameters cspParams = new CspParameters();
cspParams.KeyContainerName = "XML_DSIG_RSA_KEY";
// Initialise from .
RsaKey = new RSACryptoServiceProvider(cspParams);
RsaKey.PersistKeyInCsp = false;
RsaKey.FromXmlString(reader.ReadOuterXml());
// Load .Net templates
if (XmlDirs != null)
{
foreach (string r in XmlDirs)
doFile(r, ".xml", SignNetTranslation, ExcludeXmlDirs);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
private void SignNetTranslation(string filePath)
{
// Create a new XML document.
XmlDocument xmlDoc = new XmlDocument();
// Load an XML file into the XmlDocument object.
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load(filePath);
// Sign the XML document.
SignXml(xmlDoc, RsaKey);
Console.WriteLine("XML file signed.");
// Save the document.
xmlDoc.Save(filePath);
}
// Sign an XML file.
// This document cannot be verified unless the verifying
// code has the key with which it was signed.
private void SignXml(XmlDocument xmlDoc, RSA Key)
{
// Check arguments.
if (xmlDoc == null)
throw new ArgumentException("xmlDoc");
if (Key == null)
throw new ArgumentException("Key");
// Add the namespace.
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
nsmgr.AddNamespace("ss", "http://www.w3.org/2000/09/xmldsig#");
// Remove any existing signature(s)
XmlNode root = xmlDoc.DocumentElement;
XmlNodeList nodeList = root.SelectNodes("/*/ss:Signature", nsmgr);
for (int i = nodeList.Count - 1; i >= 0; i--)
{
nodeList[i].ParentNode.RemoveChild(nodeList[i]);
}
// Create a SignedXml object.
SignedXml signedXml = new SignedXml(xmlDoc);
// Add the key to the SignedXml document.
signedXml.SigningKey = Key;
// Create a reference to be signed.
Reference reference = new Reference();
reference.Uri = "";
// Add an enveloped transformation to the reference.
XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
reference.AddTransform(env);
// Add the reference to the SignedXml object.
signedXml.AddReference(reference);
// Compute the signature.
signedXml.ComputeSignature();
// Get the XML representation of the signature and save
// it to an XmlElement object.
XmlElement xmlDigitalSignature = signedXml.GetXml();
// Append the element to the XML document.
xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(xmlDigitalSignature, true));
}
// Call processFile on all files below f that have the given extension
private void doFile(string root, string ext, FileProcessor processFile, IList<string> excludes)
{
string canonicalPath = Path.GetFullPath(root);
// If this is a directory, walk each file/dir in that directory
if (!excludes.Contains(canonicalPath.ToLower()))
{
if (Directory.Exists(canonicalPath))
{
string[] files = Directory.GetFileSystemEntries(canonicalPath);
for (int i = 0; i < files.Length; i++)
doFile(Path.Combine(canonicalPath, files[i]), ext, processFile, excludes);
}
else if ((Path.GetFileName(canonicalPath).Length > ext.Length) && canonicalPath.Substring(canonicalPath.Length - ext.Length).Equals(ext))
{
if (Verbose >= 2) Console.WriteLine(" " + canonicalPath);
try
{
processFile(canonicalPath);
}
catch (Exception e)
{
Console.Error.WriteLine("\nCannot process file: " + canonicalPath);
Console.Error.WriteLine("exception: " + e);
}
}
}
}
}
}