// dnlib: See LICENSE.txt for more info using System; using System.IO; using System.Runtime.Serialization; using System.Security.Cryptography; using System.Threading; namespace dnlib.DotNet { /// /// Thrown if the strong name key or public key is invalid /// [Serializable] public class InvalidKeyException : Exception { /// /// Default constructor /// public InvalidKeyException() { } /// /// Constructor /// /// Error message public InvalidKeyException(string message) : base(message) { } /// /// Constructor /// /// Error message /// Other exception public InvalidKeyException(string message, Exception innerException) : base(message, innerException) { } /// /// Constructor /// /// /// protected InvalidKeyException(SerializationInfo info, StreamingContext context) : base(info, context) { } } /// /// Type of signature algorithm. See WinCrypt.h in the Windows SDK /// public enum SignatureAlgorithm : uint { /// /// RSA signature algorithm /// CALG_RSA_SIGN = 0x00002400, } static class StrongNameUtils { public static byte[] ReadBytesReverse(this BinaryReader reader, int len) { var data = reader.ReadBytes(len); if (data.Length != len) throw new InvalidKeyException("Can't read more bytes"); Array.Reverse(data); return data; } public static void WriteReverse(this BinaryWriter writer, byte[] data) { var d = (byte[])data.Clone(); Array.Reverse(d); writer.Write(d); } } /// /// A public key /// public sealed class StrongNamePublicKey { const uint RSA1_SIG = 0x31415352; readonly SignatureAlgorithm signatureAlgorithm; readonly AssemblyHashAlgorithm hashAlgorithm; readonly byte[] modulus; readonly byte[] publicExponent; /// /// Gets/sets the signature algorithm /// public SignatureAlgorithm SignatureAlgorithm => signatureAlgorithm; /// /// Gets/sets the hash algorithm /// public AssemblyHashAlgorithm HashAlgorithm => hashAlgorithm; /// /// Gets/sets the modulus /// public byte[] Modulus => modulus; /// /// Gets/sets the public exponent /// public byte[] PublicExponent => publicExponent; /// /// Default constructor /// public StrongNamePublicKey() { } /// /// Constructor /// /// Modulus /// Public exponent public StrongNamePublicKey(byte[] modulus, byte[] publicExponent) : this(modulus, publicExponent, AssemblyHashAlgorithm.SHA1, SignatureAlgorithm.CALG_RSA_SIGN) { } /// /// Constructor /// /// Modulus /// Public exponent /// Hash algorithm public StrongNamePublicKey(byte[] modulus, byte[] publicExponent, AssemblyHashAlgorithm hashAlgorithm) : this(modulus, publicExponent, hashAlgorithm, SignatureAlgorithm.CALG_RSA_SIGN) { } /// /// Constructor /// /// Modulus /// Public exponent /// Hash algorithm /// Signature algorithm public StrongNamePublicKey(byte[] modulus, byte[] publicExponent, AssemblyHashAlgorithm hashAlgorithm, SignatureAlgorithm signatureAlgorithm) { this.signatureAlgorithm = signatureAlgorithm; this.hashAlgorithm = hashAlgorithm; this.modulus = modulus; this.publicExponent = publicExponent; } /// /// Constructor /// /// Public key public StrongNamePublicKey(PublicKey pk) : this(pk.Data) { } /// /// Constructor /// /// Public key data /// Strong name key is invalid public StrongNamePublicKey(byte[] pk) : this(new BinaryReader(new MemoryStream(pk))) { } /// /// Constructor /// /// Public key file /// Strong name key is invalid public StrongNamePublicKey(string filename) : this(File.ReadAllBytes(filename)) { } /// /// Constructor /// /// Public key stream /// Strong name key is invalid public StrongNamePublicKey(Stream stream) : this(new BinaryReader(stream)) { } /// /// Constructor /// /// Public key reader /// Strong name key is invalid public StrongNamePublicKey(BinaryReader reader) { try { // Read PublicKeyBlob signatureAlgorithm = (SignatureAlgorithm)reader.ReadUInt32(); hashAlgorithm = (AssemblyHashAlgorithm)reader.ReadUInt32(); /*int pkLen = */reader.ReadInt32(); // Read PUBLICKEYSTRUC if (reader.ReadByte() != 6) throw new InvalidKeyException("Not a public key"); if (reader.ReadByte() != 2) throw new InvalidKeyException("Invalid version"); reader.ReadUInt16(); // reserved if ((SignatureAlgorithm)reader.ReadUInt32() != SignatureAlgorithm.CALG_RSA_SIGN) throw new InvalidKeyException("Not RSA sign"); // Read RSAPUBKEY if (reader.ReadUInt32() != RSA1_SIG) // magic = RSA1 throw new InvalidKeyException("Invalid RSA1 magic"); uint bitLength = reader.ReadUInt32(); publicExponent = reader.ReadBytesReverse(4); modulus = reader.ReadBytesReverse((int)(bitLength / 8)); } catch (IOException ex) { throw new InvalidKeyException("Invalid public key", ex); } } /// /// Creates a public key blob /// public byte[] CreatePublicKey() => CreatePublicKey(signatureAlgorithm, hashAlgorithm, modulus, publicExponent); internal static byte[] CreatePublicKey(SignatureAlgorithm sigAlg, AssemblyHashAlgorithm hashAlg, byte[] modulus, byte[] publicExponent) { if (sigAlg != SignatureAlgorithm.CALG_RSA_SIGN) throw new ArgumentException("Signature algorithm must be RSA"); var outStream = new MemoryStream(); var writer = new BinaryWriter(outStream); writer.Write((uint)sigAlg); // SigAlgID writer.Write((uint)hashAlg); // HashAlgID writer.Write(0x14 + modulus.Length);// cbPublicKey writer.Write((byte)6); // bType (public key) writer.Write((byte)2); // bVersion writer.Write((ushort)0); // reserved writer.Write((uint)sigAlg); // aiKeyAlg writer.Write(RSA1_SIG); // magic (RSA1) writer.Write(modulus.Length * 8); // bitlen writer.WriteReverse(publicExponent);// pubexp writer.WriteReverse(modulus); // modulus return outStream.ToArray(); } /// public override string ToString() => Utils.ToHex(CreatePublicKey(), false); } /// /// Stores a strong name key pair /// public sealed class StrongNameKey { const uint RSA2_SIG = 0x32415352; byte[] publicKey; readonly AssemblyHashAlgorithm hashAlg; readonly byte[] publicExponent; readonly byte[] modulus; readonly byte[] prime1; readonly byte[] prime2; readonly byte[] exponent1; readonly byte[] exponent2; readonly byte[] coefficient; readonly byte[] privateExponent; /// /// Gets the public key /// public byte[] PublicKey { get { if (publicKey is null) Interlocked.CompareExchange(ref publicKey, CreatePublicKey(), null); return publicKey; } } /// /// Gets the strong name signature size in bytes /// public int SignatureSize => modulus.Length; /// /// Gets the public key hash algorithm. It's usually /// public AssemblyHashAlgorithm HashAlgorithm => hashAlg; /// /// Gets the public exponent /// public byte[] PublicExponent => publicExponent; /// /// Gets the modulus /// public byte[] Modulus => modulus; /// /// Gets prime1 /// public byte[] Prime1 => prime1; /// /// Gets prime2 /// public byte[] Prime2 => prime2; /// /// Gets exponent1 /// public byte[] Exponent1 => exponent1; /// /// Gets exponent2 /// public byte[] Exponent2 => exponent2; /// /// Gets the coefficient /// public byte[] Coefficient => coefficient; /// /// Gets the private exponent /// public byte[] PrivateExponent => privateExponent; /// /// Constructor /// /// Strong name key data /// Strong name key is invalid public StrongNameKey(byte[] keyData) : this(new BinaryReader(new MemoryStream(keyData))) { } /// /// Constructor /// /// Strong name key file /// Strong name key is invalid public StrongNameKey(string filename) : this(File.ReadAllBytes(filename)) { } /// /// Constructor /// /// Strong name key stream /// Strong name key is invalid public StrongNameKey(Stream stream) : this(new BinaryReader(stream)) { } /// /// Constructor /// /// Strong name key reader /// Strong name key is invalid public StrongNameKey(BinaryReader reader) { /* * Links: * https://msdn.microsoft.com/en-us/library/cc250013%28v=prot.20%29.aspx * https://docs.microsoft.com/en-us/windows/desktop/SecCrypto/rsa-schannel-key-blobs * * struct PublicKeyBlob { * unsigned int SigAlgID; // sig algorithm used to create the sig (00002400 = CALG_RSA_SIGN) * unsigned int HashAlgID; // hash alg used to create the sig (usually 00008004 = CALG_SHA1) * ULONG cbPublicKey; // Size of the data that follows * // the rest is here * } * * typedef struct _PUBLICKEYSTRUC { * BYTE bType; * BYTE bVersion; * WORD reserved; * ALG_ID aiKeyAlg; * } BLOBHEADER, PUBLICKEYSTRUC; * * typedef struct _RSAPUBKEY { * DWORD magic; * DWORD bitlen; * DWORD pubexp; * } RSAPUBKEY; * * Format of public key * PublicKeyBlob * PUBLICKEYSTRUC publickeystruc; * RSAPUBKEY rsapubkey; * BYTE modulus[rsapubkey.bitlen/8] * * Format of public/private key pair * PUBLICKEYSTRUC publickeystruc; * RSAPUBKEY rsapubkey; * BYTE modulus[rsapubkey.bitlen/8]; * BYTE prime1[rsapubkey.bitlen/16]; // aka P * BYTE prime2[rsapubkey.bitlen/16]; // aka Q * BYTE exponent1[rsapubkey.bitlen/16]; // aka DP * BYTE exponent2[rsapubkey.bitlen/16]; // aka DQ * BYTE coefficient[rsapubkey.bitlen/16]; // aka IQ * BYTE privateExponent[rsapubkey.bitlen/8];// aka D */ try { publicKey = null; // Read PUBLICKEYSTRUC if (reader.ReadByte() != 7) throw new InvalidKeyException("Not a public/private key pair"); if (reader.ReadByte() != 2) throw new InvalidKeyException("Invalid version"); reader.ReadUInt16(); // reserved if ((SignatureAlgorithm)reader.ReadUInt32() != SignatureAlgorithm.CALG_RSA_SIGN) throw new InvalidKeyException("Not RSA sign"); // Read RSAPUBKEY if (reader.ReadUInt32() != RSA2_SIG) // magic = RSA2 throw new InvalidKeyException("Invalid RSA2 magic"); uint bitLength = reader.ReadUInt32(); publicExponent = reader.ReadBytesReverse(4); int len8 = (int)(bitLength / 8); int len16 = (int)(bitLength / 16); // Read the rest modulus = reader.ReadBytesReverse(len8); prime1 = reader.ReadBytesReverse(len16); prime2 = reader.ReadBytesReverse(len16); exponent1 = reader.ReadBytesReverse(len16); exponent2 = reader.ReadBytesReverse(len16); coefficient = reader.ReadBytesReverse(len16); privateExponent = reader.ReadBytesReverse(len8); } catch (IOException ex) { throw new InvalidKeyException("Couldn't read strong name key", ex); } } StrongNameKey(AssemblyHashAlgorithm hashAlg, byte[] publicExponent, byte[] modulus, byte[] prime1, byte[] prime2, byte[] exponent1, byte[] exponent2, byte[] coefficient, byte[] privateExponent) { this.hashAlg = hashAlg; this.publicExponent = publicExponent; this.modulus = modulus; this.prime1 = prime1; this.prime2 = prime2; this.exponent1 = exponent1; this.exponent2 = exponent2; this.coefficient = coefficient; this.privateExponent = privateExponent; } /// /// Creates a strong name key with a new hash algorithm /// /// Algorithm /// public StrongNameKey WithHashAlgorithm(AssemblyHashAlgorithm hashAlgorithm) { if (hashAlg == hashAlgorithm) return this; return new StrongNameKey(hashAlgorithm, publicExponent, modulus, prime1, prime2, exponent1, exponent2, coefficient, privateExponent); } byte[] CreatePublicKey() { var halg = hashAlg == 0 ? AssemblyHashAlgorithm.SHA1 : hashAlg; return StrongNamePublicKey.CreatePublicKey(SignatureAlgorithm.CALG_RSA_SIGN, halg, modulus, publicExponent); } /// /// Creates an instance /// public RSA CreateRSA() { RSAParameters rsaParams; rsaParams = new RSAParameters { Exponent = publicExponent, Modulus = modulus, P = prime1, Q = prime2, DP = exponent1, DQ = exponent2, InverseQ = coefficient, D = privateExponent, }; var rsa = RSA.Create(); try { rsa.ImportParameters(rsaParams); return rsa; } catch { ((IDisposable)rsa).Dispose(); throw; } } /// /// Creates a strong name blob /// public byte[] CreateStrongName() { var outStream = new MemoryStream(); var writer = new BinaryWriter(outStream); writer.Write((byte)7); // bType (public/private key) writer.Write((byte)2); // bVersion writer.Write((ushort)0); // reserved writer.Write((uint)SignatureAlgorithm.CALG_RSA_SIGN); // aiKeyAlg writer.Write(RSA2_SIG); // magic (RSA2) writer.Write(modulus.Length * 8); // bitlen writer.WriteReverse(publicExponent); writer.WriteReverse(modulus); writer.WriteReverse(prime1); writer.WriteReverse(prime2); writer.WriteReverse(exponent1); writer.WriteReverse(exponent2); writer.WriteReverse(coefficient); writer.WriteReverse(privateExponent); return outStream.ToArray(); } /// /// Creates a counter signature, just like /// sn -a IdentityPubKey.snk IdentityKey.snk SignaturePubKey.snk can do. /// The public key sn prints is 's value. /// /// Identity public key /// Identity strong name key pair /// Signature public key /// The counter signature as a hex string public static string CreateCounterSignatureAsString(StrongNamePublicKey identityPubKey, StrongNameKey identityKey, StrongNamePublicKey signaturePubKey) { var counterSignature = CreateCounterSignature(identityPubKey, identityKey, signaturePubKey); return Utils.ToHex(counterSignature, false); } /// /// Creates a counter signature, just like /// sn -a IdentityPubKey.snk IdentityKey.snk SignaturePubKey.snk can do. /// The public key sn prints is 's value. /// /// Identity public key /// Identity strong name key pair /// Signature public key /// The counter signature public static byte[] CreateCounterSignature(StrongNamePublicKey identityPubKey, StrongNameKey identityKey, StrongNamePublicKey signaturePubKey) { var hash = AssemblyHash.Hash(signaturePubKey.CreatePublicKey(), identityPubKey.HashAlgorithm); using (var rsa = identityKey.CreateRSA()) { var rsaFmt = new RSAPKCS1SignatureFormatter(rsa); string hashName = identityPubKey.HashAlgorithm.GetName(); rsaFmt.SetHashAlgorithm(hashName); var snSig = rsaFmt.CreateSignature(hash); Array.Reverse(snSig); return snSig; } } } }