Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift - Encrypt and decrypt a string using a users password

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

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

let decryptedMessage = decrypt(encryptedMessage, password)

Any advice would be much appreciated.

Thanks

UPDATE

Based on the idea below i have the following method

func testEnc() throws {
    
    let ivKey = "tEi1H3E1aj26XNro"
    let message = "Test Message"
    let password = "pass123"
    
    let aesKey = password.padding(toLength: 32, withPad: "0", startingAt: 0)
    
    let aes = try AES(key: aesKey, iv: ivKey)
    let cipherBytes: Array<UInt8> = try aes.encrypt(Array(message.utf8))
    
    let cipherData = NSData(bytes: cipherBytes, length: Int(cipherBytes.count))
    let cipherString = cipherData.base64EncodedString(options: .lineLength64Characters)
    //cipherString => beQ7u8hBGdFYqNP5z4gBGg==
    let decryptedCipherBytes = try aes.decrypt(Array(cipherString.utf8))
    let decryptedCipherData = NSData(bytes: decryptedCipherBytes, length: Int(cipherBytes.count))
    let decryptedCipherString = decryptedCipherData.base64EncodedString(options: .lineLength64Characters)
    
    assert(message == decryptedCipherString)
}

at the line

    let decryptedCipherBytes = try aes.decrypt(Array(cipherString.utf8))

I am getting the following error:

[CryptoSwift.AES.Error: dataPaddingRequired]
Conform 'CryptoSwift.AES.Error' to Debugging.Debuggable to provide more debug information.

Do you have any idea why it would not be able to decrypt the data that it has just encrypted?

Thanks

like image 949
Matthew Cawley Avatar asked Sep 28 '17 23:09

Matthew Cawley


People also ask

How to decrypt connection strings encrypted by IIS servers?

The method below uses the default key provider The method above won’t work for web farms because IIS servers won’t be able to decrypt the connection string encrypted by each other. You need to create and use an RSA key along with the RSA key provider so all servers can have the same key for decryption.

Can I use an encrypted password in a connection string?

A typical .Net framework connection string can be like the following: The challenge here is that we cannot use an encrypted password in the connection string, meaning in the PS script when I compose the connection string, I have to use the plain text value of the password.

Is there a way to decrypt a password?

You can only decrypt it if you have the password. I know it's very simple and not a lot of code, but if you don't know much about encryption and you need it, this class will help you a lot! This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Why do I need to generate an encryption key for users?

This ensure that all existing content can be decrypted while still being secured by the user's password. 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. Show activity on this post.


1 Answers

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.

like image 84
Matthew Cawley Avatar answered Mar 05 '23 12:03

Matthew Cawley