我已经关注了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];
}
仅供参考我目前只在模拟器上运行,如果这很重要的话。我重置它以在每一代之前清除钥匙串。
任何人都可以帮我理解这个吗?
答案 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