swift2 以块为单位解密媒体文件并通过AVPlayer播放

pbossiut  于 2022-11-06  发布在  Swift
关注(0)|答案(1)|浏览(146)

我有一个mp4 video file,我是encrypting保存和decrypting通过AVPlayer播放。Using CRYPTOSWIFT Library for encrypting/decrypting
它的工作很好,当我解密整个文件一次,但我的文件是相当大的,并采取100%的CPU使用率和大量的内存。所以,我需要解密加密文件块。我试图解密文件块,但它不播放视频作为AVPlayer是不承认解密块数据,也许数据是不按顺序存储,而加密文件。我已经尝试了chacha20, AES, AES.CTR & AES.CBC协议来加密和解密文件,但无济于事。

extension PlayerController: AVAssetResourceLoaderDelegate {

   func resourceLoader(resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
      let request = loadingRequest.request
      guard let path = request.URL?.path where request.URL?.scheme == Constants.customVideoScheme else { return true }
      if let contentRequest = loadingRequest.contentInformationRequest {
         do {
            let fileAttributes = try NSFileManager.defaultManager().attributesOfItemAtPath(path)
            if let fileSizeNumber = fileAttributes[NSFileSize] {
               contentRequest.contentLength = fileSizeNumber.longLongValue
            }
         } catch { }

         if fileHandle == nil {
            fileHandle = NSFileHandle(forReadingAtPath: (request.URL?.path)!)!
         }
         contentRequest.contentType = "video/mp4"
         contentRequest.byteRangeAccessSupported = true
      }

      if let data = decryptData(loadingRequest, path: path), dataRequest = loadingRequest.dataRequest {
         dataRequest.respondWithData(data)
         loadingRequest.finishLoading()
         return true
      }
      return true
   }

   func decryptData(loadingRequest: AVAssetResourceLoadingRequest, path: String) -> NSData? {
      print("Current OFFSET: \(loadingRequest.dataRequest?.currentOffset)")
      print("requested OFFSET: \(loadingRequest.dataRequest?.requestedOffset)")
      print("Current Length: \(loadingRequest.dataRequest?.requestedLength)")
      if loadingRequest.contentInformationRequest != nil {
         var data = fileHandle!.readDataOfLength((loadingRequest.dataRequest?.requestedLength)!)
         fileHandle!.seekToFileOffset(0)
         data = decodeVideoData(data)!
         return data
      } else {
         fileHandle?.seekToFileOffset(UInt64((loadingRequest.dataRequest?.currentOffset)!))
         let data = fileHandle!.readDataOfLength((loadingRequest.dataRequest?.requestedLength)!)
// let data = fileHandle!.readDataOfLength(length!)**When I use this its not playing video but play fine when try with requestedLength**
         return decodeVideoData(data)
      }
   }
}

解码nsdata的解码代码:

func decodeVideoData(data: NSData) -> NSData? {
      if let cha = ChaCha20(key: Constants.Encryption.SecretKey, iv: Constants.Encryption.IvKey) {
         let decrypted: NSData = try! data.decrypt(cha)
         return decrypted
      }
      return nil
   }

我需要关于这个问题的帮助,请指导我正确的方式来实现这一点。

yhuiod9q

yhuiod9q1#

要了解更深入和更完整的CommonCrypto Package 器,请查看my CommonCrypto wrapper
首先,我们需要定义一些函数来进行加密/解密。我假设,现在,您使用AES(256)CBC和PKCS#7填充。下面是总结代码片段:我们有一个update函数,它可以被重复调用来消耗这些块。还有一个final函数,它将 Package 任何剩余的块(通常处理填充)。

import CommonCrypto
import Foundation

enum CryptoError: Error {
    case generic(CCCryptorStatus)
}

func getOutputLength(_ reference: CCCryptorRef?, inputLength: Int, final: Bool) -> Int {
    CCCryptorGetOutputLength(reference, inputLength, final)
}

func update(_ reference: CCCryptorRef?, data: Data) throws -> Data {
    var output = [UInt8](repeating: 0, count: getOutputLength(reference, inputLength: data.count, final: false))
    let status = data.withUnsafeBytes { dataPointer -> CCCryptorStatus in
        CCCryptorUpdate(reference, dataPointer.baseAddress, data.count, &output, output.count, nil)
    }
    guard status == kCCSuccess else {
        throw CryptoError.generic(status)
    }
    return Data(output)
}

func final(_ reference: CCCryptorRef?) throws -> Data {
    var output = [UInt8](repeating: 0, count: getOutputLength(reference, inputLength: 0, final: true))
    var moved = 0
    let status = CCCryptorFinal(reference, &output, output.count, &moved)
    guard status == kCCSuccess else {
        throw CryptoError.generic(status)
    }
    output.removeSubrange(moved...)
    return Data(output)
}

接下来,为了演示的目的,加密。

let key = Data(repeating: 0x0a, count: kCCKeySizeAES256)
let iv = Data(repeating: 0, count: kCCBlockSizeAES128)
let bigFile = (0 ..< 0xffff).map { _ in
    return Data(repeating: UInt8.random(in: 0 ... UInt8.max), count: kCCBlockSizeAES128)
}.reduce(Data(), +)

var encryptor: CCCryptorRef?
CCCryptorCreate(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES), CCOptions(kCCOptionPKCS7Padding), Array(key), key.count, Array(iv), &encryptor)

do {
    let ciphertext = try update(encryptor, data: bigFile) + final(encryptor)
    print(ciphertext)    // 1048576 bytes
} catch {
    print(error)
}

在我看来,这是一个相当大的文件。现在解密,将以类似的方式完成。

var decryptor: CCCryptorRef?
CCCryptorCreate(CCOperation(kCCDecrypt), CCAlgorithm(kCCAlgorithmAES), CCOptions(kCCOptionPKCS7Padding), Array(key), key.count, Array(iv), &decryptor)

do {
    var plaintext = Data()
    for i in 0 ..< 0xffff {
        plaintext += try update(decryptor, data: ciphertext[i * kCCBlockSizeAES128 ..< i * kCCBlockSizeAES128 + kCCBlockSizeAES128])
    }
    plaintext += try final(decryptor)

    print(plaintext == bigFile, plaintext)    // true 1048560 bytes
} catch {
    print(error)
}

加密器可以针对不同的模式进行更改,并且在完成后也应该发布,我不太确定update函数的任意输出将如何表现,但这应该足以给予您了解如何使用CommonCrypto完成此操作。

相关问题