在obj-c中将NSData加密为NSString?

Posted

技术标签:

【中文标题】在obj-c中将NSData加密为NSString?【英文标题】:Encrypted NSData to NSString in obj-c? 【发布时间】:2010-11-27 22:07:42 【问题描述】:

我有一个 iPhone 应用程序,它使用 CCCrypt (AES256) 和明文密钥对输入的 NSString 进行加密。字符串和密钥被提供给返回 NSData 对象的加密方法。

请求 [data description] 其中 'data' 是加密的字符串数据会给出一个 NSString,例如:“”但是当我尝试将其转换为 NSString 时,我得到“(null )”。

我需要向用户返回一个 NSString,它可用于使用相同的明文密钥解密回原始字符串。如果 NSData 对象的 'description' 属性可以返回一个字符串,有什么方法可以从 NSData 对象生成一个 NSString 而不会得到“(null)”?

更新:感谢 Quinn,他建议使用 Base64 编码来生成混乱的字符串。据我了解,Base64 编码不是简单地交换字符,而是字符交换取决于位置,所以没关系。

我唯一担心的是,我希望能够使用“密码”对消息进行加密,并且在需要解码混乱的字符串时需要输入相同的密码 - 任何人都可以建议实现这一点的方法吗?

【问题讨论】:

我已更新我的答案以解决以下问题。你是对的,Base64 不是替代算法——它基本上将 3 个字节扩展为 4 个字节,因此编码数据是非编码数据的 1.37 倍。基本上,它需要 3 个 8 位块并将其重新划分为 4 个 6 位块,然后将每个块重新解释为 8 位块,可以很容易地用 ASCII 表示。***有更多细节。 【参考方案1】:

首先,不要使用 -[NSData description] 来创建一个 NSString 用于此类目的。 (最好将-description 视为调试输出。如果my previous answer 误导了您,我深表歉意,我只是打印描述以证明NSData 可以加密和解密。)相反,使用NSString 的-dataUsingEncoding:-initWithData:encoding:在 NSData 和 NSString 之间转换的方法。即使有了这些,请注意 AES 加密的数据可能无法按原样很好地转换为字符串 - 某些字节序列无法很好地播放,因此在创建字符串之前对数据进行编码是个好主意。

我建议您尝试Base64 encoding NSData,因为 Base64 数据始终可以表示为 ASCII 字符串。 (当然,当你这样做时,你必须在解密之前从 Base64 解码。)

这里有一些有用的资源...

Colloquy 有一些代码可以对 NSData 进行编码/解码(header 和 implementation) Google Toolbox for Mac 具有类似的功能(header 和 implementation) Cocoa With Love blog post 讨论该主题。 CocoaDev.com wiki page 讨论该主题。

编辑:我假设您会将其与我对your previous question 的关于 NSString 对象的 AES 加密的回答结合起来。将数据编码为 Base64 不会对数据本身施加任何限制——它当然可以是 AES 加密的数据本身。如果您只想输入和输出字符串,请执行以下操作:

加密 提供要加密的 NSString,以及用于加密的密码。 将字符串转换为 NSData 并对其执行 AES 加密(请参阅上一个问题)。 Base64 编码 NSData,然后创建并返回编码输出的 NSString。 解密 提供加密和编码的字符串,以及用于解密的密码。 从第一个字符串创建一个 NSData,然后对数据进行 Base64 解码。 对数据执行 AES 解密,然后创建并返回一个 NSString。

这实际上只是将两个部分链接在一起并在输出时反向执行它们的问题。根据我之前的回答,您可以修改encryptString:withKey: 以执行最后一步并返回一个字符串,并将decryptData:withKey: 更改为decryptString:withKey: 并接受两个字符串。这很简单。

【讨论】:

@FreeAsInBeer 请不要在编辑他人的答案时将其标记为社区 Wiki。这不是它的用途。 @QuinnTaylor 常见问题解答指出您是唯一有权访问社区 Wiki 复选框的人。我的编辑可能无意中导致它进入社区 Wiki 模式,但这完全是无意的。请不要指责别人做他们做不到的事情。 Reference @FreeAsInBeer 我很抱歉,毕竟这不是你的错。我不知道如果出现以下情况,答案会自动转换为 CW:“帖子已被原始所有者编辑十 (10) 次。”当我查看您的编辑时,我碰巧看到它是 CW,但它是在我之前的编辑之后发生的。很抱歉草率下结论。 :-P @QuinnTaylor 这一切都很好。这些事情都会发生。毕竟,(即使我们认为自己是全能的计算机巫师)我们只是人类。 NSData .description 的文档仅声明A string that contains a hexadecimal representation of the object’s contents in a property list format. 没有建议仅用于调试目的。如果它适用于已知的数据格式,是否有不使用它的实际理由? [编辑:啊,也许我需要在原始问题重新加密数据的上下文中专门阅读这个答案。]【参考方案2】:

我已经为 NSData 和 NSString 整理了一整套类别,为字符串提供 AES256 加密。

请在“原始”问题上查看my answer了解更多详情。

【讨论】:

【参考方案3】:

我有类似的要求,当用户输入密码以进入应用程序时,我需要加密所有字符串,以便这些敏感字符串不会一直保持未加密状态。因此,我必须仅在需要时将这些字符串加密和解密。

