Swift - Encrypt and decrypt a string using a users password











up vote
4
down vote

favorite
4












I'm trying to encrypt a String using a password in Swift but not sure how to do it. I need something that works like this as below



let password = "password"
let message = "messageToEncrypt"
let encryptedMessage = encrypt(message, password)
...

let decryptedMessage = decrypt(encryptedMessage, password)


Any advice would be much appreciated.



Thanks



UPDATE <-- Removed this section now



UPDATE 2



Ok I now have the following:



 func testEnc() throws {

let ivKey = "tEi1H3E1aj26XNro"
let message = "Test Message"
let password = "pass123"

let aesKey = password.padding(toLength: 32, withPad: "0", startingAt: 0)

do {
let messageArray = Array(message.utf8)
let encrypted = try AES(key: aesKey, iv: ivKey, blockMode: .CBC, padding: .pkcs7).encrypt(messageArray)
let encryptedString = String.init(bytes: encrypted, encoding: .utf8)
let decrypted = try AES(key: aesKey, iv: ivKey, blockMode: .CBC, padding: .pkcs7).decrypt(encrypted)
let decryptedString = String.init(bytes: decrypted, encoding: .utf8)
assert(message == decryptedString)
} catch {
print(error)
}
}


In the above code, the message is correctly encrypted and decrypted again.



decryptedString returns the same value as message which is perfect. However I need to store the encrypted value. In the above code encryptedString always returns nil. Does an AES encrypted Array<UInt8> not class as a byte string?










share|improve this question
























  • Potentially relevant reading: stackoverflow.com/questions/27072021/aes-encrypt-and-decrypt
    – CollinD
    Sep 28 '17 at 23:05










  • I have already read that post but that is based on a key based encryption using key and iv. I need a password based method. If that is the way to go. How would I generate a key and iv given only a password?
    – Matthew Cawley
    Sep 28 '17 at 23:10












  • security.stackexchange.com/questions/38828/…
    – CollinD
    Sep 28 '17 at 23:11






  • 1




    There is no standard for what it means "to encrypt." It highly depends on what you're trying to do and whether you need to be compatible with some other piece of software. Is your code the only code that will need to decrypt this? Take a look at RNCryptor for an encryption format that works the way you're describing. github.com/RNCryptor/RNCryptor
    – Rob Napier
    Sep 29 '17 at 0:03















up vote
4
down vote

favorite
4












I'm trying to encrypt a String using a password in Swift but not sure how to do it. I need something that works like this as below



let password = "password"
let message = "messageToEncrypt"
let encryptedMessage = encrypt(message, password)
...

let decryptedMessage = decrypt(encryptedMessage, password)


Any advice would be much appreciated.



Thanks



UPDATE <-- Removed this section now



UPDATE 2



Ok I now have the following:



 func testEnc() throws {

let ivKey = "tEi1H3E1aj26XNro"
let message = "Test Message"
let password = "pass123"

let aesKey = password.padding(toLength: 32, withPad: "0", startingAt: 0)

do {
let messageArray = Array(message.utf8)
let encrypted = try AES(key: aesKey, iv: ivKey, blockMode: .CBC, padding: .pkcs7).encrypt(messageArray)
let encryptedString = String.init(bytes: encrypted, encoding: .utf8)
let decrypted = try AES(key: aesKey, iv: ivKey, blockMode: .CBC, padding: .pkcs7).decrypt(encrypted)
let decryptedString = String.init(bytes: decrypted, encoding: .utf8)
assert(message == decryptedString)
} catch {
print(error)
}
}


In the above code, the message is correctly encrypted and decrypted again.



decryptedString returns the same value as message which is perfect. However I need to store the encrypted value. In the above code encryptedString always returns nil. Does an AES encrypted Array<UInt8> not class as a byte string?










share|improve this question
























  • Potentially relevant reading: stackoverflow.com/questions/27072021/aes-encrypt-and-decrypt
    – CollinD
    Sep 28 '17 at 23:05










  • I have already read that post but that is based on a key based encryption using key and iv. I need a password based method. If that is the way to go. How would I generate a key and iv given only a password?
    – Matthew Cawley
    Sep 28 '17 at 23:10












  • security.stackexchange.com/questions/38828/…
    – CollinD
    Sep 28 '17 at 23:11






  • 1




    There is no standard for what it means "to encrypt." It highly depends on what you're trying to do and whether you need to be compatible with some other piece of software. Is your code the only code that will need to decrypt this? Take a look at RNCryptor for an encryption format that works the way you're describing. github.com/RNCryptor/RNCryptor
    – Rob Napier
    Sep 29 '17 at 0:03













up vote
4
down vote

favorite
4









up vote
4
down vote

favorite
4






4





I'm trying to encrypt a String using a password in Swift but not sure how to do it. I need something that works like this as below



let password = "password"
let message = "messageToEncrypt"
let encryptedMessage = encrypt(message, password)
...

let decryptedMessage = decrypt(encryptedMessage, password)


Any advice would be much appreciated.



Thanks



UPDATE <-- Removed this section now



UPDATE 2



Ok I now have the following:



 func testEnc() throws {

let ivKey = "tEi1H3E1aj26XNro"
let message = "Test Message"
let password = "pass123"

let aesKey = password.padding(toLength: 32, withPad: "0", startingAt: 0)

do {
let messageArray = Array(message.utf8)
let encrypted = try AES(key: aesKey, iv: ivKey, blockMode: .CBC, padding: .pkcs7).encrypt(messageArray)
let encryptedString = String.init(bytes: encrypted, encoding: .utf8)
let decrypted = try AES(key: aesKey, iv: ivKey, blockMode: .CBC, padding: .pkcs7).decrypt(encrypted)
let decryptedString = String.init(bytes: decrypted, encoding: .utf8)
assert(message == decryptedString)
} catch {
print(error)
}
}


