将XML Dsig格式转换为DER ASN.1公钥

时间:2011-03-22 19:58:29

标签: ios rsa asn.1 xml-signature der

我正在开发一个iPhone应用程序,它以以下形式从ASP.NET Web服务检索RSA公钥:

<RSAKeyValue>
  <Modulus>qdd0paiiBJ+xYaN4TKDdbEzrJJw9xlbRAltb5OPdegjLoW60yOjL/sni52WVsGC9QxpNitZR33dnUscmI0cTJoxkXypPjbD94UpH+p4el2tuKBypHlE7bERApuUp55y8BiRkbQNFH8smZFWDwtIc/PsJryeGf8fAryel8c5V3PU=</Modulus>
  <Exponent>AQAB</Exponent>
</RSAKeyValue>

然后我需要将此响应转换为适当格式的NSData *(来自一些强烈的谷歌搜索,最可能是'ASN.1 DER'二进制格式。我已经有代码来转换这两个部分他们的Base64表示原始的二进制值,但我不能为我的生活找出一个合理的方法来创建一个二进制密钥。

等待密钥的代码是Apple的-addPeerPublicKey:(NSString *) keyBits:(NSData *)示例项目{代码hereSecKeyWrapper类的CryptoExercise方法。

我非常乐意以另一种方式实现 - 我只需要加密一个字符串(不需要解密)。据我所知,内置的安全框架有我需要的东西,如果我可以关闭这种格式差距。如果有一种方法来转换密钥并从Web服务发送Base64编码,这对我也有用 - 但我找不到任何方法在那里进行ASN.1编码。

1 个答案:

答案 0 :(得分:1)

因此,我使用SecKeyWrapper类生成随机密钥,然后使用-getPublicKeyBits方法获取公钥的二进制表示(以内部使用的任何格式)。假设它是某种形式的DER ASN.1,我将它作为十六进制格式化到控制台并将其加载到this program。果然,内部表示是DER ASN.1,但它是我通常为RSA键表示找到的非常简化的版本:

![SEQUENCE { INTEGER, INTEGER }][2]

不应该太难以从二进制代表中动态构建。模数和指数,因为DER编码只是

30 (for SEQUENCE) LL (total sequence byte length) 
02 (INTEGER) LL (modulus byte length) XX XX... (modulus data bytes) 
02 LL XX XX XX... (exponent length and bytes)

这是我的代码,为简单起见。它使用一些用于XML + base64的Google库,只是抬头;还有Apple的演示代码SecKeyWrapper。有关完成此工作的说明,请参阅my other question。另请注意,它与 ARC兼容;这是留给读者的练习(我今年写的,现在)。

#define kTempPublicKey @"tempPayKey"
-(NSData *)encryptedDataWithXMLPublicKey:(NSString *)base64PublicKey data:(NSData *)data {
    if(![data length]){
        @throw [NSException exceptionWithName:@"NSInvalidArgumentException" reason:@"Data not set." userInfo:nil];
    }
    GTMStringEncoding *base64 = [GTMStringEncoding rfc4648Base64StringEncoding];
    NSData *keyData = [base64 decode:base64PublicKey];
    NSError *err = nil;
    GDataXMLDocument *keyDoc = [[GDataXMLDocument alloc] initWithData:keyData options:0 error:&err];
    if(err){
        NSLog(@"Public key parse error: %@",err);
        [keyDoc release];
        return nil;
    }

    NSString *mod64 = [[[[keyDoc rootElement] elementsForName:@"Modulus"] lastObject] stringValue];
    NSString *exp64 = [[[[keyDoc rootElement] elementsForName:@"Exponent"] lastObject] stringValue];
    [keyDoc release];
    if(![mod64 length] || ![exp64 length]){
        @throw [NSException exceptionWithName:@"NSInvalidArgumentException" reason:@"Malformed public key xml." userInfo:nil];
    }

    NSData *modBits = [base64 decode:mod64];
    NSData *expBits = [base64 decode:exp64];

    /* the following is my (bmosher) hack to hand-encode the mod and exp
     * into full DER encoding format, using the following as a guide:
     * http://luca.ntop.org/Teaching/Appunti/asn1.html
     * this is due to the unfortunate fact that the underlying API will
     * only accept this format (not the separate values)
     */

    // 6 extra bytes for tags and lengths
    NSMutableData *fullKey = [[NSMutableData alloc] initWithLength:6+[modBits length]+[expBits length]];
    unsigned char *fullKeyBytes = [fullKey mutableBytes];
    unsigned int bytep = 0; // current byte pointer
    fullKeyBytes[bytep++] = 0x30;
    if(4+[modBits length]+[expBits length] >= 128){
        fullKeyBytes[bytep++] = 0x81;
        [fullKey increaseLengthBy:1];
    }
    unsigned int seqLenLoc = bytep;
    fullKeyBytes[bytep++] = 4+[modBits length]+[expBits length];
    fullKeyBytes[bytep++] = 0x02;
    if([modBits length] >= 128){
        fullKeyBytes[bytep++] = 0x81;
        [fullKey increaseLengthBy:1];
        fullKeyBytes[seqLenLoc]++;
    }
    fullKeyBytes[bytep++] = [modBits length];
    [modBits getBytes:&fullKeyBytes[bytep]];
    bytep += [modBits length];
    fullKeyBytes[bytep++] = 0x02;
    fullKeyBytes[bytep++] = [expBits length];
    [expBits getBytes:&fullKeyBytes[bytep++]];

    SecKeyRef publicKey = [[SecKeyWrapper sharedWrapper] addPeerPublicKey:kTempPublicKey keyBits:fullKey];
    [fullKey release];

    NSData *encrypted = [[SecKeyWrapper sharedWrapper] wrapSymmetricKey:data keyRef:publicKey];
    // remove temporary key from keystore
    [[SecKeyWrapper sharedWrapper] removePeerPublicKey:kTempPublicKey];

    return encrypted;
}