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

iOS中用Objective-C实现PEM公钥转ssh-rsa公钥(仿ssh-keygen逻辑)

解决iOS上PKCS#8 PEM公钥转ssh-rsa格式的问题

我明白你要实现的是终端命令ssh-keygen -i -f publicKey.pem -m PKCS8 -i > pubkey-ssh.pub的功能,在iOS应用里用Objective-C完成PKCS#8格式PEM公钥到OpenSSH ssh-rsa格式的转换。你的现有代码没成功,主要是因为没有按照OpenSSH的特定格式组装数据,而且解析PKCS#8公钥的方式也不对,下面给你完整的可行方案:

先理解转换的核心逻辑

终端的ssh-keygen命令做了这几件事:

  1. 读取PKCS#8格式的PEM公钥,解码成DER二进制数据
  2. 解析DER数据得到RSA公钥的模(n)和指数(e)
  3. 按照OpenSSH的规范组装二进制数据:
    • 先写入4字节大端的字符串长度,再写入ssh-rsa字符串本身
    • 接着写入4字节大端的指数长度,再写入指数的二进制数据(去掉前导零)
    • 最后写入4字节大端的模长度,再写入模的二进制数据(去掉前导零)
  4. 把组装好的二进制数据做Base64编码,前面加上ssh-rsa 前缀,得到最终的公钥字符串

完整的Objective-C实现代码

首先确保你已经集成了OpenSSL库(可以用CocoaPods引入pod 'OpenSSL-Universal',或者手动编译集成),然后用下面的代码:

#import <openssl/rsa.h>
#import <openssl/pem.h>
#import <openssl/bio.h>
#import <openssl/x509.h>

