encryptWithSecret($value, $this->systemDatabasePassword()); } public function encryptWithSecret(string $value, string $secret): string { if ($value === '') { throw new RuntimeException('云数据库密码不能为空。'); } $this->assertSecret($secret); $salt = random_bytes(16); $iv = random_bytes(12); $tag = ''; $encrypted = openssl_encrypt( $value, self::CIPHER, $this->deriveKey($salt, $secret), OPENSSL_RAW_DATA, $iv, $tag, self::AAD, 16 ); if (! is_string($encrypted) || strlen($tag) !== 16) { throw new RuntimeException('云数据库密码加密失败。'); } $payload = json_encode([ 'salt' => base64_encode($salt), 'iv' => base64_encode($iv), 'tag' => base64_encode($tag), 'value' => base64_encode($encrypted), ], JSON_THROW_ON_ERROR); return self::PREFIX . base64_encode($payload); } public function decrypt(string $payload): string { return $this->decryptWithSecret($payload, $this->systemDatabasePassword()); } public function decryptWithSecret(string $payload, string $secret): string { if (! str_starts_with($payload, self::PREFIX)) { throw new DecryptException('共享云数据库密码格式不受支持。'); } $this->assertSecret($secret); try { $encoded = substr($payload, strlen(self::PREFIX)); $json = base64_decode($encoded, true); $data = is_string($json) ? json_decode($json, true, 16, JSON_THROW_ON_ERROR) : null; if (! is_array($data)) { throw new RuntimeException('invalid payload'); } $salt = base64_decode((string) ($data['salt'] ?? ''), true); $iv = base64_decode((string) ($data['iv'] ?? ''), true); $tag = base64_decode((string) ($data['tag'] ?? ''), true); $encrypted = base64_decode((string) ($data['value'] ?? ''), true); if ( ! is_string($salt) || strlen($salt) !== 16 || ! is_string($iv) || strlen($iv) !== 12 || ! is_string($tag) || strlen($tag) !== 16 || ! is_string($encrypted) || $encrypted === '' ) { throw new RuntimeException('invalid payload'); } $decrypted = openssl_decrypt( $encrypted, self::CIPHER, $this->deriveKey($salt, $secret), OPENSSL_RAW_DATA, $iv, $tag, self::AAD ); if (! is_string($decrypted) || $decrypted === '') { throw new RuntimeException('invalid credential'); } return $decrypted; } catch (DecryptException $exception) { throw $exception; } catch (\Throwable) { throw new DecryptException('系统数据库凭据无法解锁共享云数据库密码。'); } } private function deriveKey(string $salt, string $secret): string { return hash_hkdf('sha256', $secret, 32, self::AAD, $salt); } private function systemDatabasePassword(): string { $connection = (string) config('database.default', 'mysql'); $password = (string) config('database.connections.' . $connection . '.password', ''); $this->assertSecret($password); return $password; } private function assertSecret(string $secret): void { if ($secret === '') { throw new RuntimeException('系统数据库密码不可用,无法处理共享云数据库密码。'); } } }