You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用CryptoKit在Swift中对哈希值进行签名

将Node.js RSA签名逻辑移植到Swift

一、使用CryptoKit实现

CryptoKit本身不直接支持解析带密码的PEM私钥,需要借助Security框架先将PEM导入为SecKey,再转换为CryptoKit兼容的密钥类型。

1. 读取并解析带密码的PEM私钥

import CryptoKit
import Security

func loadPrivateKey(from pemPath: String, passphrase: String) throws -> RSA.Signing.PrivateKey {
    // 读取PEM文件内容
    guard let pemData = try? Data(contentsOf: URL(fileURLWithPath: pemPath)) else {
        throw NSError(domain: "KeyError", code: 1, userInfo: [NSLocalizedDescriptionKey: "无法读取私钥文件"])
    }
    
    // 配置密钥导入参数
    let options: [CFString: Any] = [
        kSecAttrKeyType: kSecAttrKeyTypeRSA,
        kSecAttrKeyClass: kSecAttrKeyClassPrivate,
        kSecAttrKeySizeInBits: 2048,
        kSecReturnPersistentRef: false,
        kSecReturnRef: true,
        kSecAttrPEMPassword: passphrase.data(using: .utf8)!
    ]
    
    // 导入PEM私钥为SecKey
    var secKey: SecKey?
    let status = SecKeyCreateWithData(pemData as CFData, options as CFDictionary, &secKey)
    
    guard status == errSecSuccess, let key = secKey else {
        throw NSError(domain: "KeyError", code: 2, userInfo: [NSLocalizedDescriptionKey: "私钥导入失败,状态码:\(status)"])
    }
    
    // 将SecKey转换为CryptoKit的RSA私钥
    guard let cryptoKitKey = RSA.Signing.PrivateKey(secKey: key) else {
        throw NSError(domain: "KeyError", code: 3, userInfo: [NSLocalizedDescriptionKey: "无法转换为CryptoKit私钥"])
    }
    
    return cryptoKitKey
}

2. 执行签名(匹配Node.js逻辑)

Node.js的crypto.createSign('sha256')默认对输入数据做SHA256哈希后,用PKCS#1 v1.5填充签名,CryptoKit可直接通过指定算法完成对应操作:

func signHashString(_ hashString: String, with privateKey: RSA.Signing.PrivateKey) throws -> String {
    // 将目标字符串转为Data
    guard let data = hashString.data(using: .utf8) else {
        throw NSError(domain: "DataError", code: 1, userInfo: [NSLocalizedDescriptionKey: "字符串转Data失败"])
    }
    
    // 执行RSA-SHA256签名
    let signature = try privateKey.signature(for: data, signatureAlgorithm: .rsaSignatureMessagePKCS1v15SHA256)
    
    // 转换为Base64字符串
    return signature.base64EncodedString()
}

使用示例

do {
    let privateKey = try loadPrivateKey(from: "/path/to/WM_IO_my_rsa_key_pair", passphrase: "你的密码")
    let hashString = "需要签名的字符串"
    let signatureBase64 = try signHashString(hashString, with: privateKey)
    print("签名结果:\(signatureBase64)")
} catch {
    print("签名失败:\(error.localizedDescription)")
}

二、使用CommonCrypto/Security框架实现

若不想依赖CryptoKit,可直接用Security框架完成签名:

1. 加载带密码的PEM私钥

import Security

func loadSecPrivateKey(from pemPath: String, passphrase: String) throws -> SecKey {
    guard let pemData = try? Data(contentsOf: URL(fileURLWithPath: pemPath)) else {
        throw NSError(domain: "KeyError", code: 1, userInfo: [NSLocalizedDescriptionKey: "无法读取私钥文件"])
    }
    
    let options: [CFString: Any] = [
        kSecAttrKeyType: kSecAttrKeyTypeRSA,
        kSecAttrKeyClass: kSecAttrKeyClassPrivate,
        kSecAttrKeySizeInBits: 2048,
        kSecReturnPersistentRef: false,
        kSecReturnRef: true,
        kSecAttrPEMPassword: passphrase.data(using: .utf8)!
    ]
    
    var secKey: SecKey?
    let status = SecKeyCreateWithData(pemData as CFData, options as CFDictionary, &secKey)
    
    guard status == errSecSuccess, let key = secKey else {
        throw NSError(domain: "KeyError", code: 2, userInfo: [NSLocalizedDescriptionKey: "私钥导入失败,状态码:\(status)"])
    }
    
    return key
}

2. 执行签名

func signWithSecKey(_ hashString: String, with privateKey: SecKey) throws -> String {
    guard let data = hashString.data(using: .utf8) else {
        throw NSError(domain: "DataError", code: 1, userInfo: [NSLocalizedDescriptionKey: "字符串转Data失败"])
    }
    
    // 确认私钥支持指定签名算法
    let algorithm = SecKeyAlgorithm.rsaSignatureMessagePKCS1v15SHA256
    guard SecKeyIsAlgorithmSupported(privateKey, .sign, algorithm) else {
        throw NSError(domain: "SignError", code: 2, userInfo: [NSLocalizedDescriptionKey: "私钥不支持指定签名算法"])
    }
    
    // 执行签名
    var error: Unmanaged<CFError>?
    guard let signatureData = SecKeyCreateSignature(privateKey, algorithm, data as CFData, &error) as Data? else {
        throw error!.takeRetainedValue() as Error
    }
    
    // 转换为Base64字符串
    return signatureData.base64EncodedString()
}

使用示例

do {
    let privateKey = try loadSecPrivateKey(from: "/path/to/WM_IO_my_rsa_key_pair", passphrase: "你的密码")
    let hashString = "需要签名的字符串"
    let signatureBase64 = try signWithSecKey(hashString, with: privateKey)
    print("签名结果:\(signatureBase64)")
} catch {
    print("签名失败:\(error.localizedDescription)")
}

关键注意事项

  • Node.js默认使用PKCS#1 v1.5填充方式,Swift中必须对应使用.rsaSignatureMessagePKCS1v15SHA256算法,才能保证签名结果一致。
  • 若私钥为PKCS#8格式,上述代码同样适用,Security框架会自动识别。
  • 处理文件路径时,需确保App拥有对应文件的读取权限(比如iOS中需将文件加入项目或使用Documents目录)。

内容的提问来源于stack exchange,提问作者Berry Blue

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 18:30:59