2048, 'private_key_type' => OPENSSL_KEYTYPE_RSA, ]); if ($key === false || ! openssl_pkey_export($key, $privateKey)) { throw new RuntimeException('Unable to generate channel encryption private key.'); } $details = openssl_pkey_get_details($key); $publicKey = is_array($details) ? ($details['key'] ?? null) : null; if (! is_string($publicKey) || trim($publicKey) === '') { throw new RuntimeException('Unable to generate channel encryption public key.'); } return [ 'public_key' => trim($publicKey), 'private_key' => trim($privateKey), ]; } public static function encrypt(string $plaintext, string $publicKey): string { if (! openssl_public_encrypt($plaintext, $ciphertext, $publicKey, OPENSSL_PKCS1_OAEP_PADDING)) { throw new RuntimeException('Unable to encrypt channel entry parameter.'); } return self::base64UrlEncode($ciphertext); } public static function decrypt(string $ciphertext, string $privateKey): string { $decoded = self::base64UrlDecode($ciphertext); if ($decoded === null || ! openssl_private_decrypt($decoded, $plaintext, $privateKey, OPENSSL_PKCS1_OAEP_PADDING)) { throw new RuntimeException('Unable to decrypt channel entry parameter.'); } return trim($plaintext); } private static function base64UrlEncode(string $value): string { return rtrim(strtr(base64_encode($value), '+/', '-_'), '='); } private static function base64UrlDecode(string $value): ?string { if ($value === '' || ! preg_match('/^[A-Za-z0-9_-]+$/', $value)) { return null; } $padding = strlen($value) % 4; if ($padding !== 0) { $value .= str_repeat('=', 4 - $padding); } $decoded = base64_decode(strtr($value, '-_', '+/'), true); return $decoded === false ? null : $decoded; } }