RSA 加密文件或文本

RSA encrypt file or text

Download XCode project

我加密消息并将其保存到文件中。如果我在加密文件的同时解密文件,解密成功但如果其他时间解密函数 return nil.

我用这个class来加解密。

class Crypt{

    // MARK: Public

    // MARK: Internal
    var publicKey, privateKey: SecKey?
    var publicKeyData, privateKeyData: Data?
    var statusCode: OSStatus?


    let publicKeyAttr: [NSObject: NSObject] = [
        kSecAttrIsPermanent:true as NSObject,
        kSecAttrApplicationTag:"com.aparnik.ios.books.public".data(using: String.Encoding.utf8)! as NSObject,
        kSecClass: kSecClassKey, // added this value
        kSecReturnData: kCFBooleanTrue] // added this value
    let privateKeyAttr: [NSObject: NSObject] = [
        kSecAttrIsPermanent:true as NSObject,
        kSecAttrApplicationTag:"com.aparnik.ios.books.private".data(using: String.Encoding.utf8)! as NSObject,
        kSecClass: kSecClassKey, // added this value
        kSecReturnData: kCFBooleanTrue] // added this value

    // MARK: Private

    // MARK: Initializer
    init() {
        self.generateRSAKey()
    }

    // MARK: Function
    fileprivate func generateRSAKey() {

        var keyPairAttr = [NSObject: NSObject]()
        keyPairAttr[kSecAttrKeyType] = kSecAttrKeyTypeRSA
        keyPairAttr[kSecAttrKeySizeInBits] = 1024 as NSObject
        keyPairAttr[kSecPublicKeyAttrs] = publicKeyAttr as NSObject
        keyPairAttr[kSecPrivateKeyAttrs] = privateKeyAttr as NSObject

        statusCode = SecKeyGeneratePair(keyPairAttr as CFDictionary, &publicKey, &privateKey)

        if statusCode == noErr && self.publicKey != nil && self.privateKey != nil {
            print("Key pair generated OK")
            var resultPublicKey: AnyObject?
            var resultPrivateKey: AnyObject?
            let statusPublicKey = SecItemCopyMatching(publicKeyAttr as CFDictionary, &resultPublicKey)
            let statusPrivateKey = SecItemCopyMatching(privateKeyAttr as CFDictionary, &resultPrivateKey)

            if statusPublicKey == noErr {
                if let publicKeyData = resultPublicKey as? Data {
                    self.publicKeyData = publicKeyData
//                    let publicKeyXor = xor(publicKeyData)
                    //print("Public Key: \((publicKeyData.base64EncodedString()))")
                    //print("Public Key xor: \(publicKeyXor.base64EncodedString())")

                }
            }

            if statusPrivateKey == noErr {
                if let privateKey = resultPrivateKey as? Data {
                    self.privateKeyData = privateKey
                    //print("Private Key: \((privateKey.base64EncodedString()))"
                }
            }
        } else {
            //print("Error generating key pair: \(String(describing: statusCode))")
        }
    }


    func xor() -> Data{

        var publicKeyXor: Data = Data()

            if (self.publicKeyData != nil) {

                //print("Public Key: \((publicKeyData.base64EncodedString()))")
                //print("Public Key xor: \(publicKeyXor.base64EncodedString())")
                publicKeyXor = self.publicKeyData!

                let base: Int = 53
                let length: Int = 40
                let magic: Int = 95

                for i in 0..<length{
                    let index = i + base
                    publicKeyXor[index] = self.publicKeyData![magic] ^ self.publicKeyData![index]
                }
            }


        return publicKeyXor
    }

