简体   繁体   English

使用Swift生成base64 url​​编码的X.509格式2048位RSA公钥?

[英]Generate base64 url-encoded X.509 format 2048-bit RSA public key with Swift?

Working in Apple Swift for iOS. 在Apple Swift for iOS中工作。 I have to generate this for the backend as it's a secure app. 我必须为后端生成这个,因为它是一个安全的应用程序。

I'm new to security and certificates and have been searching for a day now with no results. 我是安全和证书的新手,现在一直在寻找一天没有结果。

How can I generate base64 url-encoded X.509 format 2048-bit RSA public key with swift? 如何使用swift生成base64 url​​编码的X.509格式2048位RSA公钥?

Any help is highly appreciated. 任何帮助都非常感谢。

There's a library for handling public-private key pairs in Swift that I recently created called Heimdall , which allows you to easily export the X.509 formatted Base64 string of the public key. 在我最近创建的一个名为Heimdall的 Swift中有一个用于处理公钥 - 私钥对的库,它允许您轻松导出公钥的X.509格式化Base64字符串。

To comply with SO rules, I will also include the implementation in this answer (so that it is self-explanatory) 为了遵守SO规则,我还将在此答案中包含实现(以便它不言自明)

public func X509PublicKey() -> NSString? {
    // Fetch the key, so that key = NSData of the public key
    let result = NSMutableData()

    let encodingLength: Int = {
        if key.length + 1 < 128 {
            return 1
        } else {
            return ((key.length + 1) / 256) + 2
        }
    }()

    let OID: [CUnsignedChar] = [0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
        0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00]

    var builder: [CUnsignedChar] = []

    // ASN.1 SEQUENCE
    builder.append(0x30)

    // Overall size, made of OID + bitstring encoding + actual key
    let size = OID.count + 2 + encodingLength + key.length
    let encodedSize = encodeLength(size)
    builder.extend(encodedSize)
    result.appendBytes(builder, length: builder.count)
    result.appendBytes(OID, length: OID.count)
    builder.removeAll(keepCapacity: false)

    builder.append(0x03)
    builder.extend(encodeLength(key.length + 1))
    builder.append(0x00)
    result.appendBytes(builder, length: builder.count)

    // Actual key bytes
    result.appendData(key)

    // Convert to Base64 and make safe for URLs
    var string = result.base64EncodedStringWithOptions(.allZeros)
    string = string.stringByReplacingOccurrencesOfString("/", withString: "_")
    string = string.stringByReplacingOccurrencesOfString("+", withString: "-")

    return string
}

public func encodeLength(length: Int) -> [CUnsignedChar] {
    if length < 128 {
        return [CUnsignedChar(length)];
    }

    var i = (length / 256) + 1
    var len = length
    var result: [CUnsignedChar] = [CUnsignedChar(i + 0x80)]

    for (var j = 0; j < i; j++) {
        result.insert(CUnsignedChar(len & 0xFF), atIndex: 1)
        len = len >> 8
    }

    return result
}

I have removed the data fetching code, refer to either source of Heimdall or Jeff Hay's answer 我已经删除了数据提取代码,请参考Heimdall的来源或Jeff Hay的答案

If the public key is already in your keychain, you can look up the public key and return the data as base64 with something similar to the following: 如果公钥已经在您的钥匙串中,您可以查找公钥并将数据作为base64返回,其内容类似于以下内容:

// Create dictionary to specify attributes for the key we're
// searching for.  Swift will automatically bridge native values 
// to to right types for the SecItemCopyMatching() call.
var queryAttrs = [NSString:AnyObject]() 
queryAttrs[kSecClass] = kSecClassKey 
queryAttrs[kSecAttrApplicationTag] = publicKeyTag
queryAttrs[kSecAttrKeyType] = kSecAttrKeyTypeRSA
queryAttrs[kSecReturnData] = true

var publicKeyBits = Unmanaged<AnyObject>?()
SecItemCopyMatching(queryAttrs, &publicKeyBits)

// Work around a compiler bug with Unmanaged<AnyObject> types
//  the following two lines should simply be 
//  let publicKeyData : NSData = publicKeyRef!.takeRetainedValue() as NSData
let opaqueBits = publicKeyBits?.toOpaque() 
let publicKeyData = Unmanaged<NSData>.fromOpaque(opaqueBits).takeUnretainedValue()

let publicKeyBase64 = publicKeyData.base64EncodedData(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)

If you need to generate the keypair and store it in the keychain, use something along these lines: 如果您需要生成密钥对并将其存储在钥匙串中,请按以下方式使用:

// Create dictionaries to specify key attributes.  Swift will
// automatically bridge native values to to right types for the
// SecKeyGeneratePair() call.
var pairAttrs = [NSString:AnyObject]()
var privateAttrs = [NSString:AnyObject]()
var publicAttrs = [NSString:AnyObject]()

privateAttrs[kSecAttrIsPermanent] = true
privateAttrs[kSecAttrApplicationTag] = privateKeyTag

publicAttrs[kSecAttrIsPermanent] = true
publicAttrs[kSecAttrApplicationTag] = publicKeyTag

pairAttrs[kSecAttrKeyType] = kSecAttrKeyTypeRSA
pairAttrs[kSecAttrKeySizeInBits] = 2048
pairAttrs[(kSecPrivateKeyAttrs.takeUnretainedValue() as! String)] = privateAttrs
pairAttrs[(kSecPublicKeyAttrs.takeUnretainedValue() as! String)] = publicAttrs

var publicKeyPtr = Unmanaged<SecKey>?()
var privateKeyPtr = Unmanaged<SecKey>?()

let status = SecKeyGeneratePair(pairAttrs, &publicKeyPtr, &privateKeyPtr)

Note: publicKeyTag and privateKeyTag are strings used to identify the key in the keystore. 注意: publicKeyTagprivateKeyTag是用于标识密钥库中密钥的字符串。 Apple recommends reverse-dns notation (com.company.key.xxx), but as long as they are unique, all should be good. Apple建议使用reverse-dns表示法(com.company.key.xxx),但只要它们是唯一的,一切都应该是好的。

Similar question with answer: Generate key pair on iphone and print to log as NSString 回答类似的问题: 在iphone上生成密钥对并打印为NSString

Although the answer there is in Objective-C, Apple reference shows that the functions (especially the most important one, SecKeyGeneratePair ) can also be called directly from Swift (as long as you can do the type conversions from all those UnsafeMutablePointers etc). 尽管在Objective-C中有答案, Apple参考文献表明函数(尤其是最重要的函数SecKeyGeneratePair )也可以直接从Swift调用(只要你可以从所有那些UnsafeMutablePointers等进行类型转换)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM