I have an iPhone app which encrypts an inputted NSString using CCCrypt (AES256) and a plaintext key. The string and key are given to the encryption method which returns an NSData object.
Requesting [data description] where 'data' is the encrypted string data gives an NSString like: "<0b368353 a707e7de 3eee5992 ee69827e e3603dc2 b0dbbc0b 861ca87d f39ce72a>" but when I try to convert that to an NSString, I get "(null)".
I need to return an NSString to the user, which can be used to decrypt back to the original string using the same plaintext key. If the 'description' property of the NSData object can return a string, is there any way I can produce an NSString from the NSData object without getting "(null)"?
UPDATE: Thanks to Quinn, who suggests using Base64 encoding to produce the muddled string. From what I understand, Base64 encoding does not simply swap characters, but the character exchange depends on the position, so that's fine.
My only concern is that I want to be able to encrypt the message with a 'passphrase', and require the identical passphrase to be entered when the muddled string needs to be decoded - can anybody suggest ways to implement this?
First off, DO NOT use -[NSData description] to create an NSString for such purposes. (It's best to treat -description as debugging output. I apologize if my previous answer misled you, I was merely printing the description to demonstrate that the NSData can be encrypted and decrypted.) Instead, use NSString's -dataUsingEncoding: and -initWithData:encoding: methods to convert between NSData and NSString. Even with these, note that AES-encrypted data will probably not translate well into strings as-is — some byte sequences just won't play nicely, so it's a good idea to encode the data before creating the string.
I'd suggest you try Base64 encoding the NSData, since Base64 data can always be represented as an ASCII string. (Of course, when you do that, you'll have to decode from Base64 before decrypting.)
Here are some helpful resources...
Colloquy has some code that does encoding/decoding on NSData (header and implementation)
Google Toolbox for Mac has similar functionality (header and implementation)
A Cocoa With Love blog post on the topic.
A CocoaDev.com wiki page on the topic.
Edit: I was assuming you'd combine this with my answer to your previous question on AES encryption of NSString objects. Encoding data as Base64 doesn't place any restrictions on the data itself — it can certainly be AES-enrypted data itself. Here's what to do if you just want string input and output:
Encryption
Provide the NSString to be encrypted, and the passphrase to use for encrypting.
Convert the string to an NSData and perform AES encryption on it (see previous question).
Base64-encode the NSData, then create and return and NSString of the encoded output.
Decryption
Provide the encrypted and encoded string, and the passphrase to use for decrypting.
Create an NSData from the first string, then Base64-decode the data.
Perform AES decryption on the data, then create and return an NSString.
It's really just a matter of chaining the two parts together and performing them in reverse on the way out. From my previous answer, you can modify encryptString:withKey: to perform the last step and return a string, and change decryptData:withKey: to be decryptString:withKey: and accept two strings. It's pretty straightforward.
I have put together a complete bundle of categories for NSData and NSString to provide AES256 encryption for strings.
Please see my answer on the 'original' question for more details.
I have similar requirement where I need to encrypt all the strings when user enters the password to enter to app so that those sensitive strings doesn't remain unencrypted all the time. So I have to keep those strings encrypted and decrypt as an when require only.
It was a simple requirement and I wanted to keep it light. So I have created a small Obfuscator using lot of useful info shared by #RobNapier in one his blog. It might help for those who are looking a lightweight solution with lot of juicy comments.
import Foundation
import CommonCrypto
// A thin wrapper around interfacing
public enum CrypticAlgo {
case AlgoAES
case AlgoDES
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)
}
}
}
public final class 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
}
}
Related
I have been trying to implement encryption using CommonCrypto library in swift 4.2. But no luck, ending up with some unknown error.
Somebody please look at this code and help me.
func encrypty(data value: String) -> EncryptionResult {
guard var messageData = value.data(using: .utf8), var key = getSecretkey()?.data(using: .utf8) else {
return EncryptionResult.failure
}
//iv ata
guard let ivData = generateRandomBytes(of: Int32(SecurityConstants.blockSize))?.data(using: .utf8) else {
return EncryptionResult.failure
}
//output
var outputData = Data(count: (messageData.count + SecurityConstants.blockSize + ivData.count))
var localOutput = outputData
//output length
var outputLength: size_t = 0
//encyrption
let status = key.withUnsafeBytes { keyBytes in
messageData.withUnsafeBytes { messageBytes in
localOutput.withUnsafeMutableBytes { mutableOutput in
ivData.withUnsafeBytes { ivDataBytes in
CCCrypt( CCOperation(kCCEncrypt),
CCAlgorithm(kCCAlgorithmAES128),
CCOptions(kCCOptionPKCS7Padding),
keyBytes,
key.count,
ivDataBytes,
messageBytes,
messageData.count,
mutableOutput,
outputData.count,
&outputLength)
}
}
}
}
guard status == Int32(kCCSuccess) else {
logError("Error in encryption")
return EncryptionResult.failure
}
outputData.count = outputLength
return EncryptionResult.success(value: outputData.base64EncodedString())
}
Error -4310 is kCCKeySizeError (see CommonCryptoError.h). That means your key is not the right size.
Looking at this code, this in particular is very suspicious:
getSecretkey()?.data(using: .utf8)
If a key is decodable as UTF-8, it's not a proper key. You seem to have the same problem with your IV. I suspect that generateRandomBytes() does not quite do what it says it does. It's also not going to be possible to decrypt this data because you throw away the random IV (which the decryptor will require). You create room for it in the output (which is good), but you never write it.
Can someone explain the behavior of the function below? Some have suggested to not use NSData. Do you have better alternatives to mention? If the returned value is Base64Encoded can I decode on one of the online encoders/decoders? Thanks.
func stringToData(message: String) -> NSData? {
let strData = NSData(base64Encoded: message, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters)
return strData
}
NSData(base64Encoded:options:) is documented to attempt to initialize a data object with the given Base64 encoded string—and return nil if it fails. In other words; it decodes a Base64 encoded string as an NSData object.
In Swift, you would likely use the base64EncodedString() function and the Data(base64Encoded:) initializer on the Data type to encode and decode data as Base64 strings, for example like this:
let originalData = Data(bytes: [1,2,3,4,5,6,7,8,9,10,11,12])
let encodedAsBase64String = originalData.base64EncodedString()
// "AQIDBAUGBwgJCgsM"
let decodedData = Data(base64Encoded: encodedAsBase64String) // is optional because the decoding can fail
// 12 bytes: <01020304 05060708 090A0B0C>
I am trying to connect to the coinspot API which has very shoddy documentation. Link below:
https://www.coinspot.com.au/api
It requires an HMAC SHA512 hash attached to the user's secret key, then a nonce key In the parameters of a POST request which is a timestamp turned to an integer to stay unique at each request. The header requires both the API key and the signature.
I use Alamofire's JSON encoded post request through coinspot's private API URL https://www.coinspot.com.au/api
Seems I get a success message for connecting but get an {status = invalid;} response.
The only 2 things I can think of are a wrong POST request (which doesn't seem the case, as if I don't add the nonce key in the parameters, I get a proper response requesting for one from the server), and the HMAC SHA-512 hash. I've taken the code from
HMAC SHA512 using CommonCrypto in Swift 3.1
And modified it slightly to just include the SHA512 hashing.
Code below:
extension String{
var sha512:String{
return HMAC.hash(inp: self, algo: HMACAlgo.SHA512)
}
}
public struct HMAC{
static func hash(inp: String, algo: HMACAlgo)->String{
if let stringData = inp.data(using: String.Encoding.utf8, allowLossyConversion: false){
return hexStringFromData(input: digest(input: stringData as NSData, algo: algo))
}
return ""
}
private static func digest(input: NSData, algo: HMACAlgo)-> NSData{
let digestLength = algo.digestLength()
var hash = [UInt8](repeating: 0, count: digestLength)
CC_SHA512(input.bytes, UInt32(input.length), &hash)
return NSData(bytes: hash, length: digestLength)
}
private static func hexStringFromData(input: NSData)-> String{
var bytes = [UInt8](repeating: 0, count: input.length)
input.getBytes(&bytes, length: input.length)
var hexString = ""
for byte in bytes{
hexString += String(format: "%02x", UInt8(byte))
}
return hexString
}
}
enum HMACAlgo{
case SHA512
func digestLength()->Int{
var result:CInt = 0
switch self{
case .SHA512:
result = CC_SHA512_DIGEST_LENGTH
}
return Int(result)
}
}
The CommonCrypto library has been properly added with an objectiveC bridging file, and printing the result gives me a proper hashed signature.
I've contacted coinspot but they are swarmed with requests and they only have a cookie template response in regards to any technical support for their API.
Here's the Alamofire segment I placed under a function to call any command to the coinspot server:
func request(_ command: Router, params: [String: AnyObject]?, completion: #escaping (Result<JSON>)-> Void){
var parameters = ["nonce": Int(Date().timeIntervalSince1970*1000) as AnyObject]
if params != nil{
parameters = parameters + params!
}
let sign = customerSecret.sha512
print(sign)
let headers:HTTPHeaders = ["key": APIKey, "sign": sign]
do{
try Alamofire.request( command.asURL(), method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: headers).responseJSON{(response) in
guard let json = response.result.value else {
completion(.failure(CoinSpotError.networkError(error: response.result.error)))
return
}
print(response)
}
} catch {
print("error")
}
}
I think the above is correct. Router is just a set of addresses that call to the proper POST request for retrieving data.
Finally, I've changed the request to GET, got rid of both headers and parameters, and I get a status ok and the resulting JSON. I figured I'll just try a command on a browser and it worked,
The API does say every request must be a post method.
Coinspot API is down, with no ETA: Coinbase API down
I continue to receive {'status': 'invalid'} back from any request I make.
Their publicapi is still up, albeit only for BTC, LTC, and DOGE.
def get_latest_price():
r = requests.get("https://www.coinspot.com.au/pubapi/latest").json()
print(r)
I'm using zaph's example for encryption and decryption from this post
The encryption works well, and with my encryption key and iv, returns an NSData object, containing the following string: "bc6983a8 65d412df 2bafdc40 f569874e", which is my input text encrypted. The content of the returned NSData object:
encrypted text: <bc6983a8 65d412df 2bafdc40 f569874e>
This text is sent to a server (json), and the server returns a response, also encrypted with the same encryption key and iv.
My question is, how can I convert the string text that comes from the server's response(bc6983a8 65d412df 2bafdc40 f569874e, for example) into an NSData object so that i can decrypt it?
I tried the follwing:
let plainData = ("<bc6983a8 65d412df 2bafdc40 f569874e>" as NSString).dataUsingEncoding(NSUTF8StringEncoding)!;
let plainData = ("<bc6983a8 65d412df 2bafdc40 f569874e>" as NSString).dataUsingEncoding(NSUTF8StringEncoding)!;
let base64String = plainData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0));
let dataDec = NSData(base64EncodedString: base64String, options: NSDataBase64DecodingOptions(rawValue: 0))
But when displaying the contents of the NSData object, the output is not the one expected:
data Optional("<3c626336 39383361 38203635 64343132 64662032 62616664 63343020 66353639 38373465 3e>")
Any help is appreciated.
You are converting your NSData to the string in a wrong way. Follow this code to convert NSData to string
//This is your encrypted data
var encryptedData = NSData()
let plainData = encryptedData(data: encryptedData, encoding: NSUTF8StringEncoding)
Hope this will work for you.
UPDATE:
This happens because you are not correctly fetching the string from your backend. Use proper method for decoding json data instead of just printing it. "<bc6983a8 65d412df 2bafdc40 f569874e>" is not the string you actually need. You need to decode your json data
See the sample code
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil
{
print("error=\(error)", terminator: "")
return
}
do{ if let newdata = try? (NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.AllowFragments) as! NSDictionary)
{
print(newdata)
}
Here 'newdata' may include the encrypted string you need. Parse it from that json, convert it to NSData and then decrypt.
UPDATE 2
Use this code to convert your data to string
let resstr = NSString(data: YourData, encoding: NSUTF8StringEncoding)
I am trying to create a multiplayer game that will send moves between players using Game Center. I'm still learning a lot about programming, so please excuse me if my question is ill-formed. Also, I am not very familiar with Obj-C, so a Swift answer would be great.
In my toy program to try and teach myself, I am trying to follow the strategy used by Shayne Meyer using the GameKitHelper class here: https://github.com/shaynemeyer/SwiftCircuitRacer/tree/master/SwiftCircuitRacer
Using this approach, Shayne sends messages to other players online using structs sent as NSData. I am able to send integers (e.g., the ILoveYou message) but not messages that carry a string property (e.g., the Thanks message). In this latter case I get "Thread 1: EXC_BAD_ACCESS(code=1, address=0x78674100)" at the line "var messageThanks = UnsafePointer,MesssageThanks>(data.bytes).memory"
Eventually, I would like to send game moves that provide both strings and integers together. How does one send a message struct as NSData when properties also include a string? Secondly, I would be appreciative if someone could help me understand fundamentally what is going on when the data is packaged and how what UnsafePointer is doing as it related to sending data via Game Center.
Thank you.
Cliff
enum MessageType: Int {
case ILoveYou, Thanks
}
struct Message {
let messageType: MessageType
}
struct MessageILoveYou {
let message: Message
let messageSenderNumber: UInt32
}
struct MessageThanks {
let message: Message
let messageSenderName: String
let messageSenderNumber: UInt32
}
func sendILoveYou() {
println("sendILoveYou:")
let nameNumber = UInt32(56)
var message = MessageILoveYou(message: Message(messageType: MessageType.ILoveYou), messageSenderNumber: nameNumber)
let data = NSData(bytes: &message, length: sizeof(MessageILoveYou))
sendData(data)
}
func sendThanks() {
println("sendThanks:")
let nameString = "Don J"
let senderNumberInt = UInt32(88)
var message = MessageThanks(message: Message(messageType: MessageType.Thanks), messageSenderName: nameString, messageSenderNumber: senderNumberInt)
let data = NSData(bytes: &message, length: sizeof(MessageThanks))
sendData(data)
}
func matchReceivedData(match: GKMatch, data: NSData, fromPlayer player: String) {
println("matchReceivedData:")
var message = UnsafePointer<Message>(data.bytes).memory
if message.messageType == MessageType.ILoveYou {
println("messageType == ILoveYou")
let messageILoveYou = UnsafePointer<MessageILoveYou>(data.bytes).memory
iLoveYouThanksDelegate?.iLoveYouReceived(from: messageILoveYou.messageSenderNumber)
} else if message.messageType == MessageType.Thanks {
println("messageType == Thanks")
var messageThanks = UnsafePointer<MessageThanks>(data.bytes).memory
iLoveYouThanksDelegate?.thanksReceived(from: messageThanks.messageSenderName)
}
}
func sendData(data: NSData) {
var sendDataError: NSError?
let gameKitHelper = GameKitHelper.sharedInstance
if let multiplayerMatch = gameKitHelper.multiplayerMatch {
let success = multiplayerMatch.sendDataToAllPlayers(data, withDataMode: .Reliable, error: &sendDataError)
if !success {
if let error = sendDataError {
println("Error:\(error.localizedDescription)")
matchEnded()
}
}
}
}
The problem here is that when you create a String in Swift, it allocates a bit of memory itself, and then uses that memory to store the actual characters of the string. All that the string value really holds is some data representing a pointer to that memory and some other info (like how much memory has been allocated, so that it can be freed properly.
You can see this here:
let str = "This is quite a long string, certainly more than 24 bytes"
sizeofValue(str) // and yet this only returns 24
When you stuff variables into an NSData object, the initializer takes a pointer to the memory of the string variable that is holding those pointers, not the characters itself:
// only storing those 24 bytes, not the actual string
let data = NSData(bytes: &str, length: sizeofValue(str))
Note, the type of the bytes argument is UnsafePointer<Void>. This is an indication that you are heading into tricky territory.
Then, when you unmarshal the data at the other end, all your receiver is going to get is some pointers to random memory (sadly, memory on the other user’s device!)
If you want to put string values into an NSData object, you are going to need to marshal them first into raw data. For example, you could encode them into an array:
let data = Array(str.utf8).withUnsafeBufferPointer { buf in
NSData(bytes: buf.baseAddress, length: buf.count)
}
As it happens, since this is a common thing to want to do, there’s a method to do this directly:
let data = str.dataUsingEncoding(NSUTF8StringEncoding)
Then, to unpack the data, you can use NSString’s constructor from an NSData object:
let newStr = NSString(data: data, encoding: NSUTF8StringEncoding)
edit: if you wanted to encode more than just a string in a single NSData, you could do something along these lines… I should say, I’ve never had to do this myself so I’m in no way familiar with the standard practices for this, there could be much better techniques or helper classes/functions. Hopefully someone with more experience can edit to show how to do this properly :)
var type = MessageType.Thanks
// start the data with the type
let data = NSMutableData(bytes: &type, length: sizeofValue(type))
// then append the string
data.appendData(Array(str.utf8).withUnsafeBufferPointer { buf in
NSMutableData(bytes: buf.baseAddress, length: buf.count)
})
switch UnsafePointer<MessageType>(data.bytes).memory {
case .ILoveYou:
// ...
case .Thanks:
let str = NSString(data: data.subdataWithRange(NSMakeRange(1, data.length-1)), encoding: NSUTF8StringEncoding)
}