In the above code, the message is correctly encrypted and decrypted again.



decryptedString returns the same value as message which is perfect. However I need to store the encrypted value. In the above code encryptedString always returns nil. Does an AES encrypted Array<UInt8> not class as a byte string?










share|improve this question















I'm trying to encrypt a String using a password in Swift but not sure how to do it. I need something that works like this as below



let password = "password"
let message = "messageToEncrypt"
let encryptedMessage = encrypt(message, password)
...

let decryptedMessage = decrypt(encryptedMessage, password)


Any advice would be much appreciated.



Thanks



UPDATE <-- Removed this section now



UPDATE 2



Ok I now have the following:



 func testEnc() throws {

let ivKey = "tEi1H3E1aj26XNro"
let message = "Test Message"
let password = "pass123"

let aesKey = password.padding(toLength: 32, withPad: "0", startingAt: 0)

do {
let messageArray = Array(message.utf8)
let encrypted = try AES(key: aesKey, iv: ivKey, blockMode: .CBC, padding: .pkcs7).encrypt(messageArray)
let encryptedString = String.init(bytes: encrypted, encoding: .utf8)
let decrypted = try AES(key: aesKey, iv: ivKey, blockMode: .CBC, padding: .pkcs7).decrypt(encrypted)
let decryptedString = String.init(bytes: decrypted, encoding: .utf8)
assert(message == decryptedString)
} catch {
print(error)
}
}


In the above code, the message is correctly encrypted and decrypted again.



decryptedString returns the same value as message which is perfect. However I need to store the encrypted value. In the above code encryptedString always returns nil. Does an AES encrypted Array<UInt8> not class as a byte string?







swift encryption






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Aug 23 at 8:56









Neeraj Shukla

356




356










asked Sep 28 '17 at 23:03









Matthew Cawley

1,2661530




1,2661530












  • Potentially relevant reading: stackoverflow.com/questions/27072021/aes-encrypt-and-decrypt
    – CollinD
    Sep 28 '17 at 23:05










  • I have already read that post but that is based on a key based encryption using key and iv. I need a password based method. If that is the way to go. How would I generate a key and iv given only a password?
    – Matthew Cawley
    Sep 28 '17 at 23:10












  • security.stackexchange.com/questions/38828/…
    – CollinD
    Sep 28 '17 at 23:11






  • 1




    There is no standard for what it means "to encrypt." It highly depends on what you're trying to do and whether you need to be compatible with some other piece of software. Is your code the only code that will need to decrypt this? Take a look at RNCryptor for an encryption format that works the way you're describing. github.com/RNCryptor/RNCryptor
    – Rob Napier
    Sep 29 '17 at 0:03


















  • Potentially relevant reading: stackoverflow.com/questions/27072021/aes-encrypt-and-decrypt
    – CollinD
    Sep 28 '17 at 23:05










  • I have already read that post but that is based on a key based encryption using key and iv. I need a password based method. If that is the way to go. How would I generate a key and iv given only a password?
    – Matthew Cawley
    Sep 28 '17 at 23:10












  • security.stackexchange.com/questions/38828/…
    – CollinD
    Sep 28 '17 at 23:11






  • 1




    There is no standard for what it means "to encrypt." It highly depends on what you're trying to do and whether you need to be compatible with some other piece of software. Is your code the only code that will need to decrypt this? Take a look at RNCryptor for an encryption format that works the way you're describing. github.com/RNCryptor/RNCryptor
    – Rob Napier
    Sep 29 '17 at 0:03
















Potentially relevant reading: stackoverflow.com/questions/27072021/aes-encrypt-and-decrypt
– CollinD
Sep 28 '17 at 23:05




Potentially relevant reading: stackoverflow.com/questions/27072021/aes-encrypt-and-decrypt
– CollinD
Sep 28 '17 at 23:05












I have already read that post but that is based on a key based encryption using key and iv. I need a password based method. If that is the way to go. How would I generate a key and iv given only a password?
– Matthew Cawley
Sep 28 '17 at 23:10






I have already read that post but that is based on a key based encryption using key and iv. I need a password based method. If that is the way to go. How would I generate a key and iv given only a password?
– Matthew Cawley
Sep 28 '17 at 23:10














security.stackexchange.com/questions/38828/…
– CollinD
Sep 28 '17 at 23:11




security.stackexchange.com/questions/38828/…
– CollinD
Sep 28 '17 at 23:11




1




1




There is no standard for what it means "to encrypt." It highly depends on what you're trying to do and whether you need to be compatible with some other piece of software. Is your code the only code that will need to decrypt this? Take a look at RNCryptor for an encryption format that works the way you're describing. github.com/RNCryptor/RNCryptor
– Rob Napier
Sep 29 '17 at 0:03




There is no standard for what it means "to encrypt." It highly depends on what you're trying to do and whether you need to be compatible with some other piece of software. Is your code the only code that will need to decrypt this? Take a look at RNCryptor for an encryption format that works the way you're describing. github.com/RNCryptor/RNCryptor
– Rob Napier
Sep 29 '17 at 0:03












2 Answers
2






active

oldest

votes

















up vote
9
down vote



accepted










Please see updated section below striked out section. I have left the striked out section to give context to the comments and to show how not to do for security purposes



I have worked it out using CryptoSwift



func testEnc() throws {

//has to be 16 characters
//ivKey is only hardcoded for use of this example
let ivKey = "tEi1H3E1aj26XNro"
let message = "Test Message"
let password = "pass123"

//key has to be 32 characters so we pad the password
let aesKey = password.padding(toLength: 32, withPad: "0", startingAt: 0)

let encrypted = try message.encryptToBase64(cipher: AES(key: aesKey, iv: ivKey, blockMode: .CBC, padding: .pkcs7))
//returns: beQ7u8hBGdFYqNP5z4gBGg==

let decrypted = try encrypted?.decryptBase64ToString(cipher: AES(key: aesKey, iv: ivKey, blockMode: .CBC, padding: .pkcs7))
//returns: Test Message
assert(message == decrypted)

}




