关于Laravel项目DecryptException:The MAC is invalid问题

当你做Laravel项目做调整时,或者项目发布时可能使用php artisan key:generate命令重新生成Key。如果你的项目中有使用Laravel中的encrypt加密算法,就会报如下错误:

DecryptException:The MAC is invalid

出现此问题的原因就是Laravel项目中的encrypt加密算法都有使用php artisan key:generate命令生成加密密钥,具体细节如下:

在Laravel框架中的vendor/laravel/framework/src/Illuminate/Foundation/helpers.php文件中定义了一个encrypt方法,具体代码如下:

if (! function_exists('encrypt')) {
    /**
     * Encrypt the given value.
     *
     * @param  string  $value
     * @return string
     */
    function encrypt($value)
    {
        //app()方法的作用是生成encrypter类实例并调用encrypt方法生成加密值
        return app('encrypter')->encrypt($value);
    }
}

同样在vendor/laravel/framework/src/Illuminate/Foundation/helpers.php文件中还定义了一个app方法,此方法用于获得系统服务类实例,具体代码如下:

if (! function_exists('app')) {
    /**
     * Get the available container instance.
     *
     * @param  string  $abstract
     * @return mixed|\Illuminate\Foundation\Application
     */
    function app($abstract = null)
    {
        if (is_null($abstract)) {
            return Container::getInstance();
        }

        return Container::getInstance()->make($abstract);
    }
}

接下来我们来看下encrypt方法是如何定义的,在vendor/laravel/framework/src/Illuminate/Encryption/Encrypter.php文件中定义如下:

    public function encrypt($value, $serialize = true)
    {
        $iv = random_bytes(16);

        // First we will encrypt the value using OpenSSL. After this is encrypted we
        // will proceed to calculating a MAC for the encrypted value so that this
        // value can be verified later as not having been changed by the users.
        // 注意此处的$this->key就是我们加密所使用密钥
        $value = \openssl_encrypt(
            $serialize ? serialize($value) : $value,
            $this->cipher, $this->key, 0, $iv
        );

        if ($value === false) {
            throw new EncryptException('Could not encrypt the data.');
        }

        // Once we have the encrypted value we will go ahead base64_encode the input
        // vector and create the MAC for the encrypted value so we can verify its
        // authenticity. Then, we'll JSON encode the data in a "payload" array.
        $mac = $this->hash($iv = base64_encode($iv), $value);

        $json = json_encode(compact('iv', 'value', 'mac'));

        if (! is_string($json)) {
            throw new EncryptException('Could not encrypt the data.');
        }

        return base64_encode($json);
    }

那上边的$this->key的值是怎么来的呢?在Encrypter类文件中有一个构造方法为此属于赋值为key:generate生成的key值,也就是说当我们实例化Encrypter类时会传入key值,构造方法定义如下:

/**
     * Create a new encrypter instance.
     *
     * @param  string  $key
     * @param  string  $cipher
     * @return void
     *
     * @throws \RuntimeException
     */
    public function __construct($key, $cipher = 'AES-128-CBC')
    {
        $key = (string) $key;

        if (static::supported($key, $cipher)) {
            $this->key = $key;
            $this->cipher = $cipher;
        } else {
            throw new RuntimeException('The only supported ciphers are AES-128-CBC and AES-256-CBC with the correct key lengths.');
        }
    }

上边有说过Encrypter类是借助上边的app方法来进行实例化的,app方法中使用container来返回具体实例,其实这个过程是Laravel注册服务的过程,Encrypt服务的注册是在vendor/laravel/framework/src/Illuminate/Encryption/EncryptionServiceProvider.php类文件中完成,具体代码如下:

    public function register()
    {
        $this->app->singleton('encrypter', function ($app) {
            // 加载app配置文件
            $config = $app->make('config')->get('app');

            // If the key starts with "base64:", we will need to decode the key before handing
            // it off to the encrypter. Keys may be base-64 encoded for presentation and we
            // want to make sure to convert them back to the raw bytes before encrypting.
            // 配置文件中的key值和cipher值
            if (Str::startsWith($key = $config['key'], 'base64:')) {
                $key = base64_decode(substr($key, 7));
            }

            // 实例化Encrypter类 并将配置文件中的key值和cipher值传递给构造方法进行加密使用
            return new Encrypter($key, $config['cipher']);
        });
    }

app.php配置文件中配置的key值和cipher值配置如下:

/*
    |--------------------------------------------------------------------------
    | Encryption Key
    |--------------------------------------------------------------------------
    |
    | This key is used by the Illuminate encrypter service and should be set
    | to a random, 32 character string, otherwise these encrypted strings
    | will not be safe. Please do this before deploying an application!
    |
    */
    
    // 此外读取的是.env环境文件中的app_key值 app_key由php artisan key:generate生成
    'key' => env('APP_KEY'),

    'cipher' => ‘AES-256-CBC’,

 通过以上的代码分析我们了解了encrypt加密数据均和key:generate生成的密钥有关,那如何来解决上边所报的错误问题呢?有的人说将数据库中的所有加密数据清空,重新使用migrate命令迁移数据,这样做也算是比较快捷的方法,但是如果数据表中的加密数据不多,并且你还没有来得及对数据进行备份,个人建议使用Laravel中的encrypt方法将数据进行加密,然后将加密的数据重新更新到数据库中就可以解决上边所出现的问题了。

COMMENTS

@

双哥PHP-一个致力于分享世界上最好的语言的网站

2013-2017©SGPHP