ios钥匙串中的公钥在每次获取时都会发生变化

时间:2013-07-19 11:34:08

标签: iphone ios encryption

我已经关注了apple开发人员文档,特别是显示如何生成密钥对,使用公钥加密和使用私钥解密的示例。他们在指南中有三个示例方法(第19页以后here)。

我已经将这三个方法粘贴到我的项目中,只是将它们更改为公共类方法,添加日志记录和连接按钮来调用它们将加密输出提供给解密:

在viewcontroller中:

-(IBAction)generateKey:(UIButton*)sender
{
    [CryptoClass generateKeyPairPlease];
}

-(IBAction)encryptAndDecrypt
{
    NSData *data = [CryptoClass encryptWithPublicKey]; 
    [CryptoClass decryptWithPrivateKey:data];
}

这三种方法的代码是:

static const UInt8 publicKeyIdentifier[] = "com.apple.sample.publickey\0";
static const UInt8 privateKeyIdentifier[] = "com.apple.sample.privatekey\0";

+ (NSData *)encryptWithPublicKey
{
    OSStatus status = noErr;

    size_t cipherBufferSize;
    uint8_t *cipherBuffer;                     // 1

    // [cipherBufferSize]
    const uint8_t dataToEncrypt[] = "the quick brown fox jumps "
    "over the lazy dog\0"; // 2
    size_t dataLength = sizeof(dataToEncrypt)/sizeof(dataToEncrypt[0]);

    SecKeyRef publicKey = NULL;                                 // 3

    NSData * publicTag = [NSData dataWithBytes:publicKeyIdentifier
                                        length:strlen((const char *)publicKeyIdentifier)]; // 4

    NSMutableDictionary *queryPublicKey =
    [[NSMutableDictionary alloc] init]; // 5

    [queryPublicKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
    [queryPublicKey setObject:publicTag forKey:(__bridge id)kSecAttrApplicationTag];
    [queryPublicKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    [queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
    // 6

    status = SecItemCopyMatching
    ((__bridge CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKey); // 7

    //  Allocate a buffer

    cipherBufferSize = SecKeyGetBlockSize(publicKey);
    cipherBuffer = malloc(cipherBufferSize);

    //  Error handling

    if (cipherBufferSize < sizeof(dataToEncrypt)) {
        // Ordinarily, you would split the data up into blocks
        // equal to cipherBufferSize, with the last block being
        // shorter. For simplicity, this example assumes that
        // the data is short enough to fit.
        printf("Could not decrypt.  Packet too large.\n");
        return NULL;
    }

    // Encrypt using the public.
    status = SecKeyEncrypt(    publicKey,
                           kSecPaddingPKCS1,
                           dataToEncrypt,
                           (size_t) dataLength,
                           cipherBuffer,
                           &cipherBufferSize
                           );                              // 8

    //  Error handling
    //  Store or transmit the encrypted text

    if (publicKey) CFRelease(publicKey);

    NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:dataLength];

    free(cipherBuffer);

    return encryptedData;
}


+ (void)generateKeyPairPlease
{
    OSStatus status = noErr;
    NSMutableDictionary *privateKeyAttr = [[NSMutableDictionary alloc] init];
    NSMutableDictionary *publicKeyAttr = [[NSMutableDictionary alloc] init];
    NSMutableDictionary *keyPairAttr = [[NSMutableDictionary alloc] init];
    // 2

    NSData * publicTag = [NSData dataWithBytes:publicKeyIdentifier
                                        length:strlen((const char *)publicKeyIdentifier)];
    NSData * privateTag = [NSData dataWithBytes:privateKeyIdentifier
                                         length:strlen((const char *)privateKeyIdentifier)];
    // 3

    SecKeyRef publicKey = NULL;
    SecKeyRef privateKey = NULL;                                // 4

    [keyPairAttr setObject:(__bridge id)kSecAttrKeyTypeRSA
                    forKey:(__bridge id)kSecAttrKeyType]; // 5
    [keyPairAttr setObject:[NSNumber numberWithInt:1024]
                    forKey:(__bridge id)kSecAttrKeySizeInBits]; // 6

    [privateKeyAttr setObject:[NSNumber numberWithBool:YES]
                       forKey:(__bridge id)kSecAttrIsPermanent]; // 7
    [privateKeyAttr setObject:privateTag
                       forKey:(__bridge id)kSecAttrApplicationTag]; // 8

    [publicKeyAttr setObject:[NSNumber numberWithBool:YES]
                      forKey:(__bridge id)kSecAttrIsPermanent]; // 9
    [publicKeyAttr setObject:publicTag
                      forKey:(__bridge id)kSecAttrApplicationTag]; // 10

    [keyPairAttr setObject:privateKeyAttr
                    forKey:(__bridge id)kSecPrivateKeyAttrs]; // 11
    [keyPairAttr setObject:publicKeyAttr
                    forKey:(__bridge id)kSecPublicKeyAttrs]; // 12

    status = SecKeyGeneratePair((__bridge CFDictionaryRef)keyPairAttr,
                                &publicKey, &privateKey); // 13
    //    error handling...


    if(publicKey) CFRelease(publicKey);
    if(privateKey) CFRelease(privateKey);                       // 14
}

+ (void)decryptWithPrivateKey: (NSData *)dataToDecrypt
{
    OSStatus status = noErr;

    size_t cipherBufferSize = [dataToDecrypt length];
    uint8_t *cipherBuffer = (uint8_t *)[dataToDecrypt bytes];

    size_t plainBufferSize;
    uint8_t *plainBuffer;

    SecKeyRef privateKey = NULL;

    NSData * privateTag = [NSData dataWithBytes:privateKeyIdentifier
                                         length:strlen((const char *)privateKeyIdentifier)];

    NSMutableDictionary *queryPrivateKey = [[NSMutableDictionary alloc] init];

    // Set the private key query dictionary.
    [queryPrivateKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
    [queryPrivateKey setObject:privateTag forKey:(__bridge id)kSecAttrApplicationTag];
    [queryPrivateKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    [queryPrivateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
    // 1

    status = SecItemCopyMatching
    ((__bridge CFDictionaryRef)queryPrivateKey, (CFTypeRef *)&privateKey); // 2

    //  Allocate the buffer
    plainBufferSize = SecKeyGetBlockSize(privateKey);
    plainBuffer = malloc(plainBufferSize);

    if (plainBufferSize < cipherBufferSize) {
        // Ordinarily, you would split the data up into blocks
        // equal to plainBufferSize, with the last block being
        // shorter. For simplicity, this example assumes that
        // the data is short enough to fit.
        printf("Could not decrypt.  Packet too large.\n");
        return;
    }

    //  Error handling

    status = SecKeyDecrypt(    privateKey,
                           kSecPaddingPKCS1,
                           cipherBuffer,
                           cipherBufferSize,
                           plainBuffer,
                           &plainBufferSize
                           );                              // 3

    //  Error handling
    //  Store or display the decrypted text
    NSLog(@"Plain: %@",[NSString stringWithUTF8String:(const char *)plainBuffer]);
    if(privateKey) CFRelease(privateKey);
}

我一直在尝试许多不同的指南,并在这里阅读了很多帖子,试图让它发挥作用。我也尝试过Apples KeyChainWrapperItem来存储和检索密钥而没有运气。我还在这里发现了一篇文章,描述并显示了获取数据格式密钥的确切代码,但由于某种原因返回nil。

我做的最后一件事是使用Matt Gallagher的NSData + Base64类别来打印加密的字符串,并且可以直观地看到每个传递的字符串完全不同,即使我没有使用此代码生成新密钥:

-(IBAction)encryptAndDecrypt
{
    NSData *data = [CryptoClass encryptWithPublicKey]; 
    NSLog(@"String: %@", [data base64EncodedString]); // Print encrypted data as base64
    [CryptoClass decryptWithPrivateKey:data];
}

仅供参考我目前只在模拟器上运行,如果这很重要的话。我重置它以在每一代之前清除钥匙串。

任何人都可以帮我理解这个吗?

1 个答案:

答案 0 :(得分:2)

提供的代码中的错误

+ (NSData *)encryptWithPublicKey中,此行将删除加密数据(并将其销毁)

NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:dataLength];

应该是

NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:cipherBufferSize];

每次都有不同的结果

每次看到不同的结果并不是错误。 PKCS#1加密算法使用一些随机种子来使密码文本每次都不同。这称为填充并防止多次攻击,例如:频率分析和密文匹配。请参阅此维基百科文章部分:http://en.wikipedia.org/wiki/RSA_(algorithm)#Padding_schemes