    // decrypt
    func decryptWithRSAKey(_ encryptedData: Data, padding: SecPadding = .PKCS1, rsaKeyRef: SecKey? = nil) -> Data? {
        let rsaKeyRef = rsaKeyRef ?? self.privateKey!
        let blockSize = SecKeyGetBlockSize(rsaKeyRef)
        let dataSize = encryptedData.count / MemoryLayout<UInt8>.size

        var encryptedDataAsArray = [UInt8](repeating: 0, count: dataSize)
        (encryptedData as NSData).getBytes(&encryptedDataAsArray, length: dataSize)

        var decryptedData = [UInt8](repeating: 0, count: 0)
        var idx = 0
        while (idx < encryptedDataAsArray.count ) {
            var idxEnd = idx + blockSize
            if ( idxEnd > encryptedDataAsArray.count ) {
                idxEnd = encryptedDataAsArray.count
            }
            var chunkData = [UInt8](repeating: 0, count: blockSize)
            for i in idx..<idxEnd {
                chunkData[i-idx] = encryptedDataAsArray[i]
            }

            var decryptedDataBuffer = [UInt8](repeating: 0, count: blockSize)
            var decryptedDataLength = blockSize

            let status = SecKeyDecrypt(rsaKeyRef, padding, chunkData, idxEnd-idx, &decryptedDataBuffer, &decryptedDataLength)
            if ( status != noErr ) {
                return nil
            }
            let finalData = removePadding(decryptedDataBuffer)
            decryptedData += finalData

            idx += blockSize
        }

        return Data(bytes: UnsafePointer<UInt8>(decryptedData), count: decryptedData.count)
    }

    // remove padding
    func removePadding(_ data: [UInt8]) -> [UInt8] {
        var idxFirstZero = -1
        var idxNextZero = data.count
        for i in 0..<data.count {
            if ( data[i] == 0 ) {
                if ( idxFirstZero < 0 ) {
                    idxFirstZero = i
                } else {
                    idxNextZero = i
                    break
                }
            }
        }
        if ( idxNextZero-idxFirstZero-1 == 0 ) {
            idxNextZero = idxFirstZero
            idxFirstZero = -1
        }
        var newData = [UInt8](repeating: 0, count: idxNextZero-idxFirstZero-1)
        for i in idxFirstZero+1..<idxNextZero {
            newData[i-idxFirstZero-1] = data[i]
        }
        return newData
    }

    // encrypt
    func encryptWithRSAKey(_ data: Data, padding: SecPadding = .PKCS1, rsaKeyRef: SecKey? = nil) -> Data? {
        let rsaKeyRef = rsaKeyRef ?? self.publicKey!
        let blockSize = SecKeyGetBlockSize(rsaKeyRef)
        let dataSize = data.count / MemoryLayout<UInt8>.size
        let maxChunkSize = padding==SecPadding.OAEP ? (blockSize - 42) : (blockSize - 11)

        var dataAsArray = [UInt8](repeating: 0, count: dataSize)
        (data as NSData).getBytes(&dataAsArray, length: dataSize)

        var encryptedData = [UInt8](repeating: 0, count: 0)
        var idx = 0
        while (idx < dataAsArray.count ) {
            var idxEnd = idx + maxChunkSize
            if ( idxEnd > dataAsArray.count ) {
                idxEnd = dataAsArray.count
            }
            var chunkData = [UInt8](repeating: 0, count: maxChunkSize)
            for i in idx..<idxEnd {
                chunkData[i-idx] = dataAsArray[i]
            }

            var encryptedDataBuffer = [UInt8](repeating: 0, count: blockSize)
            var encryptedDataLength = blockSize

            let status = SecKeyEncrypt(rsaKeyRef, padding, chunkData, idxEnd-idx, &encryptedDataBuffer, &encryptedDataLength)
            if ( status != noErr ) {
                NSLog("Error while encrypting: %i", status)
                return nil
            }
            encryptedData += encryptedDataBuffer

            idx += maxChunkSize
        }

        return Data(bytes: UnsafePointer<UInt8>(encryptedData), count: encryptedData.count)
    }

}

