{"record":{"id":"40b19bbd27bc0507","repo":"quarkusio/quarkus","slug":"invalid-public-key","errorCode":null,"errorMessage":"Invalid public key","messagePattern":"Invalid public key","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"extensions/security-webauthn/runtime/src/main/java/io/quarkus/security/webauthn/WebAuthnCredentialRecord.java","lineNumber":140,"sourceCode":"                case EC2:\n                    coseKey = EC2COSEKey.create((ECPublicKey) KeyFactory.getInstance(\"EC\").generatePublic(x509EncodedKeySpec),\n                            coseAlgorithm);\n                    break;\n                case OKP:\n                    coseKey = EdDSACOSEKey\n                            .create((EdECPublicKey) KeyFactory.getInstance(\"EdDSA\").generatePublic(x509EncodedKeySpec),\n                                    coseAlgorithm);\n                    break;\n                case RSA:\n                    coseKey = RSACOSEKey\n                            .create((RSAPublicKey) KeyFactory.getInstance(\"RSA\").generatePublic(x509EncodedKeySpec),\n                                    coseAlgorithm);\n                    break;\n                default:\n                    throw new IllegalArgumentException(\"Invalid cose algorithm: \" + coseAlgorithm);\n            }\n        } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {\n            throw new IllegalArgumentException(\"Invalid public key\", e);\n        }\n        byte[] credentialId = base64UrlDecode(persistedData.credentialId());\n        AAGUID aaguid = new AAGUID(persistedData.aaguid());\n        AttestedCredentialData attestedCredentialData = new AttestedCredentialData(aaguid, credentialId, coseKey);\n\n        return new WebAuthnCredentialRecord(persistedData.username(), counter, attestedCredentialData);\n    }\n\n    /**\n     * Record holding all the required persistent fields for logging back someone over WebAuthn.\n     */\n    public record RequiredPersistedData(\n            /**\n             * The user name. A single user name may be associated with multiple WebAuthn credentials.\n             */\n            String username,\n            /**\n             * The credential ID. This must be unique. See https://w3c.github.io/webauthn/#credential-id","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/security-webauthn/runtime/src/main/java/io/quarkus/security/webauthn/WebAuthnCredentialRecord.java#L122-L158","documentation":"During fromRequiredPersistedData, the stored X.509-encoded public key cannot be rebuilt: KeyFactory.generatePublic throws InvalidKeySpecException (corrupted/wrong-format key bytes or key does not match the algorithm) or NoSuchAlgorithmException (required KeyFactory, e.g. 'EdDSA', is unavailable). It is rethrown as IllegalArgumentException('Invalid public key').","triggerScenarios":"Calling WebAuthnCredentialRecord.fromRequiredPersistedData with a RequiredPersistedData whose publicKey byte[] is corrupted, truncated, not a valid X.509 SubjectPublicKeyInfo, or whose algorithm requires an unavailable JCA provider (e.g. EdDSA on a JDK without Ed25519 support).","commonSituations":"Database blob/column mangled by manual edits or migration; key bytes re-encoded (e.g. base64 round trip done wrong) between persist and load; running EdDSA credentials (-8) on an old JDK (<15) lacking 'EdDSA' KeyFactory; byte[] truncated by a fixed-size column.","solutions":["Re-register the WebAuthn credential so a fresh valid public key is persisted.","Verify publicKey is stored/loaded losslessly (BLOB/bytea, not a fixed-length string column) and the base64 encoding round-trips.","For EdDSA credentials, run on JDK 15+ (or with an EdDSA JCA provider installed).","Check the cause chain (getCause()) of the IllegalArgumentException to distinguish InvalidKeySpecException from NoSuchAlgorithmException."],"exampleFix":"// before: storing key as VARCHAR(64) -> truncated\n@Column(length = 64) public String publicKey;\n\n// after: lossless storage\n@Column(columnDefinition = \"bytea\") public byte[] publicKey;","handlingStrategy":"try-catch","validationCode":"// sanity-check before deserialization: non-empty, X.509 DER starts with 0x30\nbyte[] pk = persistedData.publicKey();\nif (pk == null || pk.length == 0 || (pk[0] & 0xff) != 0x30)\n    throw new IllegalStateException(\"Persisted public key is missing or not X.509 encoded\");","typeGuard":null,"tryCatchPattern":"try {\n    record = WebAuthnCredentialRecord.fromRequiredPersistedData(persistedData);\n} catch (IllegalArgumentException e) {\n    log.error(\"Stored public key unusable (cause: \" + e.getCause() + \"), re-registration required\", e);\n    throw new WebAuthnCredentialStorageException(e);\n}","preventionTips":["Store publicKey as a lossless binary column (BLOB/bytea), not a truncated VARCHAR.","Run JDK 15+ (or install an EdDSA provider) for EdDSA/OKP credentials.","Base64-url encode only for transport; decode fully before persisting.","Inspect e.getCause() to distinguish key-format vs missing-algorithm problems."],"tags":["quarkus","webauthn","runtime","public-key","jca"],"backgroundTag":"invalid-public-key","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}