iOS - ECC椭圆曲线、ECDSA签名验签和ECIES加解密

09/30/2018 16:36 下午 posted in  Bitcoin

前言

ECC英文全称"Ellipse Curve Cryptography",与传统的基于大质数因子分解困难性的加密方法不同,ECC通过椭圆曲线方程式的性质产生密钥

ECC164位的密钥产生一个安全级,相当于RSA 1024位密钥提供的保密强度,而且计算量较小,处理速度更快,存储空间和传输带宽占用较少。目前我国居民二代身份证正在使用 256 位的椭圆曲线密码,虚拟货币比特币也选择ECC作为加密算法。

加密

基于这个秘密值,用来对Alice和Bob之间的报文进行加密的实际方法是适应以前的,最初是在其他组中描述使用的离散对数密码系统。这些系统包括:

Diffie-Hellman—ECDH

MQV—ECMQV

ElGamal discrete log cryptosystem—ECElGamal

数字签名算法—ECDSA

对于ECC系统来说,完成运行系统所必须的群操作比同样大小的因数分解系统或模整数离散对数系统要慢。不过,ECC系统的拥护者相信ECDLP问题比DLP或因数分解问题要难的多,并且因此使用ECC能用小的多的密钥长度来提供同等的安全,在这方面来说它确实比例如RSA之类的更快。到目前为止已经公布的结果趋于支持这个结论,不过一些专家表示怀疑。

ECC被广泛认为是在给定密钥长度的情况下,最强大的非对称算法,因此在对带宽要求十分紧的连接中会十分有用。

优点

安全性高

有研究表示160位的椭圆密钥与1024位的RSA密钥安全性相同。

处理速度快

在私钥的加密解密速度上,ecc算法比RSA、DSA速度更快。
存储空间占用小。
带宽要求低。
以上为ECC椭圆曲线算法需要了解的基本知识,摘自强大的百科度娘。

iOS-ECC

关于ECC,苹果支持以下算法:

PKG:

curves P-224, P-256, P-384, P-521

PKV:

curves P-224, P-256, P-384, P-521

Signature Generation:

curves P-224, P-256, P-384, P-521

using (SHA-224, SHA-256, SHA384, SHA512)

Signature Verification:

curves P-224, P-256, P-384, P-521

using (SHA-1, SHA-224, SHA-256, SHA384, SHA512)

采用的都是NIST标准和规范。但是苹果官方API仅为开发者提供了椭圆曲线P-256的256位EC密钥。由于苹果SEP硬件提供的保护机制,私钥会直接以keychain的形式截留在SEP中,不能提取,也不能从外部导入,只能通过引用使用。

ECDSA

椭圆曲线数字签名算法(ECDSA)是使用椭圆曲线密码(ECC)对数字签名算法(DSA)的模拟,下面是关于ECDSA的API调用。

1、创建ECC椭圆曲线的keychain属性,属性设置具体可以根据自己需要,获取ECC私钥。

sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault,  
                                             kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,  
                                             // kSecAccessControlTouchIDAny |  
                                             kSecAccessControlPrivateKeyUsage, &error);  
    
 // Create parameters dictionary for key generation.  
 NSDictionary *parameters = @{  
                              (id)kSecAttrTokenID: (id)kSecAttrTokenIDSecureEnclave,  
                              (id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,  
                              (id)kSecAttrKeySizeInBits: @256,  
                              (id)kSecAttrLabel: @"my-se-key",  
                              (id)kSecPrivateKeyAttrs: @{  
                                      (id)kSecAttrAccessControl: (__bridge_transfer id)sacObject,  
                                      (id)kSecAttrIsPermanent: @YES,  
                                      }  
                              };
NSError *gen_error = nil;
//根据参数生成私钥
id privateKey = CFBridgingRelease(SecKeyCreateRandomKey((__bridge CFDictionaryRef)parameters, (voidvoid *)&gen_error));
2.使用私钥提取公钥,并用于签名。

//根据keychain的属性查找ECC私钥,并获取私钥引用。

NSDictionary *params = @{
 
(id)kSecClass: (id)kSecClassKey, (id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom, (id)kSecAttrKeySizeInBits: @256, (id)kSecAttrLabel: @"my-se-key", (id)kSecReturnRef: @YES, (id)kSecUseOperationPrompt: @"Authenticate to sign data" };

SecKeyRef privateKey;  
      OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)params, (CFTypeRef *)&privateKey);
     

