为 Mac 生成特定于机器的密钥

Posted

技术标签:

【中文标题】为 Mac 生成特定于机器的密钥【英文标题】:Generate machine-specific key for Mac 【发布时间】:2012-02-25 16:25:23 【问题描述】:

在 Windows 上,我们会生成一个特定于 PC 的唯一密钥,用于将许可证绑定到 PC。这是一个使用 wxWidgets 的 C++ 应用程序,理论上是跨平台兼容的,但在 Mac 端没有维护。我们使用一些特定于 Win32 的代码来生成密钥……我如何在 Mac 上做类似的事情?

【问题讨论】:

看看this question关于同样的问题。 【参考方案1】:

深入研究 whitelionV 和 blahdiblah 的 asnwers,我发现了this useful page:

Accessing the system serial number programmatically

#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>

// Returns the serial number as a CFString. 
// It is the caller's responsibility to release the returned CFString when done with it.
void CopySerialNumber(CFStringRef *serialNumber)

    if (serialNumber != NULL) 
        *serialNumber = NULL;

        io_service_t    platformExpert = ioserviceGetMatchingService(kIOMasterPortDefault,
                                                                         IOServiceMatching("IOPlatformExpertDevice"));

        if (platformExpert) 
            CFTypeRef serialNumberAsCFString = 
                IORegistryEntryCreateCFProperty(platformExpert,
                                                CFSTR(kIOPlatformSerialNumberKey),
                                                kCFAllocatorDefault, 0);
            if (serialNumberAsCFString) 
                *serialNumber = serialNumberAsCFString;
            

            IOObjectRelease(platformExpert);
        
    

Accessing the built-in MAC address programmatically

#include <stdio.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/network/IOEthernetInterface.h>
#include <IOKit/network/IONetworkInterface.h>
#include <IOKit/network/IOEthernetController.h>

static kern_return_t FindEthernetInterfaces(io_iterator_t *matchingServices);
static kern_return_t GetMACAddress(io_iterator_t intfIterator, UInt8 *MACAddress, UInt8 bufferSize);

static kern_return_t FindEthernetInterfaces(io_iterator_t *matchingServices)

    kern_return_t           kernResult; 
    CFMutableDictionaryRef  matchingDict;
    CFMutableDictionaryRef  propertyMatchDict;

    matchingDict = IOServiceMatching(kIOEthernetInterfaceClass);

    if (NULL == matchingDict) 
        printf("IOServiceMatching returned a NULL dictionary.\n");
    
    else 
        propertyMatchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
                                                      &kCFTypeDictionaryKeyCallBacks,
                                                      &kCFTypeDictionaryValueCallBacks);

        if (NULL == propertyMatchDict) 
            printf("CFDictionaryCreateMutable returned a NULL dictionary.\n");
        
        else 
            CFDictionarySetValue(matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict);
            CFRelease(propertyMatchDict);
        
    

    kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, matchingServices);    
    if (KERN_SUCCESS != kernResult) 
        printf("IOServiceGetMatchingServices returned 0x%08x\n", kernResult);
    

    return kernResult;


static kern_return_t GetMACAddress(io_iterator_t intfIterator, UInt8 *MACAddress, UInt8 bufferSize)

    io_object_t     intfService;
    io_object_t     controllerService;
    kern_return_t   kernResult = KERN_FAILURE;

    if (bufferSize < kIOEthernetAddressSize) 
        return kernResult;
    

    bzero(MACAddress, bufferSize);

    while ((intfService = IOIteratorNext(intfIterator)))
    
        CFTypeRef   MACAddressAsCFData;        
        kernResult = IORegistryEntryGetParentEntry(intfService,
                                                   kIOServicePlane,
                                                   &controllerService);

        if (KERN_SUCCESS != kernResult) 
            printf("IORegistryEntryGetParentEntry returned 0x%08x\n", kernResult);
        
        else 
            MACAddressAsCFData = IORegistryEntryCreateCFProperty(controllerService,
                                                                 CFSTR(kIOMACAddress),
                                                                 kCFAllocatorDefault,
                                                                 0);
            if (MACAddressAsCFData) 
                CFShow(MACAddressAsCFData); // for display purposes only; output goes to stderr

                CFDataGetBytes(MACAddressAsCFData, CFRangeMake(0, kIOEthernetAddressSize), MACAddress);
                CFRelease(MACAddressAsCFData);
            

            (void) IOObjectRelease(controllerService);
        

        (void) IOObjectRelease(intfService);
    

    return kernResult;


int main(int argc, char *argv[])

    kern_return_t   kernResult = KERN_SUCCESS;
    io_iterator_t   intfIterator;
    UInt8           MACAddress[kIOEthernetAddressSize];

    kernResult = FindEthernetInterfaces(&intfIterator);

    if (KERN_SUCCESS != kernResult) 
        printf("FindEthernetInterfaces returned 0x%08x\n", kernResult);
    
    else 
        kernResult = GetMACAddress(intfIterator, MACAddress, sizeof(MACAddress));

        if (KERN_SUCCESS != kernResult) 
            printf("GetMACAddress returned 0x%08x\n", kernResult);
        
        else 
            printf("This system's built-in MAC address is %02x:%02x:%02x:%02x:%02x:%02x.\n",
                    MACAddress[0], MACAddress[1], MACAddress[2], MACAddress[3], MACAddress[4], MACAddress[5]);
        
    

    (void) IOObjectRelease(intfIterator);   // Release the iterator.

    return kernResult;

虽然 MAC 从表面上看可能更可预测,但他们警告说:

Netbooting 给具有多个内置功能的系统带来了麻烦 以太网端口。这些系统上的主要以太网端口是 连接到 NetBoot 服务器。这意味着搜索 主端口可以返回任一内置 MAC 地址 取决于用于网络引导的端口。请注意,“内置” 不包括位于扩展卡上的以太网端口。

我担心这可能意味着您不会总是得到相同的值?

【讨论】:

【参考方案2】:

您可以拨打system_profiler 并查找“序列号”

/usr/sbin/system_profiler | grep "Serial Number (system)"

很可能有一种程序化的方式来获取相同的信息,但我不知道它。

【讨论】:

【参考方案3】:

要唯一标识任何机器,您可以尝试使用 MAC 地址。这个过程,虽然不是微不足道的,但它非常简单。有很多跨平台的开源库。

其实你可以试试这个 Apple dev example

【讨论】:

我听说当您在电源/电池电源之间切换或进入低功耗状态时,可见的 MAC 地址会发生变化? 每个网络设备都有一个识别MAC地址,只要你识别每个并且每次获取相同的MAC地址就不会出现问题(即总是抢eth0) 即使设备关闭?

以上是关于为 Mac 生成特定于机器的密钥的主要内容,如果未能解决你的问题,请参考以下文章

编译器是否特定于操作系统?

需要生成特定于设备 ID 的访问密钥(Android 应用程序的 android_id)

特定版本为 False 时的特定于版本的程序集引用

KMS 加密 - 本地开发

为啥 Windows Vista 64 位默认使用 32 位 cmd.exe? (特定于机器)

如何完全删除 QLayout 的边距(特定于 Mac OS)