4 Replies
      Latest reply on Oct 9, 2018 12:45 AM by Andreea.ionescu
      flarosa Level 1 Level 1 (0 points)

        Hi,

         

        I'm trying to figure out how to encrypt some data using an AES key. I also need to generate the AES key. My project is in Swift so I'd prefer to keep the code in Swift if possible.

         

        My research suggests that an AES key is simply 256 random bits, which can be created this way:

          var aesKeyBytes = [UInt8](repeating: 0, count: 32)
          _ = SecRandomCopyBytes(kSecRandomDefault, 32, &aesKeyBytes)

         

        So far so good, that code seems to fill the array with random bytes.

         

        What happens next is very hazy. I think I need a SecKey object so I can call the SecKeyEncrypt function. I tried creating one this way:

          let aesKeyData = Data(aesKeyBytes)
          let aesKeyDict:[NSObject:NSObject] = [
               kSecAttrKeyType: kSecAttrKeyTypeRSA,
               kSecAttrKeyClass: kSecAttrKeyClassSymmetric,
               kSecAttrKeySizeInBits: NSNumber(value: 256),
               kSecReturnPersistentRef: true as NSObject
          ]
          let aesKey = SecKeyCreateWithData(aesKeyData as CFData, aesKeyDict as CFDictionary, nil)

         

        It doesn't work though. The value of aesKey is nil after the function call. If I add the error parameter, the error reads:

        "Unsupported symmetric key type: 42"

         

        I suspect that kSecAttrKeyType should be something other than RSA, but I can't figure out what. In the documentation, it says I can use kSecAttrKeyTypeAES, but no such symbol actually exists in the code.

         

        Frank

        • Re: Encrypt using AES key?
          flarosa Level 1 Level 1 (0 points)

          After digging around in the documentation, I think the function I need is SecKeyCreateFromData rather than SecKeyCreateWithData. But when I type that into my app, it isn't recognized as a valid function.

           

          The documentation seems to suggest that SecKeyCreateFromData and kSecAttrKeyTypeAES are only available in Mac OS, which I can only guess is some kind of typo? I can't believe that AES encryption isn't available in iOS.

          • Re: Encrypt using AES key?
            eskimo Apple Staff Apple Staff (10,015 points)

            You are, alas, completely off in the weeds )-:  SecKeyEncrypt is for use with asymmetric encryption, like RSA or EC.  For symmetric encryption, like AES, you’ll need to use Common Crypto.  The CryptoCompatibility sample code shows the way.

            My project is in Swift so I'd prefer to keep the code in Swift if possible.

            This is quite challenging.  Common Crypto is not a nice API to call from Swift in general.  Moreover, prior to Xcode 10 (currently in beta), even importing the module is un-fun.  My recommendation is that you take the code from CryptoCompatibility, wrap it into a nice Objective-C class method that’s easy to call from Swift, and call that.

            Share and Enjoy

            Quinn “The Eskimo!”
            Apple Developer Relations, Developer Technical Support, Core OS/Hardware
            let myEmail = "eskimo" + "1" + "@apple.com"

            • Re: Encrypt using AES key?
              Andreea.ionescu Level 1 Level 1 (0 points)

              /**

                   Gets application (alice) PublicKey for exchange

                   - returns: the public key that need to be sent to Bob

                   */

                  public func getApplicationPublicKeyForExchange() throws -> SecKey? {

                      if #available(iOS 10.0, *) {

                          // generate a key for application

                   

                          let attributes: [String: Any] =

                              [kSecAttrKeySizeInBits as String: 256,

                               kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,

                               kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,

                               kSecPrivateKeyAttrs as String: [kSecAttrIsPermanent as String: false]

                          ]

                    

                          guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {

                              throw error!.takeRetainedValue() as Error

                          }

                          self.appPrivateKey = privateKey

                          self.appPublicKey = SecKeyCopyPublicKey(privateKey)

                    

                          return self.appPublicKey

                      } else {

                          return nil

                      }

                  }

               

               

              /**

                   Converts a base64 string into a SecKey

                   - parameter b64Key: String encoded base64

                   - returns: SecKey

                   */

                  func secKey(from b64Key: String) throws -> SecKey? {

                      if #available(iOS 10.0, *) {

                           let attributes: [String: Any] = [

                               kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,

                               kSecAttrKeyClass as String: kSecAttrKeyClassPublic,

                          ]

                          guard let data = Data(base64Encoded: b64Key),

                                let key = SecKeyCreateWithData(data as CFData,

                                                               attributes as CFDictionary,

                                                               &error) else {

                                                                  throw error!.takeRetainedValue() as Error

                          }

                          return key

                      }

                      return nil

                  }

               

              /**

                   Generates the shared secret using Bob's public key and the app private key

                   - parameter publicKey: SecKey the publickKey received from Bob

                   - parameter sharedSecret: The secret received from Bob. This was created by using the Alice's public key

                   */

                  public func getSharedSecret(for publicKey: SecKey?) throws -> CFData? {

                      if #available(iOS 10.0, *) {

                          guard let publicKey = publicKey, let privateKey = self.appPrivateKey else {

                              return nil

                          }

                      

                          let attributes: [String: Any] =

                              [kSecAttrKeySizeInBits as String: 256,

                               kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,

                               kSecAttrKeyClass as String: kSecAttrKeyClassPublic,

                               kSecPrivateKeyAttrs as String: [kSecAttrIsPermanent as String: false]

                          ]

                      

                          guard let sharedSecret = SecKeyCopyKeyExchangeResult(privateKey,

                                                                         SecKeyAlgorithm.ecdhKeyExchangeStandardX963SHA256,

                                                                         publicKey,

                                                                         attributes as CFDictionary, &error) else {

                              throw error!.takeRetainedValue() as Error

                          }

                          return sharedSecret

                      } else {

                          return nil

                      }

                  }

               

              // MARK: - AES256

               

              enum AESError: Swift.Error {

                      case keyGeneration(status: Int)

                      case cryptoFailed(status: CCCryptorStatus)

                      case badKeyLength

                      case badInputVectorLength

                  }

               

                  func encrypt(_ digest: Data, key: Data) throws -> Data {

                      return try crypt(input: digest, key: key, operation: CCOperation(kCCEncrypt))

                  }

               

                  func decrypt(_ encrypted: Data, key: Data) throws -> Data {

                      return try crypt(input: encrypted, key: key, operation: CCOperation(kCCDecrypt))

                  }

               

                  private func crypt(input: Data, key: Data, operation: CCOperation) throws -> Data {

                      // should be the same???

                   

                      var outLength = Int(0)

                      var outBytes = [UInt8](repeating: 0, count: input.count + kCCBlockSizeAES128)

                      var status: CCCryptorStatus = CCCryptorStatus(kCCSuccess)

                      input.withUnsafeBytes { (encryptedBytes: UnsafePointer<UInt8>!) -> () in

              //            iv.withUnsafeBytes { (ivBytes: UnsafePointer<UInt8>!) in

               

                          key.withUnsafeBytes { (keyBytes: UnsafePointer<UInt8>!) -> () in

                              status = CCCrypt(operation,

                                               CCAlgorithm(kCCAlgorithmAES),            // algorithm

                                  CCOptions(kCCOptionPKCS7Padding),           // options

                                  keyBytes,                                   // key

                                  key.count,                                  // keylength

                                  nil,                                        // iv

                                  encryptedBytes,                             // dataIn

                                  input.count,                                // dataInLength

                                  &outBytes,                                  // dataOut

                                  outBytes.count,                             // dataOutAvailable

                                  &outLength)                                 // dataOutMoved

                          }

              //        }

                      }

                      guard status == kCCSuccess else {

                          throw AESError.cryptoFailed(status: status)

                      }

                      return Data(bytes: UnsafePointer<UInt8>(outBytes), count: outLength)

                  }

               

               

              extension String {

                  func fromBase64() -> String? {

                      guard let data = Data(base64Encoded: self) else {

                          return nil

                      }

                    

                      return String(data: data, encoding: .utf8)

                  }

                

                  func toBase64() -> String {

                      return Data(self.utf8).base64EncodedString()

                  }

              }