Assembly در .NET؛ Manifest، Strong Name و Authenticode Signing

فصل ۱۷: Assembly، Manifest، Module، Strong Name و Authenticode

فصل ۱۷: Assembly، Manifest، Module، Strong Name و Authenticode

۱۷ — Assemblyها

Assembly واحد پایهٔ Deployment در .NET و همچنین Container همهٔ Typeها است. یک Assembly شامل Typeهای Compileشده همراه با کد Intermediate Language یا IL آن‌ها، Resourceهای Runtime و اطلاعاتی برای کمک به Versioning و Reference دادن به Assemblyهای دیگر است. Assembly همچنین یک Boundary برای Type Resolution تعریف می‌کند. در .NET، یک Assembly از یک File با پسوند .dll تشکیل می‌شود.

بیشتر Typeهای این فصل از Namespaceهای زیر می‌آیند:

System.Reflection
System.Resources
System.Globalization

داخل یک Assembly چه چیزهایی وجود دارد؟

یک Assembly چهار نوع محتوا دارد:

Assembly Manifest
اطلاعاتی برای CLR فراهم می‌کند؛ مانند نام Assembly، Version آن و Assemblyهای دیگری که Reference می‌کند.
Application Manifest
اطلاعاتی برای Operating System فراهم می‌کند؛ مانند نحوهٔ Deploy شدن Assembly و اینکه آیا Administrative Elevation لازم است یا نه.
Compiled Types
کد Compileشدهٔ IL و Metadata مربوط به Typeهای تعریف‌شده در Assembly.
Resources
داده‌های دیگری که داخل Assembly Embed شده‌اند، مانند Imageها و Text قابل Localization.

از میان این موارد، فقط Assembly Manifest اجباری است، هرچند تقریباً همیشه Assembly شامل Typeهای Compileشده نیز هست؛ مگر اینکه Resource Assembly باشد که بعداً توضیح داده می‌شود.

Assembly Manifest

Assembly Manifest دو وظیفه دارد:

  • Assembly را برای Managed Hosting Environment توصیف می‌کند.
  • به‌عنوان Directory مربوط به Moduleها، Typeها و Resourceهای داخل Assembly عمل می‌کند.

بنابراین Assemblyها Self-Describing هستند. مصرف‌کننده می‌تواند همهٔ Data، Typeها و Functionهای یک Assembly را بدون نیاز به Fileهای اضافی کشف کند.

خلاصهٔ Data مهم از نظر Functionality که در Manifest ذخیره می‌شود:

  • Simple Name مربوط به Assembly
  • شمارهٔ Version با AssemblyVersion
  • Public Key و Hash امضاشدهٔ Assembly، اگر Strongly Named باشد
  • فهرست Assemblyهای Referenceشده، شامل Version و Public Key آن‌ها
  • فهرست Typeهای تعریف‌شده در Assembly
  • Culture هدف، اگر Satellite Assembly باشد، با AssemblyCulture

Manifest می‌تواند اطلاعات توصیفی زیر را نیز ذخیره کند:

  • Title و Description کامل با AssemblyTitle و AssemblyDescription
  • اطلاعات Company و Copyright با AssemblyCompany و AssemblyCopyright
  • Display Version با AssemblyInformationalVersion
  • Attributeهای اضافی برای Data سفارشی

بخشی از این Data از Argumentهای داده‌شده به Compiler، مانند فهرست Assemblyهای Referenceشده یا Public Key مورد استفاده برای Signing، به دست می‌آید. بقیه از Assembly Attributeهایی می‌آید که در پرانتز اشاره شدند.

مشخص‌کردن Assembly Attributeها

Assembly Attributeهای رایج را می‌توان در Visual Studio، در صفحهٔ Properties پروژه و Tab مربوط به Package تعیین کرد. تنظیمات آن Tab به Project File با پسوند .csproj افزوده می‌شوند.

برای Attributeهایی که Package Tab پشتیبانی نمی‌کند، یا هنگامی که با .csproj کار نمی‌کنید، می‌توانید Assembly Attributeها را در Source Code تعیین کنید؛ این کار اغلب در Fileای به نام AssemblyInfo.cs انجام می‌شود.

