{"record":{"id":"8420d005b7c7ea49","repo":"passbolt/passbolt_api","slug":"e-getmessage-from-subscriptionsignatureexception","errorCode":null,"errorMessage":"$e->getMessage() (from SubscriptionSignatureException)","messagePattern":"\\$e->getMessage\\(\\) \\(from SubscriptionSignatureException\\)","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"plugins/PassboltCe/Edition/src/Controller/EditionSubscriptionsCreateController.php","lineNumber":53,"sourceCode":"\n    /**\n     * @return void\n     */\n    public function create(): void\n    {\n        $this->User->assertIsAdmin();\n\n        $keyString = $this->getRequest()->getData('data');\n        if (!is_string($keyString) || trim($keyString) === '') {\n            throw new BadRequestException(__('Subscription key data is required.'));\n        }\n\n        $this->assertNotAlreadyPro();\n\n        try {\n            $keyDto = (new EditionUpgradeService())->upgrade($keyString, $this->User->getAccessControl());\n        } catch (SubscriptionSignatureException $e) {\n            throw new BadRequestException($e->getMessage());\n        } catch (SubscriptionException $e) {\n            throw new PaymentRequiredException($e->getMessage(), $e->getErrors());\n        }\n\n        $this->success(__('The subscription was created.'), $keyDto->toArray());\n    }\n\n    /**\n     * Rejects with HTTP 409 if the instance is already on PRO or already has a\n     * persisted subscription row.\n     *\n     * @return void\n     * @throws \\Cake\\Http\\Exception\\ConflictException\n     */\n    private function assertNotAlreadyPro(): void\n    {\n        if ((new EditionGetService())->get()->isPro()) {\n            throw new ConflictException(__('The instance is already on PRO.'));","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltCe/Edition/src/Controller/EditionSubscriptionsCreateController.php#L35-L71","documentation":"When EditionUpgradeService::upgrade() throws SubscriptionSignatureException (the subscription key's signature could not be verified), the controller rethrows it as BadRequestException carrying the original message. It means the supplied key fails cryptographic validation against passbolt's signing keys.","triggerScenarios":"Upgrading with a subscription key whose signature is invalid, tampered, corrupted in transit, or signed for a different edition/domain.","commonSituations":"Key file truncated or line-wrapped by copy/paste or email; key issued for a different passbolt instance or domain; using a CE key where PRO is required; expired or revoked key.","solutions":["Re-copy the subscription key exactly as provided, without added whitespace or line breaks","Verify the key is issued for this instance (correct domain/subscription id)","Contact passbolt support to obtain a valid signed key","Check the key file was not modified after download"],"exampleFix":"// before\n$data = file_get_contents('key.asc'); // includes email quote wrapping -> invalid signature\n// after\n$data = trim(file_get_contents('key.asc')); // exact, unmodified key string","handlingStrategy":"try-catch","validationCode":"// pre-check: key is an unmodified ASCII armored string, no extra quotes/line wrapping\nif (preg_match('/[^[:print:][:space:]]/', $key)) { /* reject corrupted key */ }","typeGuard":"function looksLikeSignedKey(string $s): bool { return str_contains($s, '-----BEGIN') && trim($s) === $s; }","tryCatchPattern":"try {\n  await api.post('/edition/subscriptions', {data: key});\n} catch (e) {\n  if (e.status === 400) { /* signature invalid: re-copy key exactly, verify with provider */ }\n}","preventionTips":["Transfer key files as-is (no email/paste round-trips)","Verify checksums of key files after download","Confirm the key matches your instance domain/edition before import"],"tags":["signature","subscription","crypto"],"backgroundTag":"checksum-mismatch","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T09:17:21.228Z"}