将此消息保存到文件:

    let message = "This is my message. asdfl;jas f;lkajsdf la;skfj asd;lkfj sa;dlkfjsad ;lkfj dsal;kfj daslk;fjds flkjas dfjkdfgjkhdfs gjklsdf lkgjhdfs klgj dfskljg fdslkjg dsfjklg dfskjlg dfskljg fdskljg fdskjlgn dfsjlknv sflkdjnv ldksfjnv dfsjnvdkfjsghlfsjkdgh fdskljgh dsfkljgh dfslkjghdljkfs sdfkljsadf dsaf;lkasdjf sad;lfjk as;ldkfjas d;flkjasd flk;asdf lkjha sdflhjka sdklgha fkljgh fsdkljg alkjfh aslkjdf asldkjfh asdljkfasdlkjfhas ldfh ash aslkj asdlkj aslkjchads lkjchadslkfjhsadlkfjhsad flkjasdh flkjashdf lkjadhsf lkjasdhf lkjashdf lkjasdhf lkadsjfhadslkfjhiuwlhoewiqufhopweif asjkbdsa kjfasdlkfja sdljkfhs alkjfh adsjkfhas ldfkjhas ldkfjhajlsfh alsjdfhadlsfhlasjdkfjhsad fljkls "

        let encryptData: Data? = self.crypt.encryptWithRSAKey(message.data(using: .utf8)!)

        let fileName = "file.enc"
        let dir = try? FileManager.default.url(for: .documentDirectory,
                                               in: .userDomainMask, appropriateFor: nil, create: false)
if let fileURL = dir?.appendingPathComponent(fileName).appendingPathExtension("dgk") {

            // Write to the file Test
            do {
                //                try encry.write(to: fileURL, atomically: true, encoding: .utf8)
                try encryptData?.write(to: fileURL)
            } catch {
                print("Failed writing to URL: \(fileURL), Error: " + error.localizedDescription)
            }

        }

如果我此时解密文件,则解密成功。但是当我关闭程序并打开它并解密文件时,解密方法失败。

if let fileURL = dir?.appendingPathComponent(fileName).appendingPathExtension("dgk") {
            var encryptDataFromFile: Data?
            do {
                encryptDataFromFile = try Data(contentsOf: fileURL)
                if let decryptData: Data = self.crypt.decryptWithRSAKey(encryptDataFromFile!){
                    let decryptString: String = String(data: decryptData, encoding: .utf8)!
                    print(decryptString)
                }
                exit(0)
            } catch {
                print("Failed reading from URL: \(fileURL), Error: " + error.localizedDescription)
            }
        }

Download XCode project

更新:

  1. 每次启动应用程序都会生成一对新的密钥,因此之前加密的数据无法用新的不同的私钥解密。必须保存密钥(或至少是私钥)以备将来使用。

  2. 您通过分块加密误用了 RSA!当数据量很大,或者一般加密数据时,使用hybrid encryption。这意味着创建一个随机对称密钥,数据使用对称加密 (AES) 加密,对称密钥使用非对称加密 (RSA) 加密。两种加密方式打包在一起[=13​​=]

  3. RSA可以加密的数据大小小于密钥大小。即使对于原始 RSA,1024 位密钥也被限制为小于 127 字节。

    在代码中,密钥是 1024 ([kSecAttrKeySizeInBits] = 1024),也就是 128 字节 。考虑到 11 字节的填充,可以加密的最大数据是 116 字节。

  4. 真正的问题是为什么使用 RSA(非对称)与 AES(对称)密钥加密?

    一般不使用RSA等非对称加密来加密数据,数据一般用对称加密如AES.The来加密数据AES.The选择通常归结为需要单独的加密和解密密钥and/or 公钥簿。

    两者在可比较的密钥大小下都同样安全,而且 AES 速度更快。可比较的密钥大小:AES 128 位,RSA 3072 位。参见 NIST: Recommendation for Key Management Table 2.