یک File اختصاصی برای Attributeها فقط شامل using statementها و اعلان‌های Assembly Attribute است. برای مثال، برای آشکار کردن Typeهای با Scope داخلی به یک Unit Test Project، چنین می‌نویسید:

using System.Runtime.CompilerServices;
[assembly:InternalsVisibleTo("MyUnitTestProject")]

Application Manifest در Windows

Application Manifest یک XML File است که اطلاعات مربوط به Assembly را به OS منتقل می‌کند. هنگام Build، Application Manifest به‌عنوان Win32 Resource در Startup Executable Embed می‌شود. اگر وجود داشته باشد، پیش از آنکه CLR Assembly را Load کند خوانده و پردازش می‌شود و می‌تواند روی نحوهٔ Launch شدن Process برنامه توسط Windows اثر بگذارد.

Application Manifest مربوط به .NET یک Root Element به نام assembly در XML Namespace به نام urn:schemas-microsoft-com:asm.v1 دارد:

<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <!-- contents of manifest -->
</assembly>

Manifest زیر به OS می‌گوید Administrative Elevation درخواست کند:

<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level="requireAdministrator" />
      </requestedPrivileges>
    </security>
  </trustInfo>
</assembly>

Applicationهای UWP یک Manifest بسیار مفصل‌تر دارند که در File به نام Package.appxmanifest توصیف می‌شود. این Manifest شامل اعلان Capabilityهای برنامه است که Permissionهای داده‌شده از طرف OS را تعیین می‌کنند. ساده‌ترین راه ویرایش این File با Visual Studio است که هنگام Double-Click روی Manifest یک Dialog نشان می‌دهد.

Deploy کردن Application Manifest

برای افزودن Application Manifest به Project .NET در Visual Studio، روی Project در Solution Explorer راست‌کلیک کنید، Add و سپس New Item را بزنید و بعد Application Manifest File را انتخاب کنید. هنگام Build، Manifest داخل Output Assembly Embed می‌شود.

Moduleها

محتوای Assembly در واقع داخل یک Container میانی به نام Module بسته‌بندی می‌شود. هر Module متناظر با Fileای است که محتوای Assembly را نگه می‌دارد. علت این لایهٔ Container اضافی این است که اجازه دهد یک Assembly چند File را در بر بگیرد؛ قابلیتی که در .NET Framework وجود داشت، اما در .NET 5+ و .NET Core وجود ندارد. شکل 17-1 این رابطه را نشان می‌دهد.

شکل 17-1 — Assembly تک‌فایلی
شکل 17-1 — Assembly تک‌فایلی: Manifest اجباری و IL/Type Metadata و Resourceهای اختیاری در یک Module.

اگرچه .NET از Multifile Assembly پشتیبانی نمی‌کند، گاهی باید از سطح Container اضافی‌ای که Moduleها ایجاد می‌کنند آگاه باشید. سناریوی اصلی Reflection است؛ از جمله Reflecting Assemblies و Emitting Assemblies and Types.

کلاس Assembly

کلاس Assembly در System.Reflection دروازهٔ دسترسی به Assembly Metadata در Runtime است. راه‌های مختلفی برای گرفتن یک Assembly Object وجود دارد؛ ساده‌ترین راه property به نام Assembly روی یک Type است:

Assembly a = typeof (Program).Assembly;

همچنین می‌توانید با یکی از متدهای static کلاس Assembly یک Assembly Object بگیرید:

GetExecutingAssembly
Assembly مربوط به Typeای را برمی‌گرداند که Function در حال اجرا را تعریف کرده است.
GetCallingAssembly
مانند GetExecutingAssembly است، اما برای Functionای که Function فعلی را فراخوانی کرده است.
GetEntryAssembly
Assemblyای را برمی‌گرداند که Entry Method اصلی Application را تعریف کرده است.

پس از داشتن یک Assembly Object، می‌توانید با Propertyها و Methodهایش Metadata Assembly را Query کنید و Typeهای آن را با Reflection بررسی کنید. جدول 17-1 خلاصه‌ای از این Functionها را نشان می‌دهد.