- (NSString *)convertPKCS8PEMToSSHRSA:(NSString *)pkcs8PEM {
    // 1. 预处理PEM字符串:去掉头、尾和换行
    NSString *cleanedPEM = [pkcs8PEM stringByReplacingOccurrencesOfString:@"-----BEGIN PUBLIC KEY-----" withString:@""];
    cleanedPEM = [cleanedPEM stringByReplacingOccurrencesOfString:@"-----END PUBLIC KEY-----" withString:@""];
    cleanedPEM = [cleanedPEM stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    
    // 2. Base64解码得到DER格式的PKCS#8公钥数据
    NSData *derData = [[NSData alloc] initWithBase64EncodedString:cleanedPEM options:NSDataBase64DecodingIgnoreUnknownCharacters];
    if (!derData) {
        NSLog(@"Invalid PEM data");
        return nil;
    }
    
    // 3. 解析DER数据得到RSA公钥结构体
    const unsigned char *derBytes = (const unsigned char *)derData.bytes;
    RSA *rsa = NULL;
    EVP_PKEY *pkey = d2i_PUBKEY(NULL, &derBytes, (long)derData.length);
    if (pkey) {
        if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA) {
            rsa = EVP_PKEY_get1_RSA(pkey);
        }
        EVP_PKEY_free(pkey);
    }
    if (!rsa) {
        NSLog(@"Failed to parse RSA public key");
        return nil;
    }
    
    // 4. 组装OpenSSH格式的二进制数据
    NSMutableData *sshData = [NSMutableData data];
    
    // 写入"ssh-rsa"字符串的长度和内容
    NSString *sshHeader = @"ssh-rsa";
    uint32_t headerLen = htonl((uint32_t)sshHeader.length);
    [sshData appendBytes:&headerLen length:sizeof(headerLen)];
    [sshData appendData:[sshHeader dataUsingEncoding:NSASCIIStringEncoding]];
    
    // 写入指数e的长度和内容(去掉前导零)
    BIGNUM *e = rsa->e;
    int eLen = BN_num_bytes(e);
    uint32_t eLenBigEndian = htonl((uint32_t)eLen);
    [sshData appendBytes:&eLenBigEndian length:sizeof(eLenBigEndian)];
    
    unsigned char *eBytes = malloc(eLen);
    BN_bn2bin(e, eBytes);
    [sshData appendBytes:eBytes length:eLen];
    free(eBytes);
    
    // 写入模n的长度和内容(去掉前导零)
    BIGNUM *n = rsa->n;
    int nLen = BN_num_bytes(n);
    uint32_t nLenBigEndian = htonl((uint32_t)nLen);
    [sshData appendBytes:&nLenBigEndian length:sizeof(nLenBigEndian)];
    
    unsigned char *nBytes = malloc(nLen);
    BN_bn2bin(n, nBytes);
    [sshData appendBytes:nBytes length:nLen];
    free(nBytes);
    
    // 5. Base64编码并加上前缀
    NSString *base64Str = [sshData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
    NSString *sshRsaKey = [NSString stringWithFormat:@"ssh-rsa %@", base64Str];
    
    // 释放资源
    RSA_free(rsa);
    
    return sshRsaKey;
}

代码关键点说明

  1. PEM预处理:必须去掉PEM的头尾标记和多余的换行,才能正确Base64解码。
  2. 解析PKCS#8公钥:用d2i_PUBKEY而不是d2i_RSAPublicKey,因为PKCS#8是通用的公钥容器,d2i_PUBKEY能正确解析它,之后再提取RSA结构体。
  3. OpenSSH格式组装:严格按照大端字节序写入长度字段,并且确保模和指数的二进制数据没有前导零(BN_num_bytes和BN_bn2bin会自动处理)。
  4. 内存管理:所有OpenSSL分配的结构体(EVP_PKEY、RSA)和动态内存(eBytes、nBytes)都要手动释放,避免内存泄漏。

测试方法

把你的PKCS#8格式PEM公钥传入这个方法,得到的字符串应该和ssh-keygen命令生成的完全一致。比如:

NSString *pkcs8PEM = @"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuqNpHztewgvIiPGUHb/tWozPQCJ5uEUepNZgbhh1376vO2PZcvTYo80MVppFLzCpvEVYOLPtg4/rztT9OkN/5U4AxjFB2oz85Y3Fb6mJCVN4cQ2x+IfhUYEfy3/+uum9jO9Kh0sEiAFt5Cvok3nT8YpjjmXgDiEmudMKhP0+uocuZcWwBTLeeDkgoMYlEl6p9wPntsz0cIoU3KZNXmWRNS1b8NPYDDjOHDYuF3N8ZCWS/9SzgN2qGY3MZCiWtX3VRWeGiUbWcmGAlz0xrVSXC4iZWsQ+CSqfw4KMvTfPJrm2C7azcsrGuYtELPEqc/42UPuQbuO6M3l+ZABjw1KP\n-----END PUBLIC KEY-----";
NSString *sshRsaKey = [self convertPKCS8PEMToSSHRSA:pkcs8PEM];
NSLog(@"%@", sshRsaKey);

输出应该就是你期望的ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6qU2kfO17CC8iI8ZQdv+1ajM9AInm4RR6k1mBuGHXfvq87Y9ly/NihzQxWmkUvMKm8RVg4s+2Dj+vO1P06Q3/lTgDGMUHajPzljcVvqYkJU3hxDbH4h+FRgR/Lf/6q6b2M70qHSwSIAW3kK+iTedPximOOZeAOISa50wqE/Tr6hy5lxbAFMt54OSCgxiUSXql3C+e2zPRwihTcpk1eZZE1LVvw09gOOM4cNi4Xc3xkJZL/1LOA3aoZdjxkCIa1fdVFZ4aJRtZyoYAXPTGtVJcLiJlaxD4JKp/Dgoy9N88nuyYLtrNyuxq5i0Qs8Spx/jZQ+5Bu47ozeX5kAGPDUo9这样的格式。

内容的提问来源于stack exchange,提问作者Sandeep Kumar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:49:25