UPDATE



The above methodology, while it will work, is insecure; please read comments on this answer for more information



Based on the comments and feedback, I have written a new example that uses the framework RNCryptor



To encryp and decrypt messages I use the following 2 methods.



    func encryptMessage(message: String, encryptionKey: String) throws -> String {
let messageData = message.data(using: .utf8)!
let cipherData = RNCryptor.encrypt(data: messageData, withPassword: encryptionKey)
return cipherData.base64EncodedString()
}

func decryptMessage(encryptedMessage: String, encryptionKey: String) throws -> String {

let encryptedData = Data.init(base64Encoded: encryptedMessage)!
let decryptedData = try RNCryptor.decrypt(data: encryptedData, withPassword: encryptionKey)
let decryptedString = String(data: decryptedData, encoding: .utf8)!

return decryptedString
}


In my use case I needed to be able to handle encryption and decryption based off a password that could be changed without having to re-encrypt everything.



What I did is generated a random 32 character string and encrypted that with the password. If the user changes their password, they simply decrypt the key with the old password and re-encrypt it with the new password. This ensure that all existing content can be decrypted while still being secured by the user's password.



To generate the encryption key is use the following method:



func generateEncryptionKey(withPassword password:String) throws -> String {
let randomData = RNCryptor.randomData(ofLength: 32)
let cipherData = RNCryptor.encrypt(data: randomData, withPassword: password)
return cipherData.base64EncodedString()
}


Note: You would only generate this encryption key for the user once as it would then be stored somewhere where the user can return it using their password.






share|improve this answer



















  • 1




    Note that this is incredibly insecure. You've thrown away the vast majority of the AES keyspace. Human-typable passwords are not the same thing as keys. You have to stretch passwords into keys using a KDF (typically PBKDF2, which is available in CryptoSwift). That will require generating a random salt that you will need to store with the encrypted data. This is also very insecure because you have a fixed IV. IVs can't be hard-coded like this. You need to generate a random one and pass it along with the encrypted data.
    – Rob Napier
    Sep 29 '17 at 1:24










  • Rob, can you give an example of how you can have a string that is encrypted by a user's password and decrypted using the same password? You make some valid points, but without any examples to show me the correct way of doing things, it is hard to make the adjustments. The use case of my code will be encrypt a private key so that it can be stored in a database. The private key will be paired with a public key to handle the main encryption. The private key needs to be encrypted with the user's password and decryptable using the user's password and noone else.
    – Matthew Cawley
    Sep 29 '17 at 1:31










  • Just to add, the IV will not be hard coded like it is above. I will use something such as a md5 of the user's password or some other one way encryption.
    – Matthew Cawley
    Sep 29 '17 at 1:32






  • 2




    I agree that building this well is very difficult. That's why I pointed you to RNCryptor, which does all of this for you. See github.com/RNCryptor/RNCryptor-Spec/blob/master/… for a description of the process.
    – Rob Napier
    Sep 29 '17 at 1:37








  • 2




    @MatthewCawley, also consider the case where multiple users share the same password.
    – zneak
    Sep 29 '17 at 1:44




















up vote
1
down vote













One of the most popular libraries out there is CryptoSwift (more than 4000 stars!). It provides a lot of crypto related functionality that might interest you.



