Xamarin Public Jenkins (auto-signing) 6bdd276d05 Imported Upstream version 5.0.0.42
Former-commit-id: fd56571888259555122d8a0f58c68838229cea2b
2017-04-10 11:41:01 +00:00

524 lines
17 KiB
C#

//
// SN.cs: sn clone tool
//
// Author:
// Sebastien Pouliot <sebastien@ximian.com>
//
// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
// Copyright (C) 2004-2006,2008 Novell, Inc (http://www.novell.com)
//
using System;
using System.IO;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using Mono.Security;
using Mono.Security.Cryptography;
using Mono.Security.X509;
[assembly: AssemblyTitle("Mono StrongName")]
[assembly: AssemblyDescription("StrongName utility for signing assemblies")]
namespace Mono.Tools {
class SN {
static private void Header ()
{
Console.WriteLine (new AssemblyInfo ().ToString ());
}
static string defaultCSP;
static bool LoadConfig (bool quiet)
{
MethodInfo config = typeof (System.Environment).GetMethod ("GetMachineConfigPath",
BindingFlags.Static|BindingFlags.NonPublic);
if (config != null) {
string path = (string) config.Invoke (null, null);
bool exist = File.Exists (path);
if (!quiet && !exist)
Console.WriteLine ("Couldn't find machine.config");
StrongNameManager.LoadConfig (path);
return exist;
}
else if (!quiet)
Console.WriteLine ("Couldn't resolve machine.config location (corlib issue)");
// default CSP
return false;
}
// TODO
static int SaveConfig ()
{
// default CSP
return 1;
}
static byte[] ReadFromFile (string fileName)
{
byte[] data = null;
FileStream fs = File.Open (fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
try {
data = new byte [fs.Length];
fs.Read (data, 0, data.Length);
}
finally {
fs.Close ();
}
return data;
}
static void WriteToFile (string fileName, byte[] data)
{
FileStream fs = File.Open (fileName, FileMode.Create, FileAccess.Write);
try {
fs.Write (data, 0, data.Length);
}
finally {
fs.Close ();
}
}
static void WriteCSVToFile (string fileName, byte[] data, string mask)
{
StreamWriter sw = File.CreateText (fileName);
try {
for (int i=0; i < data.Length; i++) {
if (mask [0] == 'X')
sw.Write ("0x");
sw.Write (data [i].ToString (mask));
sw.Write (", ");
}
}
finally {
sw.Close ();
}
}
static string ToString (byte[] data)
{
StringBuilder sb = new StringBuilder ();
for (int i=0; i < data.Length; i++) {
if ((i % 39 == 0) && (data.Length > 39))
sb.Append (Environment.NewLine);
sb.Append (data [i].ToString ("x2"));
if (i > 2080) {
// ensure we can display up to 16384 bits keypair
sb.Append (" !!! TOO LONG !!!");
break;
}
}
return sb.ToString ();
}
static RSA GetKeyFromFile (string filename)
{
byte[] data = ReadFromFile (filename);
try {
// for SNK files (including the ECMA pseudo-key)
return new StrongName (data).RSA;
}
catch {
if (data.Length == 0 || data [0] != 0x30)
throw;
// this could be a PFX file
Console.Write ("Enter password for private key (will be visible when typed): ");
PKCS12 pfx = new PKCS12 (data, Console.ReadLine ());
// works only if a single key is present
if (pfx.Keys.Count != 1)
throw;
RSA rsa = (pfx.Keys [0] as RSA);
if (rsa == null)
throw;
return rsa;
}
}
#if false
// is assembly signed (or delayed signed) ?
static bool IsStrongNamed (Assembly assembly)
{
if (assembly == null)
return false;
object[] attrs = assembly.GetCustomAttributes (true);
foreach (object o in attrs) {
if (o is AssemblyKeyFileAttribute)
return true;
else if (o is AssemblyKeyNameAttribute)
return true;
}
return false;
}
#endif
static bool ReSign (string assemblyName, RSA key, bool quiet)
{
// this doesn't load the assembly (well it unloads it ;)
// http://weblogs.asp.net/nunitaddin/posts/9991.aspx
AssemblyName an = null;
try {
an = AssemblyName.GetAssemblyName (assemblyName);
}
catch {
}
if (an == null) {
Console.WriteLine ("Unable to load assembly: {0}", assemblyName);
return false;
}
StrongName sign = new StrongName (key);
byte[] token = an.GetPublicKeyToken ();
// first, try to compare using a mapped public key (e.g. ECMA)
bool same = Compare (sign.PublicKey, StrongNameManager.GetMappedPublicKey (token));
if (!same) {
// second, try to compare using the assembly public key
same = Compare (sign.PublicKey, an.GetPublicKey ());
if (!same) {
// third (and last) chance, try to compare public key token
same = Compare (sign.PublicKeyToken, token);
}
}
if (same) {
bool signed = sign.Sign (assemblyName);
if (!quiet || !signed) {
Console.WriteLine (signed ? "Assembly {0} signed." : "Couldn't sign the assembly {0}.",
assemblyName);
}
return signed;
}
Console.WriteLine ("Couldn't sign the assembly {0} with this key pair. Public key of assembly did not match signing public key.", assemblyName);
return false;
}
static int Verify (string assemblyName, bool forceVerification, bool quiet)
{
// this doesn't load the assembly (well it unloads it ;)
// http://weblogs.asp.net/nunitaddin/posts/9991.aspx
AssemblyName an = null;
try {
an = AssemblyName.GetAssemblyName (assemblyName);
}
catch {
}
if (an == null) {
Console.WriteLine ("Unable to load assembly: {0}", assemblyName);
return 2;
}
byte[] publicKey = StrongNameManager.GetMappedPublicKey (an.GetPublicKeyToken ());
if ((publicKey == null) || (publicKey.Length < 12)) {
// no mapping
publicKey = an.GetPublicKey ();
if ((publicKey == null) || (publicKey.Length < 12)) {
Console.WriteLine ("{0} is not a strongly named assembly.", assemblyName);
return 2;
}
}
// Note: MustVerify is based on the original token (by design). Public key
// remapping won't affect if the assembly is verified or not.
if (forceVerification || StrongNameManager.MustVerify (an)) {
RSA rsa = CryptoConvert.FromCapiPublicKeyBlob (publicKey, 12);
StrongName sn = new StrongName (rsa);
if (sn.Verify (assemblyName)) {
if (!quiet)
Console.WriteLine ("Assembly {0} is strongnamed.", assemblyName);
return 0;
}
else {
Console.WriteLine ("Assembly {0} is delay-signed but not strongnamed", assemblyName);
return 1;
}
}
else {
Console.WriteLine ("Assembly {0} is strongnamed (verification skipped).", assemblyName);
return 0;
}
}
static bool Compare (byte[] value1, byte[] value2)
{
if ((value1 == null) || (value2 == null))
return false;
bool result = (value1.Length == value2.Length);
if (result) {
for (int i=0; i < value1.Length; i++) {
if (value1 [i] != value2 [i])
return false;
}
}
return result;
}
static void Help (string details)
{
Console.WriteLine ("Usage: sn [-q | -quiet] options [parameters]{0}", Environment.NewLine);
Console.WriteLine (" -q | -quiet \tQuiet mode (minimal display){0}", Environment.NewLine);
switch (details) {
case "config":
Console.WriteLine ("Configuration options <1>");
Console.WriteLine (" -c provider{0}\tChange the default CSP provider", Environment.NewLine);
Console.WriteLine (" -m [y|n]{0}\tUse a machine [y] key container or user key container [n]", Environment.NewLine);
Console.WriteLine (" -Vl{0}\tList the verification options", Environment.NewLine);
Console.WriteLine (" -Vr assembly [userlist]{0}\tExempt the specified assembly from verification for the user list", Environment.NewLine);
Console.WriteLine (" -Vu assembly{0}\tRemove exemption entry for the specified assembly", Environment.NewLine);
Console.WriteLine (" -Vx{0}\tRemove all exemptions entries", Environment.NewLine);
Console.WriteLine ("{0}<1> Currently not implemented in the tool", Environment.NewLine);
break;
case "csp":
Console.WriteLine ("CSP related options");
Console.WriteLine (" -d container{0}\tDelete the specified key container", Environment.NewLine);
Console.WriteLine (" -i keypair.snk container{0}\tImport the keypair from a SNK file into a CSP container", Environment.NewLine);
Console.WriteLine (" -pc container public.key{0}\tExport the public key from a CSP container to the specified file", Environment.NewLine);
break;
case "convert":
Console.WriteLine ("Convertion options");
Console.WriteLine (" -e assembly output.pub{0}\tExport the assembly public key to the specified file", Environment.NewLine);
Console.WriteLine (" -p keypair.snk output.pub{0}\tExport the public key from a SNK file to the specified file", Environment.NewLine);
Console.WriteLine (" -o input output.txt{0}\tConvert the input file to a CSV file (using decimal).", Environment.NewLine);
Console.WriteLine (" -oh input output.txt{0}\tConvert the input file to a CSV file (using hexadecimal).", Environment.NewLine);
break;
case "sn":
Console.WriteLine ("StrongName signing options");
Console.WriteLine (" -D assembly1 assembly2{0}\tCompare assembly1 and assembly2 (without signatures)", Environment.NewLine);
Console.WriteLine (" -k keypair.snk{0}\tCreate a new keypair in the specified file", Environment.NewLine);
Console.WriteLine (" -R assembly keypair.snk{0}\tResign the assembly with the specified StrongName key file", Environment.NewLine);
Console.WriteLine (" -Rc assembly container{0}\tResign the assembly with the specified CSP container", Environment.NewLine);
Console.WriteLine (" -t file{0}\tShow the public key token from the specified file", Environment.NewLine);
Console.WriteLine (" -tp file{0}\tShow the public key and pk token from the specified file", Environment.NewLine);
Console.WriteLine (" -T assembly{0}\tShow the public key token from the specified assembly", Environment.NewLine);
Console.WriteLine (" -Tp assembly{0}\tShow the public key and pk token from the specified assembly", Environment.NewLine);
Console.WriteLine (" -v assembly{0}\tVerify the specified assembly signature", Environment.NewLine);
Console.WriteLine (" -vf assembly{0}\tVerify the specified assembly signature (even if disabled).", Environment.NewLine);
break;
default:
Console.WriteLine ("Help options");
Console.WriteLine (" -? | -h \tShow this help screen about the tool");
Console.WriteLine (" -? | -h config \tConfiguration options");
Console.WriteLine (" -? | -h csp \tCrypto Service Provider (CSP) related options");
Console.WriteLine (" -? | -h convert\tFormat convertion options");
Console.WriteLine (" -? | -h sn \tStrongName signing options");
break;
}
}
static int Process (string[] args)
{
int i = 0;
string param = args [i];
bool quiet = ((param == "-quiet") || (param == "-q"));
if (quiet)
i++;
else
Header();
LoadConfig (quiet);
StrongName sn = null;
AssemblyName an = null;
RSACryptoServiceProvider rsa = null;
CspParameters csp = new CspParameters ();
csp.ProviderName = defaultCSP;
switch (args [i++]) {
case "-c":
// Change global CSP provider options
defaultCSP = args [i];
return SaveConfig ();
case "-d":
// Delete specified key container
csp.KeyContainerName = args [i];
rsa = new RSACryptoServiceProvider (csp);
rsa.PersistKeyInCsp = false;
if (!quiet)
Console.WriteLine ("Keypair in container {0} has been deleted", args [i]);
break;
case "-D":
StrongName a1 = new StrongName ();
byte[] h1 = a1.Hash (args [i++]);
StrongName a2 = new StrongName ();
byte[] h2 = a2.Hash (args [i++]);
if (Compare (h1, h2)) {
Console.WriteLine ("Both assembly are identical (same digest for metadata)");
// TODO: if equals then compare signatures
}
else
Console.WriteLine ("Assemblies are not identical (different digest for metadata)");
break;
case "-e":
// Export public key from assembly
an = AssemblyName.GetAssemblyName (args [i++]);
WriteToFile (args[i], an.GetPublicKey ());
if (!quiet)
Console.WriteLine ("Public Key extracted to file {0}", args [i]);
break;
case "-i":
// import keypair from SNK to container
sn = new StrongName (ReadFromFile (args [i++]));
csp.KeyContainerName = args [i];
rsa = new RSACryptoServiceProvider (csp);
rsa.ImportParameters (sn.RSA.ExportParameters (true));
break;
case "-k":
// Create a new strong name key pair
// (a new RSA keypair automagically if none is present)
int size = 1024;
if (i < args.Length + 2) {
try {
size = Int32.Parse (args[i++]);
}
catch {
// oops, that wasn't a valid key size (assume 1024 bits)
i--;
}
}
sn = new StrongName (size);
WriteToFile (args[i], CryptoConvert.ToCapiKeyBlob (sn.RSA, true));
if (!quiet)
Console.WriteLine ("A new {0} bits strong name keypair has been generated in file '{1}'.", size, args [i]);
break;
case "-m":
Console.WriteLine ("Unimplemented option");
break;
case "-o":
byte[] infileD = ReadFromFile (args [i++]);
WriteCSVToFile (args [i], infileD, "D");
if (!quiet)
Console.WriteLine ("Output CSV file is {0} (decimal format)", args [i]);
break;
case "-oh":
byte[] infileX2 = ReadFromFile (args [i++]);
WriteCSVToFile (args [i], infileX2, "X2");
if (!quiet)
Console.WriteLine ("Output CVS file is {0} (hexadecimal format)", args [i]);
break;
case "-p":
// Extract public key from SNK or PKCS#12/PFX file
sn = new StrongName (GetKeyFromFile (args [i++]));
WriteToFile (args[i], sn.PublicKey);
if (!quiet)
Console.WriteLine ("Public Key extracted to file {0}", args [i]);
break;
case "-pc":
// Extract public key from container
csp.KeyContainerName = args [i++];
rsa = new RSACryptoServiceProvider (csp);
sn = new StrongName (rsa);
WriteToFile (args[i], sn.PublicKey);
if (!quiet)
Console.WriteLine ("Public Key extracted to file {0}", args [i]);
break;
case "-R":
string filename = args [i++];
if (! ReSign (filename, GetKeyFromFile (args [i]), quiet))
return 1;
break;
case "-Rc":
filename = args [i++];
csp.KeyContainerName = args [i];
rsa = new RSACryptoServiceProvider (csp);
if (! ReSign (filename, rsa, quiet))
return 1;
break;
case "-t":
// Show public key token from file
sn = new StrongName (ReadFromFile (args [i]));
// note: ignore quiet
Console.WriteLine ("Public Key Token: " + ToString (sn.PublicKeyToken), Environment.NewLine);
break;
case "-tp":
// Show public key and public key token from assembly
sn = new StrongName (ReadFromFile (args [i]));
// note: ignore quiet
Console.WriteLine ("Public Key:" + ToString (sn.PublicKey));
Console.WriteLine ("{0}Public Key Token: " + ToString (sn.PublicKeyToken), Environment.NewLine);
break;
case "-T":
// Show public key token from assembly
an = AssemblyName.GetAssemblyName (args [i++]);
// note: ignore quiet
byte [] pkt = an.GetPublicKeyToken ();
if (pkt == null) {
Console.WriteLine ("{0} does not represent a strongly named assembly.", args [i - 1]);
} else {
Console.WriteLine ("Public Key Token: " + ToString (pkt));
}
break;
case "-Tp":
// Show public key and public key token from assembly
an = AssemblyName.GetAssemblyName (args [i++]);
byte [] token = an.GetPublicKeyToken ();
if (token == null) {
Console.WriteLine ("{0} does not represent a strongly named assembly.", args [i - 1]);
} else {
Console.WriteLine ("Public Key:" + ToString (an.GetPublicKey ()));
Console.WriteLine ("{0}Public Key Token: " + ToString (token), Environment.NewLine);
}
break;
case "-v":
filename = args [i++];
return Verify (filename, false, quiet);
case "-vf":
filename = args [i++];
return Verify (filename, true, quiet); // force verification
case "-Vl":
Console.WriteLine (new StrongNameManager ().ToString ());
break;
case "-Vr":
Console.WriteLine ("Unimplemented option");
break;
case "-Vu":
Console.WriteLine ("Unimplemented option");
break;
case "-Vx":
// we must remove <verificationSettings> from each config files
Console.WriteLine ("Unimplemented option");
break;
case "-?":
case "-h":
Help ((i < args.Length) ? args [i] : null);
break;
default:
if (!quiet)
Console.WriteLine ("Unknown option {0}", args [i-1]);
return 1;
}
return 0;
}
[STAThread]
static int Main (string[] args)
{
try {
if (args.Length < 1) {
Header ();
Help (null);
} else {
return Process (args);
}
}
catch (IndexOutOfRangeException) {
Console.WriteLine ("ERROR: Invalid number of parameters.{0}", Environment.NewLine);
Help (null);
}
catch (CryptographicException ce) {
Console.WriteLine ("ERROR: {0}", ce.Message);
}
catch (Exception e) {
Console.WriteLine ("ERROR: Unknown error during processing: {0}", e);
}
return 1;
}
}
}