secretKey = hash('sha256', $key, true); $ivLength = openssl_cipher_iv_length($this->cipherMethod); if ($ivLength === false) { throw new RuntimeException('不支持的加密算法:' . $this->cipherMethod); } $this->ivLength = $ivLength; } /** * 加密后入库;空串不加密,已是密文则原样返回避免二次加密 * * @param string|null $data 明文 * @throws Exception */ public function encrypt($data): string { if ($data === null || $data === '') { return ''; } $data = (string) $data; if (self::isEncrypted($data)) { return $data; } $iv = openssl_random_pseudo_bytes($this->ivLength); if ($iv === false) { throw new RuntimeException('生成 IV 失败'); } $encryptedData = openssl_encrypt($data, $this->cipherMethod, $this->secretKey, OPENSSL_RAW_DATA, $iv); if ($encryptedData === false) { throw new RuntimeException('加密失败:' . (openssl_error_string() ?: 'unknown')); } return self::PREFIX . base64_encode($iv . $encryptedData); } /** * 从库内密文解密;无已知前缀视为明文原样返回 * * @param string|null $base64EncodedEncryptedData 库内值 * @throws Exception */ public function decrypt($base64EncodedEncryptedData): string { if ($base64EncodedEncryptedData === null || $base64EncodedEncryptedData === '') { return ''; } $base64EncodedEncryptedData = (string) $base64EncodedEncryptedData; $prefix = self::detectPrefix($base64EncodedEncryptedData); if ($prefix === null) { return $base64EncodedEncryptedData; } $payload = substr($base64EncodedEncryptedData, strlen($prefix)); $decodedData = base64_decode($payload, true); if ($decodedData === false) { throw new RuntimeException('Base64 解码失败'); } if (strlen($decodedData) <= $this->ivLength) { throw new RuntimeException('密文长度非法'); } $iv = substr($decodedData, 0, $this->ivLength); $encryptedData = substr($decodedData, $this->ivLength); $decryptedData = openssl_decrypt($encryptedData, $this->cipherMethod, $this->secretKey, OPENSSL_RAW_DATA, $iv); if ($decryptedData === false) { throw new RuntimeException('解密失败:' . (openssl_error_string() ?: 'unknown')); } return $decryptedData; } /** * 是否已是库内密文(含历史前缀) */ public static function isEncrypted(?string $value): bool { return self::detectPrefix($value) !== null; } /** * 识别密文前缀;新前缀优先 */ public static function detectPrefix(?string $value): ?string { if (!is_string($value) || $value === '') { return null; } if (str_starts_with($value, self::PREFIX)) { return self::PREFIX; } if (str_starts_with($value, self::LEGACY_PREFIX)) { return self::LEGACY_PREFIX; } return null; } }