这是一个简单的要求,我想保持轻松。因此,我使用@RobNapier 在他的blog 中分享的许多有用信息创建了一个small Obfuscator。对于那些正在寻找具有大量多汁 cmets 的轻量级解决方案的人来说,它可能会有所帮助。

进口基金会 导入 CommonCrypto // 接口周围的薄包装 公共枚举 CrypticAlgo 案例算法AES 案例算法DES
func blockSize() -> Int 
    switch self 
    case .AlgoAES:
        return kCCBlockSizeAES128
    case .AlgoDES:
        return kCCBlockSizeDES
    


func keySize() -> size_t 
    switch self 
    case .AlgoAES:
        return kCCKeySizeAES128
    case .AlgoDES:
        return kCCKeySizeDES
    


func algo() -> UInt32 
    switch self 
    case .AlgoAES:
        return CCAlgorithm(kCCAlgorithmAES)
    case .AlgoDES:
        return CCAlgorithm(kCCAlgorithmDES)
    

公共最终类 MGObfuscate

private var ivData: [UInt8]? private var derivedKey: Data? private let crypticAlgo: CrypticAlgo public init(password: String, salt: String, algo: CrypticAlgo) //Quickly get the data to release the password string let passwordData = password.data(using: .utf8)! // // Rounds require for 1 sec delay in generating hash. // Salt is a public attribute. If attacker somehow get the drivedKey and try to crack // the password via brute force, The delay due to Rounds will make it frustrating // to get actual password and deter his/her efforts. // let rounds = CCCalibratePBKDF(CCPBKDFAlgorithm(kCCPBKDF2), password.count, salt.count, CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA256), Int(CC_SHA256_DIGEST_LENGTH), 1000) let saltData = salt.data(using: .utf8)! derivedKey = MGObfuscate.derivedKey(for: passwordData, saltData: saltData, rounds: rounds) self.crypticAlgo = algo var ivData = [UInt8](repeating: 0, count: algo.blockSize()) // Random criptographically secure bytes for initialisation Vector let rStatus = SecRandomCopyBytes(kSecRandomDefault, ivData.count, &ivData) self.ivData = ivData // print(ivData) guard rStatus == errSecSuccess else fatalError("seed not generated \(rStatus)") @inline(__always) private static func derivedKey(for passwordData: Data, saltData: Data, rounds: UInt32) -> Data var derivedData = Data(count: Int(CC_SHA256_DIGEST_LENGTH)) let result = derivedData.withUnsafeMutableBytes (drivedBytes: UnsafeMutablePointer<UInt8>?) in passwordData.withUnsafeBytes( (passwordBytes: UnsafePointer<Int8>!) in saltData.withUnsafeBytes( (saltBytes: UnsafePointer<UInt8>!) in CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2), passwordBytes, passwordData.count, saltBytes, saltData.count, CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA256), rounds, drivedBytes, Int(CC_SHA256_DIGEST_LENGTH)) ) ) if kCCSuccess != result fatalError("failed to generate hash for password") return derivedData private func runCryptic(operation: Int, inputData: Data, keyData: Data, ivData: Data) -> Data let cryptLength = size_t(inputData.count + crypticAlgo.blockSize()) var cryptData = Data(count: cryptLength) let keyLength = crypticAlgo.keySize() var bytesProcessed: size_t = 0 let cryptStatus = cryptData.withUnsafeMutableBytes cryptBytes in inputData.withUnsafeBytes dataBytes in keyData.withUnsafeBytes keyBytes in ivData.withUnsafeBytes ivBytes in CCCrypt(CCOperation(operation), crypticAlgo.algo(), CCOptions(kCCOptionPKCS7Padding), keyBytes, keyLength, ivBytes, dataBytes, inputData.count, cryptBytes, cryptLength, &bytesProcessed) if cryptStatus == CCCryptorStatus(kCCSuccess) cryptData.removeSubrange(bytesProcessed..<cryptData.count) else fatalError("Error: \(cryptStatus)") return cryptData public func encriptAndPurge(inputString: inout String?) -> Data? if let inputdata = inputString?.data(using: .utf8) inputString = nil return runCryptic(operation: kCCEncrypt, inputData: inputdata, keyData: derivedKey!, ivData: Data(bytes: ivData!)) return nil public func encript(inputString: String) -> Data let inputdata = inputString.data(using: .utf8)! return runCryptic(operation: kCCEncrypt, inputData: inputdata, keyData: derivedKey!, ivData: Data(bytes: ivData!)) public func decript(data: Data, result: (String) -> Void) let data = runCryptic(operation: kCCDecrypt, inputData: data, keyData: derivedKey!, ivData: Data(bytes: ivData!)) result(String(data: data, encoding: .utf8)!) public func purge() ivData = nil derivedKey = nil

【讨论】:

以上是关于在obj-c中将NSData加密为NSString?的主要内容,如果未能解决你的问题,请参考以下文章

如何在obj-c中将图像转换为base64 [重复]

如何在 iOS 中将 NSData 变量转换为 NSInteger 变量

将 NSData 转换为 SecKeyRef

php加密文件 解密data 转nsstring 为nil. rc4 ios

一些加密方法

如何在 ios 中将 Base64 编码的 NSString 转换为字节数组(Java)?