3.签名

NSError *error;  
NSData *dataToSign = [@"我是签名内容" dataUsingEncoding:NSUTF8StringEncoding];  
NSData *signature = CFBridgingRelease(SecKeyCreateSignature(privateKey, kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (CFDataRef)dataToSign, (voidvoid *)&error));

对于kSecKeyAlgorithmECDSASignatureMessageX962SHA256签名算法,官方还给了:SHA1、SHA224、SHA384、SHA512用于EC密钥摘要。可以自己需求选择签名对应的摘要算法。API的名字也很明确的给了这里执行的标准规范为X9.62。

4.验签

//提取公钥,进行验签,验签选择的算法必须与签名时的算法一致。
 
id publicKey = CFBridgingRelease(SecKeyCopyPublicKey((SecKeyRef)privateKey));
 
Boolean verified = SecKeyVerifySignature((SecKeyRef)publicKey, kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (CFDataRef)dataToSign, (CFDataRef)signature, (void *)&error); if (verified == 1) { message = [NSString stringWithFormat:@"signature:%@ verified:%@ error:%@", signature, @"验签成功", error]; }else{ message = [NSString stringWithFormat:@"signature:%@ verified:%@ error:%@", signature, @"验签失败", error]; }

##ECIES
校验密钥是否和算法是否匹配,只有都符合条件了才能用于加密。

SecKeyAlgorithm algorithm = kSecKeyAlgorithmECIESEncryptionCofactorX963SHA256AESGCM;  
BOOL canEncrypt = SecKeyIsAlgorithmSupported((SecKeyRef)publicKey,kSecKeyOperationTypeEncrypt, algorithm);

加密

CFErrorRef error = NULL;  
   cipherText = (NSData*)CFBridgingRelease(      // ARC takes ownership  
                                           SecKeyCreateEncryptedData(publicKey,  
                                                                     algorithm,  
                                                                     (__bridge CFDataRef)encryptionData,&error));

encryptionData为要加密的数据,这里提示一下:

As an additional check before encrypting, because asymmetric encryption restricts the length of the data that you can encrypt, verify that the data is short enough. For this particular algorithm, the plain text data must be 130 bytes smaller than the key’s block size, as reported by SecKeyGetBlockSize. You therefore further condition the proceedings on a length test:

NSData* plainText = ;
canEncrypt &= ([plainText length] < (SecKeyGetBlockSize(publicKey)-130));

官方API描述,明文数据要比密钥块小130个字节。

解密

CFErrorRef error = NULL;  
    clearText = (NSData*)CFBridgingRelease(       // ARC takes ownership  
                                           SecKeyCreateDecryptedData(private,  
                                                                     algorithm,  
                                                                     (__bridge CFDataRef)cipherText,

https://developer.virgilsecurity.com/docs/sdk-and-tools

https://medium.com/@edisonlo/objective-c-digital-signature-signing-and-verification-with-pem-der-or-base64-string-aff4c0a7f805

https://kjur.github.io/jsrsasign/sample/sample-ecdsa.html

https://forums.developer.apple.com/thread/87758

PUBLIC_KEY = "MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAESJCvH4lEoGgLof637UGdAYHwFW0GddD/DbVu8yFVTt5Zq+kkftDpQDelSnhmmbr9v+ZsIESINctknP3LTbeLIg==";
PRIVATE_KEY = "MIGNAgEAMBAGByqGSM49AgEGBSuBBAAKBHYwdAIBAQQgi5h75Y80gEeJQQZ6zq7zjT9a11lyLhf9kF/ItIGFDHCgBwYFK4EEAAqhRANCAARIkK8fiUSgaAuh/rftQZ0BgfAVbQZ10P8NtW7zIVVO3lmr6SR+0OlAN6VKeGaZuv2/5mwgRIg1y2Sc/ctNt4si";