جدول 17-1 — اعضای Assembly
Functionهاهدفبخش مرتبط
FullName, GetNameنام Fully Qualified یا یک AssemblyName را برمی‌گرداند.Assembly Names
CodeBase, LocationLocation مربوط به Assembly FileLoading, Resolving, and Isolating Assemblies
Load, LoadFrom, LoadFileAssembly را به‌صورت دستی در Memory Load می‌کند.Loading, Resolving, and Isolating Assemblies
GetSatelliteAssemblySatellite Assembly مربوط به Culture داده‌شده را پیدا می‌کند.Resources and Satellite Assemblies
GetType, GetTypesیک Type یا همهٔ Typeهای تعریف‌شده در Assembly را برمی‌گرداند.Reflecting and Activating Types
EntryPointEntry Method برنامه را به‌صورت MethodInfo برمی‌گرداند.Reflecting and Invoking Members
GetModule, GetModules, ManifestModuleهمهٔ Moduleها یا Main Module یک Assembly را برمی‌گرداند.Reflecting Assemblies
GetCustomAttribute, GetCustomAttributesAttributeهای Assembly را برمی‌گرداند.Working with Attributes

Strong Name و Assembly Signing

یک Strongly Named Assembly هویت یکتا دارد. این کار با افزودن دو بخش Metadata به Manifest انجام می‌شود:

  • یک شمارهٔ یکتا که متعلق به نویسندگان Assembly است.
  • یک Hash امضاشده از Assembly که اثبات می‌کند دارندهٔ آن شمارهٔ یکتا Assembly را تولید کرده است.

برای این کار به یک Public/Private Key Pair نیاز است. Public Key شمارهٔ شناسایی یکتا را فراهم می‌کند و Private Key امکان Signing را می‌دهد.

Public Key برای تضمین یکتایی Assembly Referenceها ارزشمند است: یک Strongly Named Assembly، Public Key را بخشی از Identity خود می‌کند.

در .NET Framework، Private Key از Assembly در برابر Tampering محافظت می‌کند؛ بدون Private Key شما، شخص دیگری نمی‌تواند نسخهٔ تغییریافتهٔ Assembly را منتشر کند بدون اینکه Signature خراب شود. در عمل این موضوع هنگام Load کردن Assembly در Global Assembly Cache مربوط به .NET Framework کاربرد دارد. در .NET 5+ و .NET Core، Signature کاربرد کمی دارد، زیرا هرگز Check نمی‌شود.

افزودن Strong Name به Assemblyای که قبلاً Weak Named بوده Identity آن را تغییر می‌دهد. به همین دلیل اگر فکر می‌کنید Assembly در آینده ممکن است Strong Name لازم داشته باشد، بهتر است از همان ابتدا Strong Name داشته باشد.

چگونه Assembly را Strongly Name کنیم

ابتدا با Utility به نام sn.exe یک Public/Private Key Pair بسازید:

sn.exe -k MyKeyPair.snk

این Command یک Key Pair جدید می‌سازد و آن را در Fileای به نام MyKeyPair.snk ذخیره می‌کند. اگر بعداً این File را گم کنید، توانایی Recompile کردن Assembly با همان Identity را برای همیشه از دست خواهید داد.

با Update کردن Project File می‌توانید Assembly را با این File Sign کنید. در Visual Studio به Project Properties بروید و در Tab مربوط به Signing، گزینهٔ «Sign the assembly» را فعال کرده و File با پسوند .snk را انتخاب کنید.

همان Key Pair می‌تواند چند Assembly را Sign کند؛ اگر Simple Nameهای آن‌ها متفاوت باشد، Identityهای آن‌ها همچنان متفاوت خواهد بود.

Assembly Nameها

Identity یک Assembly از چهار قطعهٔ Metadata در Manifest تشکیل می‌شود:

  • Simple Name
  • Version؛ اگر وجود نداشته باشد 0.0.0.0
  • Culture؛ اگر Satellite نباشد neutral
  • Public Key Token؛ اگر Strongly Named نباشد null