For your question, I am assuming you want to do AES based encryption, and the message is small (but further down, there is a example for larger files etc.). Using the library mentioned above, the code (taken from the library's page itself--please scroll down):



do {
let aes = try AES(key: "passwordpassword", iv: "drowssapdrowssap")
let ciphertext = try aes.encrypt(Array("Nullam quis risus eget urna mollis ornare vel eu leo.".utf8))
} catch { }


One of the questions asked is how to get IV (Initialization Vector). In this example, the IV is simply the reverse of the password, which may not be a good idea in real world applications. The going principle is that the IV doesn't need to be secret. It just needs to be random, and if possible, unique. So, you can save the IV with the file, in a database with the cipher text.






share|improve this answer























  • I'm trying this but I am getting the following error Block size and Initialization Vector must be the same length! I am generating my IV by reversing the password
    – Matthew Cawley
    Sep 28 '17 at 23:50










  • "which may not be a good idea in real world applications." It is both a very bad idea and does not work unless the password is precisely one block in length. Also a password is not the same thing a key. Passing a human-typable password as an AES key is incredibly insecure (it destroys most of the benefit of AES's key size). While CryptoSwift is a nice package, it is a very low-level package. It assumes you have a strong background in order to correctly use crypto primitives, and will happily allow you to build things that are highly insecure.
    – Rob Napier
    Sep 29 '17 at 0:00










  • I am not sure why you are getting the error. I just tried this and it seems to be working. Can you post your code. (Sorry...newbee here on stackoverflow, so don't know all the bells and whistles). @RobNapier I completely agree. But I was trying to be as simple as possible, not suggesting that this is how you should do it. Key based encryption is tough--Salting, creating a (truly unique) crypto-random IV, etc is equally important. I completely agree with you there.
    – mgeek
    Sep 29 '17 at 0:27













Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f46479514%2fswift-encrypt-and-decrypt-a-string-using-a-users-password%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
9
down vote



accepted










Please see updated section below striked out section. I have left the striked out section to give context to the comments and to show how not to do for security purposes



I have worked it out using CryptoSwift



func testEnc() throws {

//has to be 16 characters
//ivKey is only hardcoded for use of this example
let ivKey = "tEi1H3E1aj26XNro"
let message = "Test Message"
let password = "pass123"

//key has to be 32 characters so we pad the password
let aesKey = password.padding(toLength: 32, withPad: "0", startingAt: 0)

let encrypted = try message.encryptToBase64(cipher: AES(key: aesKey, iv: ivKey, blockMode: .CBC, padding: .pkcs7))
//returns: beQ7u8hBGdFYqNP5z4gBGg==

let decrypted = try encrypted?.decryptBase64ToString(cipher: AES(key: aesKey, iv: ivKey, blockMode: .CBC, padding: .pkcs7))
//returns: Test Message
assert(message == decrypted)

}




UPDATE



The above methodology, while it will work, is insecure; please read comments on this answer for more information



Based on the comments and feedback, I have written a new example that uses the framework RNCryptor



To encryp and decrypt messages I use the following 2 methods.



    func encryptMessage(message: String, encryptionKey: String) throws -> String {
let messageData = message.data(using: .utf8)!
let cipherData = RNCryptor.encrypt(data: messageData, withPassword: encryptionKey)
return cipherData.base64EncodedString()
}

func decryptMessage(encryptedMessage: String, encryptionKey: String) throws -> String {

let encryptedData = Data.init(base64Encoded: encryptedMessage)!
let decryptedData = try RNCryptor.decrypt(data: encryptedData, withPassword: encryptionKey)
let decryptedString = String(data: decryptedData, encoding: .utf8)!

return decryptedString
}


In my use case I needed to be able to handle encryption and decryption based off a password that could be changed without having to re-encrypt everything.



What I did is generated a random 32 character string and encrypted that with the password. If the user changes their password, they simply decrypt the key with the old password and re-encrypt it with the new password. This ensure that all existing content can be decrypted while still being secured by the user's password.



To generate the encryption key is use the following method:



func generateEncryptionKey(withPassword password:String) throws -> String {
let randomData = RNCryptor.randomData(ofLength: 32)
let cipherData = RNCryptor.encrypt(data: randomData, withPassword: password)
return cipherData.base64EncodedString()
}


Note: You would only generate this encryption key for the user once as it would then be stored somewhere where the user can return it using their password.






share|improve this answer



















  • 1




    Note that this is incredibly insecure. You've thrown away the vast majority of the AES keyspace. Human-typable passwords are not the same thing as keys. You have to stretch passwords into keys using a KDF (typically PBKDF2, which is available in CryptoSwift). That will require generating a random salt that you will need to store with the encrypted data. This is also very insecure because you have a fixed IV. IVs can't be hard-coded like this. You need to generate a random one and pass it along with the encrypted data.
    – Rob Napier
    Sep 29 '17 at 1:24










  • Rob, can you give an example of how you can have a string that is encrypted by a user's password and decrypted using the same password? You make some valid points, but without any examples to show me the correct way of doing things, it is hard to make the adjustments. The use case of my code will be encrypt a private key so that it can be stored in a database. The private key will be paired with a public key to handle the main encryption. The private key needs to be encrypted with the user's password and decryptable using the user's password and noone else.
    – Matthew Cawley
    Sep 29 '17 at 1:31










  • Just to add, the IV will not be hard coded like it is above. I will use something such as a md5 of the user's password or some other one way encryption.
    – Matthew Cawley
    Sep 29 '17 at 1:32






  • 2




    I agree that building this well is very difficult. That's why I pointed you to RNCryptor, which does all of this for you. See github.com/RNCryptor/RNCryptor-Spec/blob/master/… for a description of the process.
    – Rob Napier
    Sep 29 '17 at 1:37








  • 2




    @MatthewCawley, also consider the case where multiple users share the same password.
    – zneak
    Sep 29 '17 at 1:44

















up vote
9
down vote



accepted










Please see updated section below striked out section. I have left the striked out section to give context to the comments and to show how not to do for security purposes



I have worked it out using CryptoSwift



func testEnc() throws {

//has to be 16 characters
//ivKey is only hardcoded for use of this example
let ivKey = "tEi1H3E1aj26XNro"
let message = "Test Message"
let password = "pass123"

//key has to be 32 characters so we pad the password
let aesKey = password.padding(toLength: 32, withPad: "0", startingAt: 0)

let encrypted = try message.encryptToBase64(cipher: AES(key: aesKey, iv: ivKey, blockMode: .CBC, padding: .pkcs7))
//returns: beQ7u8hBGdFYqNP5z4gBGg==

let decrypted = try encrypted?.decryptBase64ToString(cipher: AES(key: aesKey, iv: ivKey, blockMode: .CBC, padding: .pkcs7))
//returns: Test Message
assert(message == decrypted)

}




UPDATE



The above methodology, while it will work, is insecure; please read comments on this answer for more information



Based on the comments and feedback, I have written a new example that uses the framework RNCryptor



To encryp and decrypt messages I use the following 2 methods.



    func encryptMessage(message: String, encryptionKey: String) throws -> String {
let messageData = message.data(using: .utf8)!
let cipherData = RNCryptor.encrypt(data: messageData, withPassword: encryptionKey)
return cipherData.base64EncodedString()
}

func decryptMessage(encryptedMessage: String, encryptionKey: String) throws -> String {

let encryptedData = Data.init(base64Encoded: encryptedMessage)!
let decryptedData = try RNCryptor.decrypt(data: encryptedData, withPassword: encryptionKey)
let decryptedString = String(data: decryptedData, encoding: .utf8)!

return decryptedString
}


In my use case I needed to be able to handle encryption and decryption based off a password that could be changed without having to re-encrypt everything.



What I did is generated a random 32 character string and encrypted that with the password. If the user changes their password, they simply decrypt the key with the old password and re-encrypt it with the new password. This ensure that all existing content can be decrypted while still being secured by the user's password.



To generate the encryption key is use the following method:



func generateEncryptionKey(withPassword password:String) throws -> String {
let randomData = RNCryptor.randomData(ofLength: 32)
let cipherData = RNCryptor.encrypt(data: randomData, withPassword: password)
return cipherData.base64EncodedString()
}


Note: You would only generate this encryption key for the user once as it would then be stored somewhere where the user can return it using their password.






share|improve this answer



















  • 1




    Note that this is incredibly insecure. You've thrown away the vast majority of the AES keyspace. Human-typable passwords are not the same thing as keys. You have to stretch passwords into keys using a KDF (typically PBKDF2, which is available in CryptoSwift). That will require generating a random salt that you will need to store with the encrypted data. This is also very insecure because you have a fixed IV. IVs can't be hard-coded like this. You need to generate a random one and pass it along with the encrypted data.
    – Rob Napier
    Sep 29 '17 at 1:24










  • Rob, can you give an example of how you can have a string that is encrypted by a user's password and decrypted using the same password? You make some valid points, but without any examples to show me the correct way of doing things, it is hard to make the adjustments. The use case of my code will be encrypt a private key so that it can be stored in a database. The private key will be paired with a public key to handle the main encryption. The private key needs to be encrypted with the user's password and decryptable using the user's password and noone else.
    – Matthew Cawley
    Sep 29 '17 at 1:31










  • Just to add, the IV will not be hard coded like it is above. I will use something such as a md5 of the user's password or some other one way encryption.
    – Matthew Cawley
    Sep 29 '17 at 1:32






  • 2




    I agree that building this well is very difficult. That's why I pointed you to RNCryptor, which does all of this for you. See github.com/RNCryptor/RNCryptor-Spec/blob/master/… for a description of the process.
    – Rob Napier
    Sep 29 '17 at 1:37








  • 2




    @MatthewCawley, also consider the case where multiple users share the same password.
    – zneak
    Sep 29 '17 at 1:44















up vote
9
down vote



accepted







up vote
9
down vote



accepted






Please see updated section below striked out section. I have left the striked out section to give context to the comments and to show how not to do for security purposes



I have worked it out using CryptoSwift



func testEnc() throws {

//has to be 16 characters
//ivKey is only hardcoded for use of this example
let ivKey = "tEi1H3E1aj26XNro"
let message = "Test Message"
let password = "pass123"

//key has to be 32 characters so we pad the password
let aesKey = password.padding(toLength: 32, withPad: "0", startingAt: 0)

let encrypted = try message.encryptToBase64(cipher: AES(key: aesKey, iv: ivKey, blockMode: .CBC, padding: .pkcs7))
//returns: beQ7u8hBGdFYqNP5z4gBGg==

let decrypted = try encrypted?.decryptBase64ToString(cipher: AES(key: aesKey, iv: ivKey, blockMode: .CBC, padding: .pkcs7))
//returns: Test Message
assert(message == decrypted)

}




UPDATE



The above methodology, while it will work, is insecure; please read comments on this answer for more information



Based on the comments and feedback, I have written a new example that uses the framework RNCryptor



To encryp and decrypt messages I use the following 2 methods.



    func encryptMessage(message: String, encryptionKey: String) throws -> String {
let messageData = message.data(using: .utf8)!
let cipherData = RNCryptor.encrypt(data: messageData, withPassword: encryptionKey)
return cipherData.base64EncodedString()
}

func decryptMessage(encryptedMessage: String, encryptionKey: String) throws -> String {

let encryptedData = Data.init(base64Encoded: encryptedMessage)!
let decryptedData = try RNCryptor.decrypt(data: encryptedData, withPassword: encryptionKey)
let decryptedString = String(data: decryptedData, encoding: .utf8)!

return decryptedString
}


In my use case I needed to be able to handle encryption and decryption based off a password that could be changed without having to re-encrypt everything.



What I did is generated a random 32 character string and encrypted that with the password. If the user changes their password, they simply decrypt the key with the old password and re-encrypt it with the new password. This ensure that all existing content can be decrypted while still being secured by the user's password.



To generate the encryption key is use the following method:



func generateEncryptionKey(withPassword password:String) throws -> String {
let randomData = RNCryptor.randomData(ofLength: 32)
let cipherData = RNCryptor.encrypt(data: randomData, withPassword: password)
return cipherData.base64EncodedString()
}


Note: You would only generate this encryption key for the user once as it would then be stored somewhere where the user can return it using their password.






share|improve this answer














Please see updated section below striked out section. I have left the striked out section to give context to the comments and to show how not to do for security purposes



I have worked it out using CryptoSwift



func testEnc() throws {

//has to be 16 characters
//ivKey is only hardcoded for use of this example
let ivKey = "tEi1H3E1aj26XNro"
let message = "Test Message"
let password = "pass123"

//key has to be 32 characters so we pad the password
let aesKey = password.padding(toLength: 32, withPad: "0", startingAt: 0)

let encrypted = try message.encryptToBase64(cipher: AES(key: aesKey, iv: ivKey, blockMode: .CBC, padding: .pkcs7))
//returns: beQ7u8hBGdFYqNP5z4gBGg==

let decrypted = try encrypted?.decryptBase64ToString(cipher: AES(key: aesKey, iv: ivKey, blockMode: .CBC, padding: .pkcs7))
//returns: Test Message
assert(message == decrypted)

}




UPDATE



The above methodology, while it will work, is insecure; please read comments on this answer for more information



Based on the comments and feedback, I have written a new example that uses the framework RNCryptor



To encryp and decrypt messages I use the following 2 methods.



    func encryptMessage(message: String, encryptionKey: String) throws -> String {
let messageData = message.data(using: .utf8)!
let cipherData = RNCryptor.encrypt(data: messageData, withPassword: encryptionKey)
return cipherData.base64EncodedString()
}

func decryptMessage(encryptedMessage: String, encryptionKey: String) throws -> String {

let encryptedData = Data.init(base64Encoded: encryptedMessage)!
let decryptedData = try RNCryptor.decrypt(data: encryptedData, withPassword: encryptionKey)
let decryptedString = String(data: decryptedData, encoding: .utf8)!

return decryptedString
}


In my use case I needed to be able to handle encryption and decryption based off a password that could be changed without having to re-encrypt everything.



What I did is generated a random 32 character string and encrypted that with the password. If the user changes their password, they simply decrypt the key with the old password and re-encrypt it with the new password. This ensure that all existing content can be decrypted while still being secured by the user's password.



To generate the encryption key is use the following method:



func generateEncryptionKey(withPassword password:String) throws -> String {
let randomData = RNCryptor.randomData(ofLength: 32)
let cipherData = RNCryptor.encrypt(data: randomData, withPassword: password)
return cipherData.base64EncodedString()
}


Note: You would only generate this encryption key for the user once as it would then be stored somewhere where the user can return it using their password.







share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 9 '17 at 2:48

























answered Sep 29 '17 at 1:22









Matthew Cawley

1,2661530




1,2661530








  • 1




    Note that this is incredibly insecure. You've thrown away the vast majority of the AES keyspace. Human-typable passwords are not the same thing as keys. You have to stretch passwords into keys using a KDF (typically PBKDF2, which is available in CryptoSwift). That will require generating a random salt that you will need to store with the encrypted data. This is also very insecure because you have a fixed IV. IVs can't be hard-coded like this. You need to generate a random one and pass it along with the encrypted data.
    – Rob Napier
    Sep 29 '17 at 1:24










  • Rob, can you give an example of how you can have a string that is encrypted by a user's password and decrypted using the same password? You make some valid points, but without any examples to show me the correct way of doing things, it is hard to make the adjustments. The use case of my code will be encrypt a private key so that it can be stored in a database. The private key will be paired with a public key to handle the main encryption. The private key needs to be encrypted with the user's password and decryptable using the user's password and noone else.
    – Matthew Cawley
    Sep 29 '17 at 1:31










  • Just to add, the IV will not be hard coded like it is above. I will use something such as a md5 of the user's password or some other one way encryption.
    – Matthew Cawley
    Sep 29 '17 at 1:32






  • 2




    I agree that building this well is very difficult. That's why I pointed you to RNCryptor, which does all of this for you. See github.com/RNCryptor/RNCryptor-Spec/blob/master/… for a description of the process.
    – Rob Napier
    Sep 29 '17 at 1:37








  • 2




    @MatthewCawley, also consider the case where multiple users share the same password.
    – zneak
    Sep 29 '17 at 1:44
















  • 1




    Note that this is incredibly insecure. You've thrown away the vast majority of the AES keyspace. Human-typable passwords are not the same thing as keys. You have to stretch passwords into keys using a KDF (typically PBKDF2, which is available in CryptoSwift). That will require generating a random salt that you will need to store with the encrypted data. This is also very insecure because you have a fixed IV. IVs can't be hard-coded like this. You need to generate a random one and pass it along with the encrypted data.
    – Rob Napier
    Sep 29 '17 at 1:24










  • Rob, can you give an example of how you can have a string that is encrypted by a user's password and decrypted using the same password? You make some valid points, but without any examples to show me the correct way of doing things, it is hard to make the adjustments. The use case of my code will be encrypt a private key so that it can be stored in a database. The private key will be paired with a public key to handle the main encryption. The private key needs to be encrypted with the user's password and decryptable using the user's password and noone else.
    – Matthew Cawley
    Sep 29 '17 at 1:31










  • Just to add, the IV will not be hard coded like it is above. I will use something such as a md5 of the user's password or some other one way encryption.
    – Matthew Cawley
    Sep 29 '17 at 1:32






  • 2




    I agree that building this well is very difficult. That's why I pointed you to RNCryptor, which does all of this for you. See github.com/RNCryptor/RNCryptor-Spec/blob/master/… for a description of the process.
    – Rob Napier
    Sep 29 '17 at 1:37








  • 2




    @MatthewCawley, also consider the case where multiple users share the same password.
    – zneak
    Sep 29 '17 at 1:44










1




1




Note that this is incredibly insecure. You've thrown away the vast majority of the AES keyspace. Human-typable passwords are not the same thing as keys. You have to stretch passwords into keys using a KDF (typically PBKDF2, which is available in CryptoSwift). That will require generating a random salt that you will need to store with the encrypted data. This is also very insecure because you have a fixed IV. IVs can't be hard-coded like this. You need to generate a random one and pass it along with the encrypted data.
– Rob Napier
Sep 29 '17 at 1:24




Note that this is incredibly insecure. You've thrown away the vast majority of the AES keyspace. Human-typable passwords are not the same thing as keys. You have to stretch passwords into keys using a KDF (typically PBKDF2, which is available in CryptoSwift). That will require generating a random salt that you will need to store with the encrypted data. This is also very insecure because you have a fixed IV. IVs can't be hard-coded like this. You need to generate a random one and pass it along with the encrypted data.
– Rob Napier
Sep 29 '17 at 1:24












Rob, can you give an example of how you can have a string that is encrypted by a user's password and decrypted using the same password? You make some valid points, but without any examples to show me the correct way of doing things, it is hard to make the adjustments. The use case of my code will be encrypt a private key so that it can be stored in a database. The private key will be paired with a public key to handle the main encryption. The private key needs to be encrypted with the user's password and decryptable using the user's password and noone else.
– Matthew Cawley
Sep 29 '17 at 1:31




Rob, can you give an example of how you can have a string that is encrypted by a user's password and decrypted using the same password? You make some valid points, but without any examples to show me the correct way of doing things, it is hard to make the adjustments. The use case of my code will be encrypt a private key so that it can be stored in a database. The private key will be paired with a public key to handle the main encryption. The private key needs to be encrypted with the user's password and decryptable using the user's password and noone else.
– Matthew Cawley
Sep 29 '17 at 1:31












Just to add, the IV will not be hard coded like it is above. I will use something such as a md5 of the user's password or some other one way encryption.
– Matthew Cawley
Sep 29 '17 at 1:32




Just to add, the IV will not be hard coded like it is above. I will use something such as a md5 of the user's password or some other one way encryption.
– Matthew Cawley
Sep 29 '17 at 1:32




2




2




I agree that building this well is very difficult. That's why I pointed you to RNCryptor, which does all of this for you. See github.com/RNCryptor/RNCryptor-Spec/blob/master/… for a description of the process.
– Rob Napier
Sep 29 '17 at 1:37






I agree that building this well is very difficult. That's why I pointed you to RNCryptor, which does all of this for you. See github.com/RNCryptor/RNCryptor-Spec/blob/master/… for a description of the process.
– Rob Napier
Sep 29 '17 at 1:37






2




2




@MatthewCawley, also consider the case where multiple users share the same password.
– zneak
Sep 29 '17 at 1:44






@MatthewCawley, also consider the case where multiple users share the same password.
– zneak
Sep 29 '17 at 1:44














up vote
1
down vote













One of the most popular libraries out there is CryptoSwift (more than 4000 stars!). It provides a lot of crypto related functionality that might interest you.



For your question, I am assuming you want to do AES based encryption, and the message is small (but further down, there is a example for larger files etc.). Using the library mentioned above, the code (taken from the library's page itself--please scroll down):



do {
let aes = try AES(key: "passwordpassword", iv: "drowssapdrowssap")
let ciphertext = try aes.encrypt(Array("Nullam quis risus eget urna mollis ornare vel eu leo.".utf8))
} catch { }


One of the questions asked is how to get IV (Initialization Vector). In this example, the IV is simply the reverse of the password, which may not be a good idea in real world applications. The going principle is that the IV doesn't need to be secret. It just needs to be random, and if possible, unique. So, you can save the IV with the file, in a database with the cipher text.






share|improve this answer























  • I'm trying this but I am getting the following error Block size and Initialization Vector must be the same length! I am generating my IV by reversing the password
    – Matthew Cawley
    Sep 28 '17 at 23:50










  • "which may not be a good idea in real world applications." It is both a very bad idea and does not work unless the password is precisely one block in length. Also a password is not the same thing a key. Passing a human-typable password as an AES key is incredibly insecure (it destroys most of the benefit of AES's key size). While CryptoSwift is a nice package, it is a very low-level package. It assumes you have a strong background in order to correctly use crypto primitives, and will happily allow you to build things that are highly insecure.
    – Rob Napier
    Sep 29 '17 at 0:00










  • I am not sure why you are getting the error. I just tried this and it seems to be working. Can you post your code. (Sorry...newbee here on stackoverflow, so don't know all the bells and whistles). @RobNapier I completely agree. But I was trying to be as simple as possible, not suggesting that this is how you should do it. Key based encryption is tough--Salting, creating a (truly unique) crypto-random IV, etc is equally important. I completely agree with you there.
    – mgeek
    Sep 29 '17 at 0:27

















up vote
1
down vote













One of the most popular libraries out there is CryptoSwift (more than 4000 stars!). It provides a lot of crypto related functionality that might interest you.



For your question, I am assuming you want to do AES based encryption, and the message is small (but further down, there is a example for larger files etc.). Using the library mentioned above, the code (taken from the library's page itself--please scroll down):



do {
let aes = try AES(key: "passwordpassword", iv: "drowssapdrowssap")
let ciphertext = try aes.encrypt(Array("Nullam quis risus eget urna mollis ornare vel eu leo.".utf8))
} catch { }


One of the questions asked is how to get IV (Initialization Vector). In this example, the IV is simply the reverse of the password, which may not be a good idea in real world applications. The going principle is that the IV doesn't need to be secret. It just needs to be random, and if possible, unique. So, you can save the IV with the file, in a database with the cipher text.






share|improve this answer























  • I'm trying this but I am getting the following error Block size and Initialization Vector must be the same length! I am generating my IV by reversing the password
    – Matthew Cawley
    Sep 28 '17 at 23:50










  • "which may not be a good idea in real world applications." It is both a very bad idea and does not work unless the password is precisely one block in length. Also a password is not the same thing a key. Passing a human-typable password as an AES key is incredibly insecure (it destroys most of the benefit of AES's key size). While CryptoSwift is a nice package, it is a very low-level package. It assumes you have a strong background in order to correctly use crypto primitives, and will happily allow you to build things that are highly insecure.
    – Rob Napier
    Sep 29 '17 at 0:00










  • I am not sure why you are getting the error. I just tried this and it seems to be working. Can you post your code. (Sorry...newbee here on stackoverflow, so don't know all the bells and whistles). @RobNapier I completely agree. But I was trying to be as simple as possible, not suggesting that this is how you should do it. Key based encryption is tough--Salting, creating a (truly unique) crypto-random IV, etc is equally important. I completely agree with you there.
    – mgeek
    Sep 29 '17 at 0:27















up vote
1
down vote










up vote
1
down vote









One of the most popular libraries out there is CryptoSwift (more than 4000 stars!). It provides a lot of crypto related functionality that might interest you.



For your question, I am assuming you want to do AES based encryption, and the message is small (but further down, there is a example for larger files etc.). Using the library mentioned above, the code (taken from the library's page itself--please scroll down):



do {
let aes = try AES(key: "passwordpassword", iv: "drowssapdrowssap")
let ciphertext = try aes.encrypt(Array("Nullam quis risus eget urna mollis ornare vel eu leo.".utf8))
} catch { }


One of the questions asked is how to get IV (Initialization Vector). In this example, the IV is simply the reverse of the password, which may not be a good idea in real world applications. The going principle is that the IV doesn't need to be secret. It just needs to be random, and if possible, unique. So, you can save the IV with the file, in a database with the cipher text.






share|improve this answer














One of the most popular libraries out there is CryptoSwift (more than 4000 stars!). It provides a lot of crypto related functionality that might interest you.



For your question, I am assuming you want to do AES based encryption, and the message is small (but further down, there is a example for larger files etc.). Using the library mentioned above, the code (taken from the library's page itself--please scroll down):



do {
let aes = try AES(key: "passwordpassword", iv: "drowssapdrowssap")
let ciphertext = try aes.encrypt(Array("Nullam quis risus eget urna mollis ornare vel eu leo.".utf8))
} catch { }


One of the questions asked is how to get IV (Initialization Vector). In this example, the IV is simply the reverse of the password, which may not be a good idea in real world applications. The going principle is that the IV doesn't need to be secret. It just needs to be random, and if possible, unique. So, you can save the IV with the file, in a database with the cipher text.







share|improve this answer














share|improve this answer



share|improve this answer








edited Sep 28 '17 at 23:51

























answered Sep 28 '17 at 23:17









mgeek

113




113












  • I'm trying this but I am getting the following error Block size and Initialization Vector must be the same length! I am generating my IV by reversing the password
    – Matthew Cawley
    Sep 28 '17 at 23:50










  • "which may not be a good idea in real world applications." It is both a very bad idea and does not work unless the password is precisely one block in length. Also a password is not the same thing a key. Passing a human-typable password as an AES key is incredibly insecure (it destroys most of the benefit of AES's key size). While CryptoSwift is a nice package, it is a very low-level package. It assumes you have a strong background in order to correctly use crypto primitives, and will happily allow you to build things that are highly insecure.
    – Rob Napier
    Sep 29 '17 at 0:00










  • I am not sure why you are getting the error. I just tried this and it seems to be working. Can you post your code. (Sorry...newbee here on stackoverflow, so don't know all the bells and whistles). @RobNapier I completely agree. But I was trying to be as simple as possible, not suggesting that this is how you should do it. Key based encryption is tough--Salting, creating a (truly unique) crypto-random IV, etc is equally important. I completely agree with you there.
    – mgeek
    Sep 29 '17 at 0:27




















  • I'm trying this but I am getting the following error Block size and Initialization Vector must be the same length! I am generating my IV by reversing the password
    – Matthew Cawley
    Sep 28 '17 at 23:50










  • "which may not be a good idea in real world applications." It is both a very bad idea and does not work unless the password is precisely one block in length. Also a password is not the same thing a key. Passing a human-typable password as an AES key is incredibly insecure (it destroys most of the benefit of AES's key size). While CryptoSwift is a nice package, it is a very low-level package. It assumes you have a strong background in order to correctly use crypto primitives, and will happily allow you to build things that are highly insecure.
    – Rob Napier
    Sep 29 '17 at 0:00










  • I am not sure why you are getting the error. I just tried this and it seems to be working. Can you post your code. (Sorry...newbee here on stackoverflow, so don't know all the bells and whistles). @RobNapier I completely agree. But I was trying to be as simple as possible, not suggesting that this is how you should do it. Key based encryption is tough--Salting, creating a (truly unique) crypto-random IV, etc is equally important. I completely agree with you there.
    – mgeek
    Sep 29 '17 at 0:27


















I'm trying this but I am getting the following error Block size and Initialization Vector must be the same length! I am generating my IV by reversing the password
– Matthew Cawley
Sep 28 '17 at 23:50




I'm trying this but I am getting the following error Block size and Initialization Vector must be the same length! I am generating my IV by reversing the password
– Matthew Cawley
Sep 28 '17 at 23:50












"which may not be a good idea in real world applications." It is both a very bad idea and does not work unless the password is precisely one block in length. Also a password is not the same thing a key. Passing a human-typable password as an AES key is incredibly insecure (it destroys most of the benefit of AES's key size). While CryptoSwift is a nice package, it is a very low-level package. It assumes you have a strong background in order to correctly use crypto primitives, and will happily allow you to build things that are highly insecure.
– Rob Napier
Sep 29 '17 at 0:00




"which may not be a good idea in real world applications." It is both a very bad idea and does not work unless the password is precisely one block in length. Also a password is not the same thing a key. Passing a human-typable password as an AES key is incredibly insecure (it destroys most of the benefit of AES's key size). While CryptoSwift is a nice package, it is a very low-level package. It assumes you have a strong background in order to correctly use crypto primitives, and will happily allow you to build things that are highly insecure.
– Rob Napier
Sep 29 '17 at 0:00












I am not sure why you are getting the error. I just tried this and it seems to be working. Can you post your code. (Sorry...newbee here on stackoverflow, so don't know all the bells and whistles). @RobNapier I completely agree. But I was trying to be as simple as possible, not suggesting that this is how you should do it. Key based encryption is tough--Salting, creating a (truly unique) crypto-random IV, etc is equally important. I completely agree with you there.
– mgeek
Sep 29 '17 at 0:27






I am not sure why you are getting the error. I just tried this and it seems to be working. Can you post your code. (Sorry...newbee here on stackoverflow, so don't know all the bells and whistles). @RobNapier I completely agree. But I was trying to be as simple as possible, not suggesting that this is how you should do it. Key based encryption is tough--Salting, creating a (truly unique) crypto-random IV, etc is equally important. I completely agree with you there.
– mgeek
Sep 29 '17 at 0:27




















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f46479514%2fswift-encrypt-and-decrypt-a-string-using-a-users-password%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Schultheiß

Verwaltungsgliederung Dänemarks

Liste der Kulturdenkmale in Wilsdruff