         }
     }
     
+    private class func generateSymmetricKey(keySize: Int) -> NSData? {
+        var result = [UInt8](count: keySize, repeatedValue: 0)
+        SecRandomCopyBytes(kSecRandomDefault, keySize, &result)
+        
+        return NSData(bytes: result, length: keySize)
+    }
+    
     private class func encodeLength(length: Int) -> [CUnsignedChar] {
         if length < 128 {
             return [CUnsignedChar(length)];

        return result
    }
    
    private class func encrypt(string: String, secKey: SecKeyRef, blockSize: Int) -> NSData? {
        if string.startIndex == string.endIndex {
            return nil
        }
                
        let length = count(string)
        let blockLength = blockSize - 11
        var range = Range<String.Index>(start: string.startIndex, end: advance(string.startIndex, blockLength, string.endIndex))
        
        var encryptedData = [UInt8](count: Int(blockSize), repeatedValue: 0)
        var encryptedLength = blockSize
        
        let result = NSMutableData()
        
        // First range is always there
        let substring = string.substringWithRange(range)
        var data = [UInt8](substring.utf8)
        
        switch SecKeyEncrypt(secKey, SecPadding(kSecPaddingPKCS1), &data, distance(range.startIndex, range.endIndex), &encryptedData, &encryptedLength) {
            case noErr:
                result.appendBytes(&encryptedData, length: encryptedLength)
            default:
                return nil
        }
        
        // Remaining ranges are only accessible if we have not yet reached the end of the string
        while range.endIndex != string.endIndex {
            range = Range<String.Index>(start: range.endIndex, end: advance(range.endIndex, blockLength, string.endIndex))

            let substring = string.substringWithRange(range)
            var data = [UInt8](substring.utf8)
            
            switch SecKeyEncrypt(secKey, SecPadding(kSecPaddingPKCS1), &data, distance(range.startIndex, range.endIndex), &encryptedData, &encryptedLength) {
                case noErr:
                    result.appendBytes(&encryptedData, length: encryptedLength)
                default:
                    return nil
            }
        }
        
        return result
    }
    
    private class func decrypt(data: NSData, secKey: SecKeyRef, blockSize: Int) -> NSData? {
        if data.length < blockSize {
            return nil
        }
        
        let result = NSMutableData()
        
        let encryptedDataLength: Int = blockSize
        var range = NSRange(location: 0, length: blockSize)
        var encryptedData = [UInt8](count: encryptedDataLength, repeatedValue: 0)

        var decryptedData = [UInt8](count: Int(blockSize), repeatedValue: 0)
        var decryptedDataLength = blockSize
        
        while NSMaxRange(range) <= data.length {
            data.getBytes(&encryptedData, range: range)
            
            switch SecKeyDecrypt(secKey, SecPadding(kSecPaddingPKCS1), encryptedData, range.length, &decryptedData, &decryptedDataLength) {
                case noErr:
                    result.appendBytes(&decryptedData, length: decryptedDataLength)
                default:
                    return nil
            }
            
            let end = range.location + range.length
            range = NSRange(location: end, length: blockSize)
        }
        
        return result
    }
}

    }
    
    ///
    /// Encrypt an arbitrary string using AES256, the key for which
    /// is generated for a particular process and then encrypted with the
    /// public key from the RSA pair and prepended to the resulting data
    ///
    /// Result is a combination, where the first
    /// Encrypt an arbitrary string using the public key of the pair
    ///
    /// :param: string      Input string to be encrypted
    /// :param: urlEncode   If true, resulting Base64 string is URL encoded
    ///
    /// :returns: The encrypted data, as Base64 string. First
    ///
    public func encrypt(string: String, urlEncode: Bool = false) -> String? {
        // Generate a key and encrypt the message with said key
        if let key = Heimdall.generateSymmetricKey(Int(kCCKeySizeAES256)), encrypted = Heimdall.encrypt(string, key: key) {
            // Final resulting data
            let result = NSMutableData()
            
            // Encrypt the AES key with our public key
            if let publicKey = obtainKey(.Public) {
                let blockSize = SecKeyGetBlockSize(publicKey)
                var encryptedData = [UInt8](count: Int(blockSize), repeatedValue: 0)
                var encryptedLength = blockSize
                var data = UnsafePointer<UInt8>(key.bytes)
                
                switch SecKeyEncrypt(publicKey, SecPadding(kSecPaddingPKCS1), data, Int(key.length), &encryptedData, &encryptedLength) {
                case noErr:
                    result.appendBytes(&encryptedData, length: encryptedLength)
                default:
                    return nil
                }
            }
            
            result.appendData(encrypted)
            
            // Convert to a string
            var resultString = result.base64EncodedStringWithOptions(.allZeros)
            
            if urlEncode {
                resultString = resultString.stringByReplacingOccurrencesOfString("/", withString: "_")
                resultString = resultString.stringByReplacingOccurrencesOfString("+", withString: "-")
            }
            
            return resultString
        }
        
        return nil
