Swift - Encrypt and decrypt a string using a users

2020-05-19 03:22发布

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?

2条回答
放我归山
2楼-- · 2020-05-19 03:40

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.

查看更多
甜甜的少女心
3楼-- · 2020-05-19 03:49

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.

查看更多
登录 后发表回答