0

我遵循了苹果开发人员文档,特别是显示如何生成密钥对、使用公钥加密和使用私钥解密的示例。他们在本指南中提供了三个示例方法(此处从第 19 页开始)。

我已将这三种方法复制并粘贴到我的项目中,仅将它们更改为公共类方法,添加日志记录和连接按钮以调用它们将加密的输出馈送到解密中:

在视图控制器中:

-(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];
}

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

谁能帮我理解这一点?

4

1 回答 1

2

提供的代码中的错误

+ (NSData *)encryptWithPublicKey中,这一行将剥离加密数据(并销毁它)

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

它应该是

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

每次结果都不一样

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

于 2013-08-17T14:13:33.100 回答