Simple Name از هیچ Attributeای نمی‌آید، بلکه از نام Fileای می‌آید که Assembly در ابتدا به آن Compile شده است، با حذف Extension. بنابراین Simple Name مربوط به System.Xml.dll برابر System.Xml است. Rename کردن File، Simple Name داخل Assembly را عوض نمی‌کند.

Version Number از Attribute به نام AssemblyVersion می‌آید و Stringی است که به چهار بخش تقسیم می‌شود:

major.minor.build.revision

Version را می‌توان چنین تعیین کرد:

[assembly: AssemblyVersion ("2.5.6.7")]

Culture از Attribute به نام AssemblyCulture می‌آید و روی Satellite Assemblyها اعمال می‌شود. Public Key Token نیز از Strong Name داده‌شده هنگام Compile می‌آید.

Fully Qualified Nameها

Fully Qualified Assembly Name رشته‌ای است که هر چهار مؤلفهٔ Identity را با قالب زیر در بر می‌گیرد:

simple-name, Version=version, Culture=culture, PublicKeyToken=public-key

برای مثال Fully Qualified Name مربوط به System.Private.CoreLib.dll چنین است:

System.Private.CoreLib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=7cec85d7bea7798e

اگر Assembly هیچ AssemblyVersion نداشته باشد، Version به شکل 0.0.0.0 ظاهر می‌شود. اگر Unsigned باشد، Public Key Token به شکل null نمایش داده می‌شود.

property به نام FullName در یک Assembly، Fully Qualified Name را برمی‌گرداند. Compiler هنگام ثبت Assembly Referenceها در Manifest همیشه از Fully Qualified Name استفاده می‌کند.

کلاس AssemblyName

AssemblyName کلاسی است که برای هر یک از چهار مؤلفهٔ Fully Qualified Assembly Name یک Property strongly typed دارد. دو هدف اصلی دارد:

  • یک Fully Qualified Assembly Name را Parse یا Build می‌کند.
  • مقداری Data اضافه برای کمک به Resolve کردن یا یافتن Assembly نگه می‌دارد.

یک AssemblyName را به هر یک از روش‌های زیر می‌توانید بگیرید:

  • نمونه‌سازی AssemblyName و دادن Fully Qualified Name.
  • فراخوانی GetName روی یک Assembly موجود.
  • فراخوانی AssemblyName.GetAssemblyName و دادن Path یک Assembly File روی Disk.

همچنین می‌توانید AssemblyName را بدون Argument بسازید و سپس هر Property را تنظیم کنید تا Fully Qualified Name ساخته شود. وقتی به این شکل Construct شود Mutable است.

Propertyها و Methodهای اصلی:

string      FullName    { get; }            // Fully qualified name
string      Name        { get; set; }       // Simple name
Version     Version     { get; set; }       // Assembly version
CultureInfo CultureInfo { get; set; }       // For satellite assemblies
string      CodeBase    { get; set; }       // Location
byte[]      GetPublicKey();                 // 160 bytes
void        SetPublicKey (byte[] key);
byte[]      GetPublicKeyToken();            // 8-byte version
void        SetPublicKeyToken (byte[] publicKeyToken);

Version خودش یک نمایش strongly typed است و Propertyهایی برای Major، Minor، Build و Revision دارد. GetPublicKey کل Cryptographic Public Key را برمی‌گرداند؛ GetPublicKeyToken هشت Byte آخر را که برای ایجاد Identity استفاده می‌شوند برمی‌گرداند.

برای گرفتن Simple Name یک Assembly با AssemblyName:

Console.WriteLine (typeof (string).Assembly.GetName().Name);
// System.Private.CoreLib

برای گرفتن Version:

string v = myAssembly.GetName().Version.ToString();

Assembly Informational Version و File Version

دو Assembly Attribute دیگر برای بیان اطلاعات Version وجود دارد. برخلاف AssemblyVersion، دو Attribute زیر روی Identity یک Assembly اثر نمی‌گذارند و بنابراین روی رفتار Compile-Time یا Runtime اثری ندارند:

