/// <summary> /// Removes any friend assembly references (InternalsVisibleTo attributes) that do not have public keys. /// </summary> /// <param name="assemblyPath">The path to the assembly you want to remove friend references from.</param> /// <param name="keyPath">The path to the strong-name key file you want to use (.snk or .pfx).</param> /// <param name="keyFilePassword">The password for the provided strong-name key file.</param> /// <param name="probingPaths">Additional paths to probe for references.</param> /// <returns> /// <c>true</c> if any invalid friend references were found and fixed, <c>false</c> if no invalid friend references was found. /// </returns> /// <exception cref="System.ArgumentNullException">assemblyPath was not provided.</exception> /// <exception cref="System.IO.FileNotFoundException">Could not find provided assembly file.</exception> public static bool RemoveInvalidFriendAssemblies(string assemblyPath, string keyPath, string keyFilePassword, params string[] probingPaths) { // Verify assembly path was passed in. if (string.IsNullOrWhiteSpace(assemblyPath)) { throw new ArgumentNullException(nameof(assemblyPath)); } // Make sure the file actually exists. if (!File.Exists(assemblyPath)) { throw new FileNotFoundException($"Could not find provided assembly file '{assemblyPath}'.", assemblyPath); } bool fixApplied = false; using (var outFileMgr = new OutputFileManager(assemblyPath, assemblyPath)) { using (var a = AssemblyDefinition.ReadAssembly(assemblyPath, GetReadParameters(assemblyPath, probingPaths))) { var ivtAttributes = a.CustomAttributes.Where(attr => attr.AttributeType.FullName == typeof(InternalsVisibleToAttribute).FullName).ToList(); foreach (var friendReference in ivtAttributes) { // Find any without a public key defined. if (friendReference.HasConstructorArguments && friendReference.ConstructorArguments.Any(ca => ca.Value?.ToString().IndexOf("PublicKey=", StringComparison.Ordinal) == -1)) { a.CustomAttributes.Remove(friendReference); fixApplied = true; } } if (fixApplied) { if (!a.Name.IsRetargetable) { a.Write(outFileMgr.IntermediateAssemblyPath, new WriterParameters { StrongNameKeyPair = GetStrongNameKeyPair(keyPath, keyFilePassword), WriteSymbols = File.Exists(Path.ChangeExtension(assemblyPath, ".pdb")) }); } AssemblyInfoCache.TryRemove(assemblyPath, out var _); } } outFileMgr.Commit(); } return(fixApplied); }
/// <summary> /// Fixes an assembly reference. /// </summary> /// <param name="assemblyPath">The path to the assembly you want to fix a reference for.</param> /// <param name="referenceAssemblyPath">The path to the reference assembly path you want to fix in the first assembly.</param> /// <param name="keyPath">The path to the strong-name key file you want to use (.snk or .pfx).</param> /// <param name="keyFilePassword">The password for the provided strong-name key file.</param> /// <param name="probingPaths">Additional paths to probe for references.</param> /// <returns> /// <c>true</c> if an assembly reference was found and fixed, <c>false</c> if no reference was found. /// </returns> /// <exception cref="System.ArgumentNullException">assemblyPath was not provided. /// or /// referenceAssemblyPath was not provided.</exception> /// <exception cref="System.IO.FileNotFoundException">Could not find provided assembly file. /// or /// Could not find provided reference assembly file.</exception> public static bool FixAssemblyReference(string assemblyPath, string referenceAssemblyPath, string keyPath, string keyFilePassword, params string[] probingPaths) { // Verify assembly path was passed in. if (string.IsNullOrWhiteSpace(assemblyPath)) { throw new ArgumentNullException(nameof(assemblyPath)); } if (string.IsNullOrWhiteSpace(referenceAssemblyPath)) { throw new ArgumentNullException(nameof(referenceAssemblyPath)); } // Make sure the file actually exists. if (!File.Exists(assemblyPath)) { throw new FileNotFoundException($"Could not find provided assembly file '{assemblyPath}'.", assemblyPath); } if (!File.Exists(referenceAssemblyPath)) { throw new FileNotFoundException($"Could not find provided reference assembly file '{referenceAssemblyPath}'.", referenceAssemblyPath); } bool fixApplied = false; using (var fileManagerA = new OutputFileManager(assemblyPath, assemblyPath)) using (var fileManagerB = new OutputFileManager(referenceAssemblyPath, referenceAssemblyPath)) { using (var a = AssemblyDefinition.ReadAssembly(assemblyPath, GetReadParameters(assemblyPath, probingPaths))) using (var b = AssemblyDefinition.ReadAssembly(referenceAssemblyPath, GetReadParameters(referenceAssemblyPath, probingPaths))) { var assemblyReference = a.MainModule.AssemblyReferences.FirstOrDefault(r => r.Name.Equals(b.Name.Name, StringComparison.OrdinalIgnoreCase)); // Found a matching reference, let's set the public key token. if (assemblyReference != null && BitConverter.ToString(assemblyReference.PublicKeyToken) != BitConverter.ToString(b.Name.PublicKeyToken)) { assemblyReference.PublicKeyToken = b.Name.PublicKeyToken ?? new byte[0]; assemblyReference.Version = b.Name.Version; a.Write(fileManagerA.IntermediateAssemblyPath, new WriterParameters { StrongNameKeyPair = GetStrongNameKeyPair(keyPath, keyFilePassword), WriteSymbols = File.Exists(Path.ChangeExtension(assemblyPath, ".pdb")) }); AssemblyInfoCache.TryRemove(assemblyPath, out var _); fixApplied = true; } var friendReference = b.CustomAttributes.SingleOrDefault(attr => attr.AttributeType.FullName == typeof(InternalsVisibleToAttribute).FullName && attr.ConstructorArguments[0].Value.ToString() == a.Name.Name); if (friendReference != null && a.Name.HasPublicKey) { // Add the public key to the attribute. var typeRef = friendReference.ConstructorArguments[0].Type; friendReference.ConstructorArguments.Clear(); friendReference.ConstructorArguments.Add(new CustomAttributeArgument(typeRef, a.Name.Name + ", PublicKey=" + BitConverter.ToString(a.Name.PublicKey).Replace("-", string.Empty))); // Save and resign. b.Write(fileManagerB.IntermediateAssemblyPath, new WriterParameters { StrongNameKeyPair = GetStrongNameKeyPair(keyPath, keyFilePassword), WriteSymbols = File.Exists(Path.ChangeExtension(referenceAssemblyPath, ".pdb")) }); AssemblyInfoCache.TryRemove(assemblyPath, out var _); fixApplied = true; } } fileManagerA.Commit(); fileManagerB.Commit(); } return(fixApplied); }
/// <summary> /// Signs the assembly at the specified path with your own strong-name key file. /// </summary> /// <param name="assemblyPath">The path to the assembly you want to strong-name sign.</param> /// <param name="keyPath">The path to the strong-name key file you want to use (.snk or .pfx).</param> /// <param name="outputPath">The directory path where the strong-name signed assembly will be copied to.</param> /// <param name="keyFilePassword">The password for the provided strong-name key file.</param> /// <param name="probingPaths">Additional paths to probe for references.</param> /// <returns> /// The assembly information of the new strong-name signed assembly. /// </returns> /// <exception cref="System.ArgumentNullException">assemblyPath parameter was not provided.</exception> /// <exception cref="System.IO.FileNotFoundException">Could not find provided assembly file. /// or /// Could not find provided strong-name key file.</exception> /// <exception cref="System.BadImageFormatException">The file is not a .NET managed assembly.</exception> public static AssemblyInfo SignAssembly(string assemblyPath, string keyPath, string outputPath, string keyFilePassword, params string[] probingPaths) { // Verify assembly path was passed in. if (string.IsNullOrWhiteSpace(assemblyPath)) { throw new ArgumentNullException(nameof(assemblyPath)); } // Make sure the file actually exists. if (!File.Exists(assemblyPath)) { throw new FileNotFoundException($"Could not find provided assembly file '{assemblyPath}'.", assemblyPath); } if (string.IsNullOrWhiteSpace(outputPath)) { // Overwrite the file if no output path is provided. outputPath = Path.GetDirectoryName(assemblyPath); } else { // Create the directory structure. Directory.CreateDirectory(outputPath); } string outputFile = Path.Combine(Path.GetFullPath(outputPath), Path.GetFileName(assemblyPath)); using (var outputFileMgr = new OutputFileManager(assemblyPath, outputFile)) { // Get the assembly info and go from there. var info = GetAssemblyInfo(assemblyPath); // Don't sign assemblies with a strong-name signature. if (info.IsSigned) { // If the target directory is different from the input... if (!outputFileMgr.IsInPlaceReplace) { // ...just copy the source file to the destination. outputFileMgr.CopySourceToFinalOutput(); } return(GetAssemblyInfo(outputFile)); } if (outputFileMgr.IsInPlaceReplace) { outputFileMgr.CreateBackup(); } using (var ad = AssemblyDefinition.ReadAssembly(assemblyPath, GetReadParameters(assemblyPath, probingPaths))) { ad.Write(outputFileMgr.IntermediateAssemblyPath, new WriterParameters() { StrongNameKeyPair = GetStrongNameKeyPair(keyPath, keyFilePassword), WriteSymbols = outputFileMgr.HasSymbols }); } AssemblyInfoCache.TryRemove(assemblyPath, out var _); outputFileMgr.Commit(); return(GetAssemblyInfo(outputFile)); } }