Проверьте цифровую подпись файла OSX (.dmg) в Windows
В настоящее время я проверяю, что цифровая подпись файлов установщика Windows (.msi) действительна в C# с использованием API WinVerifyTrust. Я также проверяю, что отпечаток от подписи из известного списка.
Мне нужно сделать то же самое для файлов Mac OSX (.dmg) в C# (в Windows). Есть какой-либо способ сделать это?
2 ответа
В любом файле DMG есть блок «Коли», я не думаю, что вы легко найдете готовый код для окон, способный прочитать его на С#... Но посмотрите здесь http://newosxbook.com/DMG.html
Что вас практически интересует, так это последние 512 байт файла.
typedef struct {
char Signature[4]; // Magic ('koly')
uint32_t Version; // Current version is 4
uint32_t HeaderSize; // sizeof(this), always 512
uint32_t Flags; // Flags
uint64_t RunningDataForkOffset; //
uint64_t DataForkOffset; // Data fork offset (usually 0, beginning of file)
uint64_t DataForkLength; // Size of data fork (usually up to the XMLOffset, below)
uint64_t RsrcForkOffset; // Resource fork offset, if any
uint64_t RsrcForkLength; // Resource fork length, if any
uint32_t SegmentNumber; // Usually 1, may be 0
uint32_t SegmentCount; // Usually 1, may be 0
uuid_t SegmentID; // 128-bit GUID identifier of segment (if SegmentNumber !=0)
uint32_t DataChecksumType; // Data fork
uint32_t DataChecksumSize; // Checksum Information
uint32_t DataChecksum[32]; // Up to 128-bytes (32 x 4) of checksum
uint64_t XMLOffset; // Offset of property list in DMG, from beginning
uint64_t XMLLength; // Length of property list
uint8_t Reserved1[120]; // 120 reserved bytes - zeroed
uint32_t ChecksumType; // Master
uint32_t ChecksumSize; // Checksum information
uint32_t Checksum[32]; // Up to 128-bytes (32 x 4) of checksum
uint32_t ImageVariant; // Commonly 1
uint64_t SectorCount; // Size of DMG when expanded, in sectors
uint32_t reserved2; // 0
uint32_t reserved3; // 0
uint32_t reserved4; // 0
} __attribute__((__packed__)) UDIFResourceFile;
Рассмотрим следующие строки кода в качестве примера чтения байтов.
public static uint ToUInt32BigEndian(byte[] buffer, int offset)
{
uint val = (uint)(((buffer[offset + 0] << 24) & 0xFF000000U) | ((buffer[offset + 1] << 16) & 0x00FF0000U)
| ((buffer[offset + 2] << 8) & 0x0000FF00U) | ((buffer[offset + 3] << 0) & 0x000000FFU));
return val;
}
public static ulong ToUInt64BigEndian(byte[] buffer, int offset)
{
return ((ulong)ToUInt32BigEndian(buffer, offset + 0) << 32) | ToUInt32BigEndian(buffer, offset + 4);
}
internal class UdifChecksum : IByteArraySerializable
{
public uint ChecksumSize;
public byte[] Data;
public uint Type;
public int Size
{
get { return 136; }
}
public int ReadFrom(byte[] buffer, int offset)
{
Type = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0);
ChecksumSize = EndianUtilities.ToUInt32BigEndian(buffer, offset + 4);
Data = EndianUtilities.ToByteArray(buffer, offset + 8, 128);
return 136;
}
public void WriteTo(byte[] buffer, int offset)
{
throw new NotImplementedException();
}
}
Здесь вы можете увидеть пример чтения всех свойств из заголовка файла
internal class UdifResourceFile : IByteArraySerializable
{
public UdifChecksum DataForkChecksum;
public ulong DataForkLength;
public ulong DataForkOffset;
public uint Flags;
public uint HeaderSize;
public uint ImageVariant;
public UdifChecksum MasterChecksum;
public ulong RsrcForkLength;
public ulong RsrcForkOffset;
public ulong RunningDataForkOffset;
public long SectorCount;
public uint SegmentCount;
public Guid SegmentGuid;
public uint SegmentNumber;
public uint Signature;
public uint Version;
public ulong XmlLength;
public ulong XmlOffset;
public bool SignatureValid
{
get { return Signature == 0x6B6F6C79; }
}
public int Size
{
get { return 512; }
}
public int ReadFrom(byte[] buffer, int offset)
{
Signature = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0);
Version = EndianUtilities.ToUInt32BigEndian(buffer, offset + 4);
HeaderSize = EndianUtilities.ToUInt32BigEndian(buffer, offset + 8);
Flags = EndianUtilities.ToUInt32BigEndian(buffer, offset + 12);
RunningDataForkOffset = EndianUtilities.ToUInt64BigEndian(buffer, offset + 16);
DataForkOffset = EndianUtilities.ToUInt64BigEndian(buffer, offset + 24);
DataForkLength = EndianUtilities.ToUInt64BigEndian(buffer, offset + 32);
RsrcForkOffset = EndianUtilities.ToUInt64BigEndian(buffer, offset + 40);
RsrcForkLength = EndianUtilities.ToUInt64BigEndian(buffer, offset + 48);
SegmentNumber = EndianUtilities.ToUInt32BigEndian(buffer, offset + 56);
SegmentCount = EndianUtilities.ToUInt32BigEndian(buffer, offset + 60);
SegmentGuid = EndianUtilities.ToGuidBigEndian(buffer, offset + 64);
DataForkChecksum = EndianUtilities.ToStruct<UdifChecksum>(buffer, offset + 80);
XmlOffset = EndianUtilities.ToUInt64BigEndian(buffer, offset + 216);
XmlLength = EndianUtilities.ToUInt64BigEndian(buffer, offset + 224);
MasterChecksum = EndianUtilities.ToStruct<UdifChecksum>(buffer, offset + 352);
ImageVariant = EndianUtilities.ToUInt32BigEndian(buffer, offset + 488);
SectorCount = EndianUtilities.ToInt64BigEndian(buffer, offset + 492);
return Size;
}
public void WriteTo(byte[] buffer, int offset)
{
throw new NotImplementedException();
}
}
Подробнее о коде здесь ( https://github.com/DiscUtils/DiscUtils )
Если вы читаете XML-определение файла (проверьте XMLOffset и XMLLength), вы сможете определить структуру файла и извлечь файлы (и их свойства).
The XML Property list (which is uncompressed and easily viewable by seeking to the DOCTYPE declaration using more(1) or using tail(1)) is technically the resource fork of the DMG. The property list file contains, at a minimum, a "blkx" key, though it may contain other key/values, most commonly "plst", and sometimes a service level agreement (SLA) which will be displayed by the OS (specifically, /System/Library/PrivateFrameworks/DiskImages.framework/Versions/A/Resources/DiskImages UI Agent.app/Contents/MacOS/DiskImages UI Agent) as a pre-requisite to attaching the DMG*. Due to XML parser restrictions, data in the property list is 7-bit. This forces all binary (8-bit) data to be encoded using Base-64 encoding (a wiser choice would have been using CDATA blocks)
Поскольку MAC подписывает файлы (насколько я помню), подпись можно было брать оттуда. Контрольная сумма на верхнем уровне защищает от модификации пакета и должна быть сделана с тем же сертификатом, который используется для подписи.
Надеюсь это поможет.
Используйте OpenSsl, Bouncy Castle или System.Security.Cryptography (контрольная сумма sha256 или аналогичная) для проверки контрольной суммы с использованием C# в вашей системе. Если вы являетесь поставщиком, вы можете сначала создать хеш и опубликовать его на странице загрузки для файлов.msi и.dmg. Я пробовал это раньше, и это работает хорошо. Однако в данный момент у меня нет кода, который можно прикрепить к этому ответу, используйте функцию ComputeHash в System.Security.Cryptography.
Если у вас нет прямого доступа к файлам, вы можете скачать.dmg и создать из него хеш, используя C#. Хэш, который будет корректно проверен при проверке, если не манипулировать этим. Создание хеша из всех байтов файла является гораздо более безопасным, чем доверять внедренным данным, которые можно заменить и подписать, чтобы они казались действительными, если только вы не перепроверите все с создателями.