美文网首页unity3D技术分享
Unity 打包 AssetBundle加密

Unity 打包 AssetBundle加密

作者: _灯下影子 | 来源:发表于2021-07-16 13:17 被阅读0次

    AssetStudio解包工具(gitHub上可下载):
    链接:https://pan.baidu.com/s/1eZpjQaqOJHnGrP9H9PZeRQ
    提取码:mbjq

    方案一
    在AssetBundle头部加入部分字节

        [MenuItem("AssetBundle/Build Encrypt OffSet")]
        static void BuildEncryptOffSet()
        {
            var exportPath = Application.streamingAssetsPath;
            if (Directory.Exists(exportPath))
            {
                Directory.CreateDirectory(exportPath);
            }
            var manifest = BuildPipeline.BuildAssetBundles(exportPath, 
                BuildAssetBundleOptions.ChunkBasedCompression, 
                EditorUserBuildSettings.activeBuildTarget);
            foreach (var abPath in manifest.GetAllAssetBundles())
            {
                string filepath = $"{Application.streamingAssetsPath}/{abPath}";
                // 偏移值可调,或者动态设置都可以
                uint offset = 96;
                if ( offset > 0)
                {
                    byte[] filedata = File.ReadAllBytes(filepath);
                    int filelen = ((int)offset + filedata.Length);
                    byte[] buffer = new byte[filelen];
                    
                    for (int slen = 0; slen < offset; slen++)
                    {
                        buffer[slen] = 1;
                    }
                    for (int slen = 0; slen < filedata.Length; slen++)
                    {
                        buffer[slen + offset] = filedata[slen];
                    }
                    FileStream fs = File.OpenWrite(filepath);
                    fs.Write(buffer, 0, filelen);
                    fs.Close();
                }
            }
        }
    

    同步加载解密

    protected AssetBundle _bundle;
    protected void AssetBundleUnCryptoSync(string assetBundlePatchFile)
      {
                uint offSet = 96;
                _bundle = AssetBundle.LoadFromFile(assetBundlePatchFile, 0,offSet);
      }
    

    异步加载解密

    protected IEnumerator AssetBundleUnCrypto(string assetBundlePatchFile)
      {
                uint offSet = 96;
                AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(assetBundlePatchFile, 0,offSet);
                yield return request;
                _bundle = request.assetBundle;
      }
    

    方案二
    全字节加密

    [MenuItem("AssetBundle/Build Encrypt")]
        static void BuildEncrypt()
        {
            var exportPath = Application.streamingAssetsPath;
            if (!Directory.Exists(exportPath))
                Directory.CreateDirectory(exportPath);
    
            var manifest = BuildPipeline.BuildAssetBundles(exportPath, BuildAssetBundleOptions.ChunkBasedCompression, EditorUserBuildSettings.activeBuildTarget);
            foreach (var name in manifest.GetAllAssetBundles())
            {
                var uniqueSalt = Encoding.UTF8.GetBytes(name);
    
                var data = File.ReadAllBytes($"{Application.streamingAssetsPath}/{name}");
                using (var baseStream = new FileStream($"{Application.streamingAssetsPath}/{name}", FileMode.OpenOrCreate))
                {
                    var cryptor = new SeekableAesStream(baseStream, "password", uniqueSalt);
                    cryptor.Write(data, 0, data.Length);
                }
            }
        }
    
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;
    using System.Security.Cryptography;
    using UnityEngine;
    
    public class SeekableAesStream : Stream
    {
        private Stream baseStream;
        private AesManaged aes;
        private ICryptoTransform encryptor;
        public bool autoDisposeBaseStream { get; set; } = true;
    
        /// <param name="salt">//** WARNING **: MUST be unique for each stream otherwise there is NO security</param>
        public SeekableAesStream(Stream baseStream, string password, byte[] salt)
        {
            this.baseStream = baseStream;
            using (var key = new PasswordDeriveBytes(password, salt))
            {
                aes = new AesManaged();
                aes.KeySize = 128;
                aes.Mode = CipherMode.ECB;
                aes.Padding = PaddingMode.None;
                aes.Key = key.GetBytes(aes.KeySize / 8);
                aes.IV = new byte[16]; //zero buffer is adequate since we have to use new salt for each stream
                encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
            }
        }
    
        private void cipher(byte[] buffer, int offset, int count, long streamPos)
        {
            //find block number
            var blockSizeInByte = aes.BlockSize / 8;
            var blockNumber = (streamPos / blockSizeInByte) + 1;
            var keyPos = streamPos % blockSizeInByte;
    
            //buffer
            var outBuffer = new byte[blockSizeInByte];
            var nonce = new byte[blockSizeInByte];
            var init = false;
    
            for (int i = offset; i < count; i++)
            {
                //encrypt the nonce to form next xor buffer (unique key)
                if (!init || (keyPos % blockSizeInByte) == 0)
                {
                    BitConverter.GetBytes(blockNumber).CopyTo(nonce, 0);
                    encryptor.TransformBlock(nonce, 0, nonce.Length, outBuffer, 0);
                    if (init) keyPos = 0;
                    init = true;
                    blockNumber++;
                }
                buffer[i] ^= outBuffer[keyPos]; //simple XOR with generated unique key
                keyPos++;
            }
        }
    
        public override bool CanRead { get { return baseStream.CanRead; } }
        public override bool CanSeek { get { return baseStream.CanSeek; } }
        public override bool CanWrite { get { return baseStream.CanWrite; } }
        public override long Length { get { return baseStream.Length; } }
        public override long Position { get { return baseStream.Position; } set { baseStream.Position = value; } }
        public override void Flush() { baseStream.Flush(); }
        public override void SetLength(long value) { baseStream.SetLength(value); }
        public override long Seek(long offset, SeekOrigin origin) { return baseStream.Seek(offset, origin); }
    
        public override int Read(byte[] buffer, int offset, int count)
        {
            var streamPos = Position;
            var ret = baseStream.Read(buffer, offset, count);
            cipher(buffer, offset, count, streamPos);
            return ret;
        }
    
        public override void Write(byte[] buffer, int offset, int count)
        {
            cipher(buffer, offset, count, Position);
            baseStream.Write(buffer, offset, count);
        }
    
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                encryptor?.Dispose();
                aes?.Dispose();
                if (autoDisposeBaseStream)
                    baseStream?.Dispose();
            }
    
            base.Dispose(disposing);
        }
    }
    
    

    同步加载解密

            protected void AssetBundleUnCryptoSync(string assetBundlePatchFile)
            {
    
    #if UNITY_EDITOR
               
                FileStream fileStream;
                fileStream = new FileStream(assetBundlePatchFile, FileMode.Open);
                var uniqueSalt1 = Encoding.UTF8.GetBytes(bundleName); // 打包时设置的salt名
                // Stream暗号化解除
                var uncryptor1 = new SeekableAesStream(fileStream, "password", uniqueSalt1);
                _bundle = AssetBundle.LoadFromStream(uncryptor1);
    #else
                var www = UnityWebRequest.Get(assetBundlePatchFile);
                www.SendWebRequest();
                // 阻塞,实现同步效果
                while(!www.isDone){
                    System.Threading.Thread.Sleep (1/30);
                }
    
                if (!(www.isHttpError || www.isNetworkError))
                {
                    var dataStream = new MemoryStream();
                    dataStream.Write(www.downloadHandler.data, 0, www.downloadHandler.data.Length);
                    var uniqueSalt = Encoding.UTF8.GetBytes(bundleName); // 打包时设置的salt名
                    // Stream暗号化解除
                    var uncryptor = new SeekableAesStream(dataStream, AssetBundlePathResolver.BundlePassword, uniqueSalt);
                    _bundle = AssetBundle.LoadFromStream(uncryptor);
                }
                else
                {
                    LuaInterface.Debugger.Log(string.Format("{1}:[{0}]",www.error,"www.error"));
                }
                
                www.Dispose();
                www = null;
    #endif
                
                this.Complete();
            }
    

    方案一,性能几乎无消耗
    方案二,性能消耗比较大
    如果对加密要求不是很高,推荐使用方案一。

    相关文章

      网友评论

        本文标题:Unity 打包 AssetBundle加密

        本文链接:https://www.haomeiwen.com/subject/fgeoultx.html