2016-11-10 19 views
0

Я пытаюсь подписать данные и проверить подпись с использованием алгоритма эллиптической кривой на iOS. Создание ключей работает достаточно хорошо, но попытка подписи данных возвращает ошибку -1 - что очень общее.Подписание данных ключом kSecAttrKeyTypeEC на iOS

Ключи создаются следующим образом:

publicKeyRef = NULL; 
privateKeyRef = NULL; 

NSDictionary * privateKeyAttr = @{(id)kSecAttrIsPermanent : @1, 
            (id)kSecAttrApplicationTag : privateTag}; 

NSDictionary * publicKeyAttr = @{(id)kSecAttrIsPermanent : @1, 
           (id)kSecAttrApplicationTag : privateTag}; 

NSDictionary * keyPairAttr = @{(id)kSecAttrKeySizeInBits : @(keySize), 
           (id)kSecAttrKeyType : (id)kSecAttrKeyTypeEC, 
           (id)kSecPrivateKeyAttrs : privateKeyAttr, 
           (id)kSecPublicKeyAttrs : publicKeyAttr}; 

OSStatus status = SecKeyGeneratePair((CFDictionaryRef)keyPairAttr, &publicKeyRef, &privateKeyRef); 

Это возвращает статус 0, до сих пор так хорошо. Фактическое подписание происходит так:

- (NSData *) signData:(NSData *)dataToSign withPrivateKey:(SecKeyRef)privateKey { 
    NSData * digestToSign = [self sha1DigestForData:dataToSign]; 

    size_t signedHashBytesSize = SecKeyGetBlockSize(privateKey); 

    uint8_t * signedHashBytes = malloc(signedHashBytesSize * sizeof(uint8_t)); 
    memset((void *)signedHashBytes, 0x0, signedHashBytesSize); 
    OSStatus signErr = SecKeyRawSign(privateKey, 
           kSecPaddingPKCS1, 
           digestToSign.bytes, 
           digestToSign.length, 
           (uint8_t *)signedHashBytes, 
           &signedHashBytesSize); 
    NSLog(@"Status: %d", signErr); 

    NSData * signedHash = [NSData dataWithBytes:(const void *)signedHashBytes length:(NSUInteger)signedHashBytesSize]; 
    if (signedHashBytes) free(signedHashBytes); 

    return (signErr == noErr) ? signedHash : nil; 
} 

- (NSData *)sha1DigestForData:(NSData *)data { 
    NSMutableData *result = [[NSMutableData alloc] initWithLength:CC_SHA1_DIGEST_LENGTH]; 
    CC_SHA1(data.bytes, (CC_LONG) data.length, result.mutableBytes); 

    return result; 
} 

вызов к SecKeyRawSign() возвращается -1.

Это заимствовано из https://forums.developer.apple.com/message/95740#95740

Что такое правильный способ использования ключа ЕС для подписания данных? Здесь есть рабочее решение для ключей RSA: Signing and Verifying on iOS using RSA, но я не смог адаптировать его к ключам EC.

ответ

1

Похоже, что часть проблемы связана с правильным синтаксисом при создании указателей и вычислении размера данных для звонков на SecKeyRawSign. Рабочий пример в Swift 3 выглядит следующим образом:

генерации ключей, хранится в защищенной Анклав (и временно в переменных экземпляра):

func generateKeyPair() -> Bool { 
    if let access = SecAccessControlCreateWithFlags(nil, 
                kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, 
                [.userPresence, .privateKeyUsage], 
                nil) { 

     let privateKeyAttr = [kSecAttrIsPermanent : 1, 
           kSecAttrApplicationTag : privateTag, 
           kSecAttrAccessControl as String: access 
      ] as NSDictionary 

     let publicKeyAttr = [kSecAttrIsPermanent : 0, 
          kSecAttrApplicationTag : publicTag 
      ] as NSDictionary 

     let keyPairAttr = [kSecAttrKeySizeInBits : 256, 
          kSecAttrKeyType : kSecAttrKeyTypeEC, 
          kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave, 
          kSecPrivateKeyAttrs : privateKeyAttr, 
          kSecPublicKeyAttrs : publicKeyAttr] as NSDictionary 

     let err = SecKeyGeneratePair(keyPairAttr, &publicKey, &privateKey) 
     return err == noErr 
} 

подписывания данных:

func signData(plainText: Data) -> NSData? { 
    guard privateKey != nil else { 
     print("Private key unavailable") 
     return nil 
    } 

    let digestToSign = self.sha1DigestForData(data: plainText as NSData) as Data 

    let signature = UnsafeMutablePointer<UInt8>.allocate(capacity: 128) 
    var signatureLength = 128 
    let err = SecKeyRawSign(privateKey!, 
          .PKCS1SHA1, 
          [UInt8](digestToSign), 
          Int(CC_SHA1_DIGEST_LENGTH), 
          signature, 
          &signatureLength) 

    print("Signature status: \(err)") 

    let sigData = NSData(bytes: signature, length: Int(signatureLength)) 

    return sigData 
} 

func sha1DigestForData(data: NSData) -> NSData { 
    let len = Int(CC_SHA1_DIGEST_LENGTH) 
    let digest = UnsafeMutablePointer<UInt8>.allocate(capacity: len) 
    CC_SHA1(data.bytes, CC_LONG(data.length), digest) 
    return NSData(bytesNoCopy: UnsafeMutableRawPointer(digest), length: len) 
} 

Подтвердить подпись:

func verifySignature(plainText: Data, signature: NSData) -> Bool { 
    guard publicKey != nil else { 
     print("Public key unavailable") 
     return false 
    } 

    let digestToSign = self.sha1DigestForData(data: plainText as NSData) as Data 
    let signedHashBytesSize = signature.length 

    let err = SecKeyRawVerify(publicKey!, 
           .PKCS1SHA1, 
           [UInt8](digestToSign), 
           Int(CC_SHA1_DIGEST_LENGTH), 
           [UInt8](signature as Data), 
           signedHashBytesSize) 

    print("Verification status: \(err)") 

    return err == noErr 
} 

Если вам нужно экспорта открытого ключа так, что он может быть использован другим приложением или устройством, это можно сделать так:

let parameters = [ 
    kSecClass as String: kSecClassKey, 
    kSecAttrKeyType as String: kSecAttrKeyTypeEC, 
    kSecAttrLabel as String: "Public Key", 
    kSecAttrIsPermanent as String: false, 
    kSecValueRef as String: publicKey, 
    kSecAttrKeyClass as String: kSecAttrKeyClassPublic, 
    kSecReturnData as String: true 
    ] as CFDictionary 
var data:AnyObject? 
let status = SecItemAdd(parameters, &data) 
print("Public key added \(status)") 
if let keyData = data as? NSData { 
    print("This is the key, send it where it needs to go:\n\(keyData)") 
}