AssemblyInformationalVersion
Versionی که به End User نمایش داده می‌شود. در Dialog مربوط به Windows File Properties با عنوان Product Version دیده می‌شود. هر Stringی می‌تواند اینجا قرار گیرد، مانند 5.1 Beta 2. معمولاً همهٔ Assemblyهای یک Application یک Informational Version یکسان می‌گیرند.
AssemblyFileVersion
برای اشاره به Build Number همان Assembly طراحی شده است. در Windows File Properties با عنوان File Version دیده می‌شود. مانند AssemblyVersion باید Stringی شامل حداکثر چهار Number جداشده با Period باشد.

Authenticode Signing

Authenticode یک سیستم Code Signing است که هدفش اثبات Identity ناشر است. Authenticode و Strong-Name Signing مستقل از هم هستند؛ می‌توانید Assembly را با یکی یا هر دو Sign کنید.

اگرچه Strong-Name Signing می‌تواند ثابت کند Assemblyهای A، B و C از یک Party آمده‌اند، با فرض اینکه Private Key فاش نشده باشد، نمی‌تواند بگوید آن Party چه کسی بوده است. برای دانستن اینکه Party مثلاً Joe Albahari یا Microsoft Corporation بوده است به Authenticode نیاز دارید.

Authenticode هنگام Download برنامه‌ها از Internet مفید است، زیرا اطمینان می‌دهد برنامه از فرد یا سازمانی آمده که Certificate Authority نام برده و در مسیر انتقال تغییر نکرده است. همچنین هنگام اجرای نخستین‌بار یک Application Downloadشده، Warning مربوط به «Unknown Publisher» را حذف می‌کند. Authenticode Signing برای ارسال App به Windows Store نیز الزامی است.

Authenticode فقط با Assemblyهای .NET کار نمی‌کند؛ Executableها و Binaryهای Unmanaged مانند Deployment Fileهای .msi را نیز پشتیبانی می‌کند. البته Authenticode تضمین نمی‌کند برنامه Malware ندارد، هرچند احتمال آن را کمتر می‌کند؛ زیرا فرد یا سازمانی حاضر شده نام خود را، با پشتوانهٔ Passport یا Company Document، پشت Executable یا Library قرار دهد.

Signing با Authenticode مستلزم تماس با یک Certificate Authority یا CA و ارائهٔ مدرک Identity شخص یا شرکت، مانند مدارک ثبتی شرکت، است. پس از بررسی مدارک، CA یک X.509 Code-Signing Certificate صادر می‌کند که معمولاً یک تا پنج سال اعتبار دارد. با آن می‌توانید Assemblyها را با Utility به نام signtool Sign کنید. همچنین می‌توانید با Utility به نام makecert خودتان Certificate بسازید، اما چنین Certificateای فقط روی Computerهایی شناخته می‌شود که صریحاً روی آن‌ها Install شده باشد.

اینکه Certificateهای Self-Signed نشده می‌توانند روی هر Computer کار کنند متکی بر Public Key Infrastructure است. اساساً Certificate شما با Certificate دیگری که متعلق به CA است Sign می‌شود. CA مورد اعتماد است، چون CAها در OS Load شده‌اند. برای دیدن آن‌ها در Windows Control Panel، عبارت certificate را Search کنید، در Administrative Tools روی «Manage computer certificates» بزنید، Certificate Manager را باز کنید و از Trusted Root Certification Authorities وارد Certificates شوید. CA در صورت فاش شدن Certificate یک Publisher می‌تواند آن را Revoke کند؛ بنابراین Verification امضای Authenticode مستلزم دریافت دوره‌ای فهرست به‌روز Certificateهای Revokeشده از CA است.

از آنجا که Authenticode از Cryptographic Signing استفاده می‌کند، اگر کسی بعداً File را دستکاری کند Signature نامعتبر می‌شود. Cryptography، Hashing و Signing در فصل ۲۰ بررسی می‌شوند.

چگونه با Authenticode Sign کنیم

گرفتن و نصب Certificate

