Golang如何将X509证书保存到Mac钥匙串

Golang如何将X509证书保存到Mac钥匙串 我正在寻找用Go语言实现的示例代码,用于将X509证书保存到Mac钥匙串中。有什么建议吗?

1 回复

更多关于Golang如何将X509证书保存到Mac钥匙串的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go语言中,可以通过调用macOS的Security Framework API来实现将X509证书保存到钥匙串。以下是一个完整的示例代码:

package main

import (
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "log"
    "os"
    "unsafe"

    "github.com/keybase/go-keychain"
)

// 导入必要的C头文件
/*
#cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework Security -framework Foundation
#import <Security/Security.h>
#import <Foundation/Foundation.h>

// 辅助函数:将Go字节数组转换为NSData
NSData* bytesToNSData(const void* bytes, size_t length) {
    return [NSData dataWithBytes:bytes length:length];
}

// 保存证书到钥匙串
int saveCertificateToKeychain(const void* certData, size_t certLength, 
                             const char* label, const char* commonName) {
    @autoreleasepool {
        // 创建证书数据
        NSData *certificateData = bytesToNSData(certData, certLength);
        
        // 创建查询字典
        NSMutableDictionary *query = [NSMutableDictionary dictionary];
        
        // 设置证书类型
        [query setObject:(__bridge id)kSecClassCertificate forKey:(__bridge id)kSecClass];
        
        // 设置证书数据
        [query setObject:certificateData forKey:(__bridge id)kSecValueData];
        
        // 设置标签
        NSString *labelStr = [NSString stringWithUTF8String:label];
        [query setObject:labelStr forKey:(__bridge id)kSecAttrLabel];
        
        // 设置通用名称
        NSString *commonNameStr = [NSString stringWithUTF8String:commonName];
        [query setObject:commonNameStr forKey:(__bridge id)kSecAttrSubject];
        
        // 设置可访问性
        [query setObject:(__bridge id)kSecAttrAccessibleWhenUnlocked 
                  forKey:(__bridge id)kSecAttrAccessible];
        
        // 尝试添加证书到钥匙串
        OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
        
        if (status == errSecSuccess) {
            NSLog(@"证书已成功保存到钥匙串");
            return 0;
        } else if (status == errSecDuplicateItem) {
            NSLog(@"证书已存在于钥匙串中");
            return 1;
        } else {
            NSLog(@"保存证书失败,错误码: %d", (int)status);
            return -1;
        }
    }
}
*/
import "C"

// SaveCertificateToKeychain 将X509证书保存到Mac钥匙串
func SaveCertificateToKeychain(cert *x509.Certificate, label string) error {
    // 将证书编码为DER格式
    certData := cert.Raw
    
    // 调用C函数保存证书
    result := C.saveCertificateToKeychain(
        unsafe.Pointer(&certData[0]),
        C.size_t(len(certData)),
        C.CString(label),
        C.CString(cert.Subject.CommonName),
    )
    
    if result == 0 {
        return nil
    } else if result == 1 {
        return fmt.Errorf("证书已存在")
    }
    return fmt.Errorf("保存证书失败")
}

// 使用keychain库的替代方案
func SaveCertificateUsingKeychainLib(cert *x509.Certificate, label string) error {
    // 将证书编码为PEM格式
    certPEM := pem.EncodeToMemory(&pem.Block{
        Type:  "CERTIFICATE",
        Bytes: cert.Raw,
    })
    
    // 创建钥匙串项
    item := keychain.NewItem()
    item.SetSecClass(keychain.SecClassCertificate)
    item.SetLabel(label)
    item.SetData(certPEM)
    item.SetAccessible(keychain.AccessibleWhenUnlocked)
    
    // 添加证书到钥匙串
    err := keychain.AddItem(item)
    if err != nil {
        if err.Error() == "The specified item already exists in the keychain." {
            return fmt.Errorf("证书已存在")
        }
        return err
    }
    
    return nil
}

func main() {
    // 示例:从文件加载证书
    certData, err := os.ReadFile("certificate.pem")
    if err != nil {
        log.Fatal("无法读取证书文件:", err)
    }
    
    // 解码PEM证书
    block, _ := pem.Decode(certData)
    if block == nil {
        log.Fatal("无效的PEM格式")
    }
    
    // 解析X509证书
    cert, err := x509.ParseCertificate(block.Bytes)
    if err != nil {
        log.Fatal("解析证书失败:", err)
    }
    
    // 方法1:使用C绑定保存证书
    fmt.Println("使用方法1保存证书...")
    err = SaveCertificateToKeychain(cert, "MyGoCertificate")
    if err != nil {
        fmt.Println("方法1错误:", err)
    } else {
        fmt.Println("方法1成功")
    }
    
    // 方法2:使用go-keychain库保存证书
    fmt.Println("\n使用方法2保存证书...")
    err = SaveCertificateUsingKeychainLib(cert, "MyGoCertificate")
    if err != nil {
        fmt.Println("方法2错误:", err)
    } else {
        fmt.Println("方法2成功")
    }
}

这个示例提供了两种实现方式:

  1. 直接使用C绑定:通过cgo调用macOS Security Framework API
  2. 使用go-keychain库:第三方库提供了更简洁的接口

使用前需要安装依赖:

go get github.com/keybase/go-keychain

证书文件需要是PEM格式。如果证书已经是DER格式,可以直接使用x509.ParseCertificate()解析。

回到顶部