#include <openssl/rand.h>
#include <openssl/ecdsa.h>
#include <openssl/obj_mac.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
#import <CommonCrypto/CommonDigest.h>
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.whiteColor;
self.pubKey = [NSMutableString string];
[self deleteKeyAsync];
}
#define Secp256r1CurveLen 256
unsigned char Secp256r1header[] =
{
0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
0x01, 0x06, 0x08, 0x2A, 0x86, 0x48,
0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03,
0x42, 0x00
};
#define Secp256r1headerLen 26
#define PublicKeyInitialTag @"-----BEGIN PUBLIC KEY-----\n"
#define PublicKeyFinalTag @"\n-----END PUBLIC KEY-----"
//产生密钥
- (void)generateKeyAsync {
CFErrorRef error = NULL;
SecAccessControlRef sacObject;
//设置ACL,使用kSecAccessControlTouchIDAny表示使用Touch ID来保护密钥。
sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
kSecAccessControlTouchIDAny |
kSecAccessControlPrivateKeyUsage, &error);
NSDictionary *parameters = @{
(__bridge id)kSecAttrTokenID: (__bridge id)kSecAttrTokenIDSecureEnclave,//表示使用SecureEnclave来保存密钥
(__bridge id)kSecAttrKeyType: (__bridge id)kSecAttrKeyTypeECSECPrimeRandom,//表示产生ECC密钥对,注意目前只支持256位的ECC算法
(__bridge id)kSecAttrKeySizeInBits: @256,
(__bridge id)kSecPrivateKeyAttrs: @{
(__bridge id)kSecAttrAccessControl: (__bridge_transfer id)sacObject,
(__bridge id)kSecAttrIsPermanent: @YES,
(__bridge id)kSecAttrLabel: @"my-seec-key",
},
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
SecKeyRef publicKey, privateKey;
OSStatus status = SecKeyGeneratePair((__bridge CFDictionaryRef)parameters, &publicKey, &privateKey);
if (status == errSecSuccess) {
NSLog(@"产生密码成功");
//这里先把公钥保存到keychain才能拿到真正的公钥数据
NSDictionary *pubDict = @{
(__bridge id)kSecClass : (__bridge id)kSecClassKey,
(__bridge id)kSecAttrKeyType : (__bridge id)kSecAttrKeyTypeECSECPrimeRandom,
(__bridge id)kSecAttrLabel : @"public",
(__bridge id)kSecAttrIsPermanent : @(YES),
(__bridge id)kSecValueRef : (__bridge id)publicKey,
(__bridge id)kSecAttrKeyClass : (__bridge id)kSecAttrKeyClassPublic,
(__bridge id)kSecReturnData : @(YES)
};
CFTypeRef dataRef = NULL;
status = SecItemAdd((__bridge CFDictionaryRef)pubDict, &dataRef);
if(status == errSecSuccess){
NSLog(@"导出公钥成功");
//下面是将公钥转换为PEM格式,为了后面使用openssl验证签名
//PEM格式 = PublicKeyInitialTag + Base64(Secp256r1header + publicKeyData) + PublicKeyFinalTag
NSData *publicKeyData = (__bridge NSData *)dataRef;
NSLog(@"publicKeyData :%@",publicKeyData);
NSMutableData *data = [NSMutableData dataWithBytes:Secp256r1header
length:sizeof(Secp256r1header)];
[data appendData:publicKeyData];
NSString *base64String = [data base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
NSMutableString *publicKeyStr = [NSMutableString string];
[publicKeyStr appendString:PublicKeyInitialTag];
[publicKeyStr appendString:base64String];
[publicKeyStr appendString:PublicKeyFinalTag];
self.pubKey = publicKeyStr;
NSLog(@"++++%@",self.pubKey);
}else{
NSLog(@"导出公钥失败");
}
CFRelease(dataRef);
CFRelease(privateKey);
CFRelease(publicKey);
dispatch_async(dispatch_get_main_queue(), ^{
[self useKeyAsync];
});
}else{
NSLog(@"产生密码失败");
}
});
}
//使用密钥
- (void)useKeyAsync {
NSDictionary *query = @{
(__bridge id)kSecClass: (__bridge id)kSecClassKey,
(__bridge id)kSecAttrKeyClass: (__bridge id)kSecAttrKeyClassPrivate,
(__bridge id)kSecAttrLabel: @"my-seec-key",
(__bridge id)kSecReturnRef: @YES,
(__bridge id)kSecUseOperationPrompt: @"验证签名"
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
SecKeyRef privateKey;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&privateKey);
if (status == errSecSuccess) {
const unsigned char tmp[] = "123";
NSData *rawData = [[NSData alloc] initWithBytes:tmp length:3];
//调用SecKeyRawSign的时候系统会自动调起Touch ID验证用户指纹
//指纹的验证和数据的签名都在Secure Enclave中进行保证了安全
// status = SecKeyRawSign(privateKey, kSecPaddingPKCS1, dataToSign, sizeof(dataToSign), sign, &signatureLength);
NSError *error;
NSData *signature = CFBridgingRelease(SecKeyCreateSignature(privateKey, kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (CFDataRef)rawData, (void *)&error));
if (!error) {
NSLog(@"SecKeyRawSign签名数据成功");
//使用openSSL验证签名
const char *pemPubKey = [self.pubKey UTF8String];
BIO *buf = BIO_new_mem_buf((void*)pemPubKey, (int)self.pubKey.length);
EC_KEY *ecKey = PEM_read_bio_EC_PUBKEY(buf, NULL, NULL, NULL);
EC_KEY_print_fp(stdout, ecKey, 2);
const uint8_t *dataToSign = [rawData bytes];
size_t dataToSignLen = rawData.length;
uint8_t hash[CC_SHA256_DIGEST_LENGTH];
CC_SHA256(dataToSign, (uint32_t)dataToSignLen, hash);
int ret = ECDSA_verify(0, hash, CC_SHA256_DIGEST_LENGTH, [signature bytes], signature.length, ecKey);
if (ret == 1) {
NSLog(@"openssl 验证签名成功");
}else{
NSLog(@"openssl 验证签名失败");
}
}
CFRelease(privateKey);
}
else {
NSLog(@"SecKeyRawSign签名数据失败");
}
});
}
//删除密钥
- (void)deleteKeyAsync {
NSDictionary *query = @{
(__bridge id)kSecAttrTokenID: (__bridge id)kSecAttrTokenIDSecureEnclave,
(__bridge id)kSecClass: (__bridge id)kSecClassKey,
(__bridge id)kSecAttrKeyClass: (__bridge id)kSecAttrKeyClassPrivate,
(__bridge id)kSecAttrLabel: @"my-seec-key",
(__bridge id)kSecReturnRef: @YES,
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
if(status == errSecSuccess){
NSLog(@"delete success");
}else{
NSLog(@"delete fail");
}
[self generateKeyAsync];
});
}