گام نخست گرفتن Code-Signing Certificate از یک CA است. سپس یا می‌توانید با Certificate به‌صورت File محافظت‌شده با Password کار کنید یا آن را در Certificate Store کامپیوتر Load کنید. مزیت روش دوم این است که می‌توانید بدون نیاز به مشخص کردن Password Sign کنید؛ این کار مانع نمایش Password در Automated Build Scriptها یا Batch Fileها می‌شود.

Code-Signing Certificate را از کجا بگیریم؟

فقط تعداد محدودی Code-Signing CA به‌عنوان Root Certification Authority از پیش در Windows Load شده‌اند. از جمله Comodo، GoDaddy، GlobalSign، DigiCert، Thawte و Symantec.

Resellerهایی مانند K Software نیز Certificateهای Code Signing را از Authorityهای فوق با تخفیف ارائه می‌کنند.

Certificateهای Authenticode صادرشده توسط K Software، Comodo، GoDaddy و GlobalSign به‌عنوان گزینه‌هایی با محدودیت کمتر معرفی می‌شوند، چون برنامه‌های غیرمایکروسافتی را نیز Sign می‌کنند. به‌جز این تفاوت، Productهای Vendorهای مختلف از نظر Functionality معادل هستند.

توجه کنید Certificate مربوط به SSL عموماً برای Authenticode Signing قابل استفاده نیست، با وجود اینکه هر دو از زیرساخت X.509 استفاده می‌کنند. بخشی از دلیل این است که SSL دربارهٔ اثبات مالکیت Domain است، اما Authenticode دربارهٔ اثبات Identity شماست.

برای Load کردن Certificate در Certificate Store کامپیوتر، Certificate Manager را همان‌طور که گفته شد باز کنید. Folder مربوط به Personal را باز کنید، روی Folder به نام Certificates راست‌کلیک کنید و All Tasks/Import را انتخاب کنید. Import Wizard شما را در فرایند راهنمایی می‌کند. پس از کامل‌شدن Import، دکمهٔ View را روی Certificate بزنید، به Details بروید و Thumbprint آن را Copy کنید. این همان SHA-256 Hashی است که بعداً برای شناسایی Certificate هنگام Signing لازم دارید.

Signing با signtool.exe

با Utility به نام signtool که همراه Visual Studio است می‌توانید Programهای خود را Authenticode-Sign کنید. این ابزار در مسیر Microsoft SDKs\ClickOnce\SignTool زیر Program Files قرار دارد. Command زیر Fileای به نام LINQPad.exe را با Certificate موجود در My Store کامپیوتر به نام «Joseph Albahari» و Algorithm امن SHA256 Sign می‌کند:

signtool sign /n "Joseph Albahari" /fd sha256 LINQPad.exe

همچنین با /d و /du می‌توانید Description و Product URL را مشخص کنید:

... /d LINQPad /du http://www.linqpad.net

در بیشتر موارد همچنین لازم است یک Time-Stamping Server مشخص کنید؛ ادامهٔ این موضوع در مقالهٔ بعدی آمده است.

منبع: C# 12 in a Nutshell, The Definitive Reference — Chapter 17, pages 765–775. ترجمهٔ متن مطابق ساختار منبع انجام شده است.

امتیاز کاربران به این مقاله

☆☆☆☆☆

0 نفر امتیاز داده اند. میانگین: 0.0 از 5

 

0 نظر

نظر محترم شما در مورد مقاله های وب سایت برنامه نویسی و پایگاه داده

نظرات محترم شما در خدمات رسانی بهتر ما را یاری می نمایند. لطفا اگر مایل بودید یک نظر ما را مهمان فرمائید. آدرس ایمیل و وب سایت شما نمایش داده نخواهد شد.

0 / 500

اطلاعات تماس

  • آدرس:اصفهان-خیابان ام کلثوم غربی - بعد خیابان تخم چی - بیست متر بعد از پیتزا ننه شب - کوچه تعمیر گاه سمار زغالی - پلاک 354 - درب مشکی - طبقه هفتم
  • آدرس ایمیل:najafzade@gmail.com
  • وب سایت:http://www.a00b.com/
  • تلفن ثابت:(+98)9131253620
  • تلفن همراه:09131253620