#if false using Autodesk.Revit.DB; using Autodesk.Revit.DB.ExtensibleStorage; using OpenMcdf; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.OpenSsl; using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Security.Cryptography; using System.Text; using System.Windows; namespace ShrlAlgoToolkit.RevitCore.Assists; public class PublishAssist { #region FileAttributes /// /// 设置文件为只读 /// /// /// public static void SetReadOnly(string filePath, bool readOnly = true) { try { if(File.Exists(filePath)) { var info = new FileInfo(filePath); info.IsReadOnly = readOnly; } else { Debug.WriteLine("文件不存在。"); } } catch(UnauthorizedAccessException) { Debug.WriteLine("无权限修改文件属性。"); } catch(Exception ex) { Debug.WriteLine($"错误: {ex.Message}"); } } /// /// 判断文件是否被加密 /// /// /// /// public static bool IsEncrypted(string filePath, string tag = "VmVyc2lvbiAxLjAgU1pNZWRpRGF0YQ==") { // 关键:用写入时相同的编码(通常 UTF-8 或 ASCII) var tagBytes = Encoding.UTF8.GetBytes(tag); // 或 Encoding.ASCII var data = File.ReadAllBytes(filePath); // 检查开头和结尾是否匹配 var startMatch = data.Length >= tagBytes.Length && ArrayEquals(data, 0, tagBytes, 0, tagBytes.Length); var endMatch = data.Length >= tagBytes.Length * 2 && ArrayEquals(data, data.Length - tagBytes.Length, tagBytes, 0, tagBytes.Length); return startMatch && endMatch; } static bool ArrayEquals(byte[] a, int aStart, byte[] b, int bStart, int length) { for(var i = 0; i < length; i++) { if(a[aStart + i] != b[bStart + i]) return false; } return true; } /// /// 设置文件为只读 /// /// public static void SetPrivate(string filePath) { try { if(File.Exists(filePath)) { var info = new FileInfo(filePath); File.SetAttributes(filePath, FileAttributes.Hidden | FileAttributes.System | FileAttributes.ReadOnly); } else { Debug.WriteLine("文件不存在。"); } } catch(UnauthorizedAccessException) { Debug.WriteLine("无权限修改文件属性。"); } catch(Exception ex) { Debug.WriteLine($"错误: {ex.Message}"); } } #endregion //需要与addin文件里的GUID保持一致 private static readonly Guid SchemaGuid = new Guid("A519E82B-911C-4CA5-9BC5-ED509B2055D2"); /// /// 存储图片字节数据列表 (使用 Base64 编码) /// public static void StoreSignatureMap(Document doc, IDictionary signatureMap) { ProjectInfo projectInfo = doc.ProjectInformation; if(projectInfo == null) return; Schema schema = GetOrCreateSchema(); Entity entity = projectInfo.GetEntity(schema); if(entity == null || !entity.IsValid()) { entity = new Entity(schema); // 如果没有,才新建 } try { IDictionary stringMap = signatureMap .ToDictionary(kvp => kvp.Key, kvp => Convert.ToBase64String(kvp.Value)); entity.Set("SignatureMap", stringMap); projectInfo.SetEntity(entity); } catch(Exception ex) { MessageBox.Show(ex.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } /// /// 读取签章图片字节数据列表 (使用 Base64 解码) /// public static IDictionary RetrieveSignatureMap(Document doc) { ProjectInfo projectInfo = doc.ProjectInformation; var emptyDict = new Dictionary(); if(projectInfo == null) return emptyDict; Schema schema = Schema.Lookup(SchemaGuid); if(schema == null) return emptyDict; Entity entity = projectInfo.GetEntity(schema); if(entity == null || !entity.IsValid()) return emptyDict; IDictionary retrievedMap = entity.Get>("SignatureMap"); if(retrievedMap == null) return emptyDict; return retrievedMap.ToDictionary(kvp => kvp.Key, kvp => Convert.FromBase64String(kvp.Value)); } /// /// 存储基本信息数据列表 /// public static void StoreSignatureCommonMap(Document doc, IDictionary signatureCommonMap) { ProjectInfo projectInfo = doc.ProjectInformation; if(projectInfo == null) return; Schema schema = GetOrCreateSchema(); //每个元素只有一个实体 Entity entity = projectInfo.GetEntity(schema); if(entity == null || !entity.IsValid()) { entity = new Entity(schema); // 如果没有,才新建 } try { entity.Set("SignatureCommonMap", signatureCommonMap); projectInfo.SetEntity(entity); } catch(Exception ex) { MessageBox.Show(ex.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } /// /// 读取基本信息数据列表 /// public static IDictionary RetrieveSignatureCommonMap(Document doc) { ProjectInfo projectInfo = doc.ProjectInformation; var emptyDict = new Dictionary(); if(projectInfo == null) return emptyDict; Schema schema = Schema.Lookup(SchemaGuid); if(schema == null) return emptyDict; Entity entity = projectInfo.GetEntity(schema); if(entity == null || !entity.IsValid()) return emptyDict; IDictionary retrievedMap = entity.Get>("SignatureCommonMap"); if(retrievedMap == null) return emptyDict; return retrievedMap.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); } /// /// 获取或创建数据模式 /// private static Schema GetOrCreateSchema() { Schema schema = Schema.Lookup(SchemaGuid); if(schema != null) return schema; try { SchemaBuilder schemaBuilder = new SchemaBuilder(SchemaGuid); schemaBuilder.SetReadAccessLevel(AccessLevel.Application); schemaBuilder.SetWriteAccessLevel(AccessLevel.Application); schemaBuilder.SetVendorId("SZMC"); schemaBuilder.SetApplicationGUID(SchemaGuid); schemaBuilder.SetSchemaName("SignatureStorage"); // 添加 Map 字段用来存储图片字节数据列表 schemaBuilder.AddMapField("SignatureMap", typeof(string), typeof(string)); // 添加 Map 字段用来存储基本信息数据列表 schemaBuilder.AddMapField("SignatureCommonMap", typeof(string), typeof(string)); schema = schemaBuilder.Finish(); } catch(Exception ex) { MessageBox.Show(ex.Message, "创建schema错误"); } return schema; } #region ExtensionsStorage //private const string SchemaGuid = "{FBC67C54-781F-4D0A-B31B-8F54707889E0}"; ///// ///// 计算hash256 ///// ///// ///// //public static string ComputeFileHash(string filePath) //{ // using (SHA256 sha256 = SHA256.Create()) // using (FileStream stream = File.OpenRead(filePath)) // { // byte[] hashBytes = sha256.ComputeHash(stream); // return BitConverter.ToString(hashBytes).Replace("-", string.Empty).ToLower(); // } //} #endregion #region PreviewSettings /// /// 完全替换缩略图 /// /// /// internal static void ReplaceThumb(string rvtFilePath, Bitmap bmp) { using var cf = new CompoundFile(rvtFilePath, CFSUpdateMode.Update, CFSConfiguration.Default); // 获取根存储 CFStorage root = cf.RootStorage; // 更新 Windows 预览 if(root.TryGetStream("RevitPreview4.0", out var previewStream)) { using var outMs = new MemoryStream(); bmp.Save(outMs, ImageFormat.Png); previewStream.SetData(outMs.ToArray()); } // 更新 Revit 预览 if(root.TryGetStream("BasicFileInfo", out var basicFileInfo)) { // 读取现有数据 byte[] data = basicFileInfo.GetData(); // 基本文件信息头部大小(版本等信息) const int HEADER_SIZE = 0x2C8; if(data.Length > HEADER_SIZE) { // 转换图片为字节数组 using(var ms = new MemoryStream()) { // 转换为24位色 using(var temp = new Bitmap(bmp.Width, bmp.Height, PixelFormat.Format24bppRgb)) { using(var g = Graphics.FromImage(temp)) { g.DrawImage(bmp, 0, 0); } temp.Save(ms, ImageFormat.Bmp); } byte[] imageBytes = ms.ToArray(); // 跳过BMP文件头(14字节)和信息头(40字节) int startIndex = 54; // 计算新数据大小 int newDataSize = HEADER_SIZE + (imageBytes.Length - startIndex); byte[] newData = new byte[newDataSize]; // 复制原有头部数据 Array.Copy(data, 0, newData, 0, HEADER_SIZE); // 复制新的图像数据 Array.Copy(imageBytes, startIndex, newData, HEADER_SIZE, imageBytes.Length - startIndex); // 写回流 basicFileInfo.SetData(newData); } } } cf.Commit(); } /// /// 合并图片 /// /// 缩略图 /// 图标 /// internal static Bitmap CombineImages(Bitmap thumbnail, Bitmap overlay) { // 创建画布 Bitmap result = new Bitmap(thumbnail.Width, thumbnail.Height); using(Graphics graphics = Graphics.FromImage(result)) { // 绘制缩略图 graphics.DrawImage(thumbnail, 0, 0); // 计算叠加位置(右下角) int padding = 5; int x = 0 + padding; //int x = thumbnail.Width - overlay.Width - padding; int y = thumbnail.Height - overlay.Height - padding; // 保持图标原始大小或按比例缩放 int iconSize = 48; var resizedOverlay = new Bitmap(overlay, iconSize, iconSize); // 带透明度绘制 graphics.DrawImage( resizedOverlay, new System.Drawing.Rectangle(x, y, iconSize, iconSize), new System.Drawing.Rectangle(0, 0, overlay.Width, overlay.Height), GraphicsUnit.Pixel); } return result; } /// /// 添加水印 /// /// /// /// internal static void AddWatermark(string rvtFilePath, Bitmap bmp, string watermark) { // 打开 RVT 文件的复合文件结构 using var cf = new CompoundFile(rvtFilePath, CFSUpdateMode.Update, CFSConfiguration.Default); // 获取根存储 CFStorage root = cf.RootStorage; // 检查是否存在 "RevitPreview4.0" 流 if(!root.TryGetStream("RevitPreview4.0", out var previewStream)) { Debug.WriteLine("未找到 RevitPreview4.0 流。"); return; } // 在图像上绘制半透明文字水印 if(!string.IsNullOrEmpty(watermark)) { using var g = Graphics.FromImage(bmp); var font = new Font("微软雅黑", 20, System.Drawing.FontStyle.Bold); var brush = new SolidBrush(System.Drawing.Color.FromArgb(128, System.Drawing.Color.Red)); g.DrawString(watermark, font, brush, new PointF(20, bmp.Height - 80)); } // 将修改后的图像保存为 PNG,再写回流 using var outMs = new MemoryStream(); bmp.Save(outMs, ImageFormat.Png); previewStream.SetData(outMs.ToArray()); // 提交更改 cf.Commit(); } #endregion #region Hash // 公钥(硬编码) private const string PublicKeyPem = """ -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyN+Z04RmVOvzI2Z8Li1t xB9yv80Y4Pd+M0wiuW9BHccmVlm7LVlWDRZgaAykQxIt0UskZbaIQ5eR4mqywVoM BygfWqurVtgC6dO3SSbhZiOuSA3g1xJLgje6TOQIPWDGw1Ap0sbWrCuk+bxPFjSi XANksY3Qfl1e6YETYEEpptTXorM8enM4368/KkvEhFswBqE5/Qf2x7XUpPAfnh3Y cl45bUu87yDeyAnX4HEuA+aZPHEgXFPmhndtqMiUsLEajQNd8zZQHKmjMbubAq9s l10w7smZUck+bozHO0RP31ytI6n/7I0T9PXOb/rwjeXBxXD1K0kvU3DPuq2+a5sa iwIDAQAB -----END PUBLIC KEY----- """; // 私钥(硬编码) private const string PrivateKeyPem = """ -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAyN+Z04RmVOvzI2Z8Li1txB9yv80Y4Pd+M0wiuW9BHccmVlm7 LVlWDRZgaAykQxIt0UskZbaIQ5eR4mqywVoMBygfWqurVtgC6dO3SSbhZiOuSA3g 1xJLgje6TOQIPWDGw1Ap0sbWrCuk+bxPFjSiXANksY3Qfl1e6YETYEEpptTXorM8 enM4368/KkvEhFswBqE5/Qf2x7XUpPAfnh3Ycl45bUu87yDeyAnX4HEuA+aZPHEg XFPmhndtqMiUsLEajQNd8zZQHKmjMbubAq9sl10w7smZUck+bozHO0RP31ytI6n/ 7I0T9PXOb/rwjeXBxXD1K0kvU3DPuq2+a5saiwIDAQABAoIBAACdcETZkedzXBM5 6WofTAof4bWOgovvHJCipBgOiubPKPdG5jNq/bHX2/kSnbl2eWWMQUEuGIiYJccG /6zF6GDQ3FqHZcNbp8M4gbymNZV0TfTB0KOTyDAaojQ381LgyIK1DcWBHdw2wCRZ vo6v1BXez4evaUy5ckXQfAA9POiAH6Im7QQlPamaUJfBCfRqFNvTcL6YQngHg8RW 4xFvN8Lxhgnqftw0dY1YzNkDBz+toQ+211LTAJWYC4vZ2sHoGvR74YxZUWPS4wms QdIq50kRwL45Z9IZcxqp1x27W0pEwBsOO2DhnM6r7mB7Mg9BMEjUFf3oM+vrx67W w6McEXkCgYEA8q+Bb6ldNqWNa5uLmAziEqXCnN/X9o1Cr81YIeWZpx4Ovx2aR3wH 4OtWl9VmYrU7w758nhPH27q8ADxjSbIB3ShPPdfLcKBoW/5eg+K8Bexr5Fb6Lyqo SMhFLptxOdNzYLej99XppzRk4CV3s7IZsfQd6okSdfvlqka5ExFjXDkCgYEA0+TZ IBsc0nCZFTlcDxxMCkmj+c4+c01BGgSeoVSKIgyp5d5DSZoNjxXsrP3Nw3uq2+fh v4DD/V76BpzQ6v7P+Xg9YbW1QO6QdsG4Cf1bnAFYaTwQwdm2To5SXixYpVnON4Xv 4fOWVL0qrn3PK6aUTJCcEOdhx1YtBl+P4gVq9OMCgYEAhtLcDOXBhE96/rI+Xi2i FwwBz1dISo/14WcqNEKzFzXKqYBPqrEMS3dS9y02IVZgKoIUB1oj6T2XnmXuHw4G nQ/83fWZ7ysebyUk6w42uO+8jPGJMlT3gt/IF3mB8Mc5TR0YueIcWajw9dm645jp T4S4bgjSubtEv1FlFmPNmHkCgYAtmq5ka5FrdVJtL/gxRFzVRf4lsI7eT/039VCS 1lgYdfNHBuwidiZ+6jATF1jmvsIKLED472S/LmbPb4bDXO4z+f3z/qCxBedPt+e9 Nqs1y1BU7dmJbyr+g0EVBaRaihaI+qmjTsLHICOEhI1HDsYSKHl+Zd40gcaU/ZJB Cft4EwKBgQDeK/3k6OJTPuzskuqMsQy6yHd7hesxvdsfUH78tZrkRB2B9fciOa2v ulfBss9KKVCQeKaC+91ptLpMOlSJhc9tZUsWvE19vVlNmN0ezQyfSFn+E09Ouqin 0cMfedRgHigwi+T818aFnC8qPzTMqCMQyxRTYxEC43OB9VcPyzo1yA== -----END RSA PRIVATE KEY----- """; private static AsymmetricKeyParameter GetPublicKey() { using var reader = new StringReader(PublicKeyPem); var pemReader = new PemReader(reader); return (AsymmetricKeyParameter)pemReader.ReadObject(); } private static AsymmetricKeyParameter GetPrivateKey() { using var reader = new StringReader(PrivateKeyPem); var pemReader = new PemReader(reader); var keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject(); return keyPair.Private; } private static byte[] ComputeFileHash(string filePath) { using var sha256 = SHA256.Create(); using var stream = File.OpenRead(filePath); return sha256.ComputeHash(stream); } private static byte[] EncryptData(byte[] data, AsymmetricKeyParameter publicKey) { var rsaEngine = new Org.BouncyCastle.Crypto.Engines.RsaEngine(); rsaEngine.Init(true, publicKey); return rsaEngine.ProcessBlock(data, 0, data.Length); } private static byte[] DecryptData(byte[] encryptedData, AsymmetricKeyParameter privateKey) { var rsaEngine = new Org.BouncyCastle.Crypto.Engines.RsaEngine(); rsaEngine.Init(false, privateKey); return rsaEngine.ProcessBlock(encryptedData, 0, encryptedData.Length); } private static readonly string EncryptFileName = ".hashes.enc"; /// /// 获取相对路径 /// /// 相对于某个目录 /// 示例路径 /// private static string GetRelativePath(string relativeTo, string path) { // 确保路径以目录分隔符结尾(对于目录路径) string from = Path.GetFullPath(relativeTo).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + Path.DirectorySeparatorChar; string to = Path.GetFullPath(path); Uri fromUri = new Uri(from); Uri toUri = new Uri(to); // 计算相对 URI Uri relativeUri = fromUri.MakeRelativeUri(toUri); // 将相对 URI 转换回字符串,并替换可能的 '/' 为当前平台的分隔符 string relativePath = Uri.UnescapeDataString(relativeUri.ToString()).Replace('/', Path.DirectorySeparatorChar); return relativePath; } public static string GenerateEncFile(string directoryPath) { var publicKey = GetPublicKey(); var files = Directory.GetFiles(directoryPath, "*.rvt", SearchOption.AllDirectories); var encFilePath = Path.Combine(directoryPath, EncryptFileName); using var fs = new FileStream(encFilePath, FileMode.Create, FileAccess.Write); using var bw = new BinaryWriter(fs); foreach(var file in files) { //var fileName = Path.GetFileName(file); var fileName = GetRelativePath(directoryPath, file); var fileHash = ComputeFileHash(file); var nameBytes = Encoding.UTF8.GetBytes(fileName); var combined = nameBytes.Concat(fileHash).ToArray(); var encrypted = EncryptData(combined, publicKey); bw.Write(nameBytes.Length); bw.Write(nameBytes); bw.Write(encrypted.Length); bw.Write(encrypted); } return encFilePath; } public static StringBuilder VerifyEncFile(string directoryPath, out bool hasModified) { hasModified = false; var privateKey = GetPrivateKey(); var encFilePath = Path.Combine(directoryPath, EncryptFileName); StringBuilder sb = new StringBuilder(); if(!File.Exists(encFilePath)) { sb.AppendLine("验证文件未找到。"); return sb; } Dictionary allFiles; try { //转成相对路径 allFiles = Directory.GetFiles(directoryPath, "*.rvt", SearchOption.AllDirectories) .ToDictionary( file => GetRelativePath(directoryPath, file), file => file, StringComparer.OrdinalIgnoreCase); } catch(Exception ex) { MessageBox.Show(ex.Message, "验证出错!", MessageBoxButton.OK, MessageBoxImage.Error); return sb; } using var fs = new FileStream(encFilePath, FileMode.Open, FileAccess.Read); using var br = new BinaryReader(fs); while(fs.Position < fs.Length) { var nameLen = br.ReadInt32(); //if (nameLen <= 0 || nameLen > 256) // 假设文件名长度不超过256字节 //{ // Console.WriteLine("无效的文件名长度。"); // continue; //} var nameBytes = br.ReadBytes(nameLen); var fileName = Encoding.UTF8.GetString(nameBytes); var encLen = br.ReadInt32(); var encrypted = br.ReadBytes(encLen); if(!allFiles.TryGetValue(fileName, out var actualFilePath)) { sb.AppendLine($"{fileName} 未找到"); continue; } var decrypted = DecryptData(encrypted, privateKey); var expectedName = Encoding.UTF8.GetString(decrypted, 0, nameBytes.Length); var expectedHash = decrypted.Skip(nameBytes.Length).ToArray(); var currentHash = ComputeFileHash(actualFilePath); bool match = expectedName == fileName && currentHash.SequenceEqual(expectedHash); if(!match) { hasModified = true; sb.AppendLine($"{fileName}"); } } return sb; } #endregion } #endif