{"record":{"id":"69fcb3d9e4cbeb2b","repo":"grafana/k6","slug":"failed-to-validate-private-key-w","errorCode":null,"errorMessage":"failed to validate private key: %w","messagePattern":"failed to validate private key: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/webcrypto/jwk.go","lineNumber":397,"sourceCode":"\t}\n\n\tprivKey := &rsa.PrivateKey{\n\t\tPublicKey: pubKey,\n\t\tD:         new(big.Int).SetBytes(dBytes),\n\t\tPrimes: []*big.Int{\n\t\t\tnew(big.Int).SetBytes(pBytes),\n\t\t\tnew(big.Int).SetBytes(qBytes),\n\t\t},\n\t\tPrecomputed: rsa.PrecomputedValues{\n\t\t\tDp:   new(big.Int).SetBytes(dpBytes),\n\t\t\tDq:   new(big.Int).SetBytes(dqBytes),\n\t\t\tQinv: new(big.Int).SetBytes(qiBytes),\n\t\t},\n\t}\n\n\terr = privKey.Validate()\n\tif err != nil {\n\t\treturn nil, UnknownCryptoKeyType, 0, fmt.Errorf(\"failed to validate private key: %w\", err)\n\t}\n\n\treturn privKey, PrivateCryptoKeyType, pubKey.N.BitLen(), nil\n}\n\nfunc exportRSAJWK(key *CryptoKey) (any, error) {\n\texported := &JsonWebKey{}\n\texported.Set(\"kty\", \"RSA\")\n\n\tswitch rsaKey := key.handle.(type) {\n\tcase *rsa.PrivateKey:\n\t\texported.Set(\"n\", base64URLEncode(rsaKey.N.Bytes()))\n\t\texported.Set(\"e\", base64URLEncode(big.NewInt(int64(rsaKey.E)).Bytes()))\n\t\texported.Set(\"d\", base64URLEncode(rsaKey.D.Bytes()))\n\t\texported.Set(\"p\", base64URLEncode(rsaKey.Primes[0].Bytes()))\n\t\texported.Set(\"q\", base64URLEncode(rsaKey.Primes[1].Bytes()))\n\t\texported.Set(\"dp\", base64URLEncode(rsaKey.Precomputed.Dp.Bytes()))\n\t\texported.Set(\"dq\", base64URLEncode(rsaKey.Precomputed.Dq.Bytes()))","sourceCodeStart":379,"sourceCodeEnd":415,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/webcrypto/jwk.go#L379-L415","documentation":"After decoding all members, k6 reconstructs a Go rsa.PrivateKey and calls Validate(), which verifies mathematical consistency: p*q == n, 0 < dp/dq/qi, d*e == 1 mod lambda(n), and CRT relations. This error means the fields decoded fine as base64url but do not form a valid RSA private key together.","triggerScenarios":"crypto.subtle.importKey('jwk', ...) where one of d, p, q, dp, dq, qi is empty or zero (missing members decode to the empty string, then to big.Int 0), or where the values belong to a different key / were reordered. Exporting a JWK and editing members by hand also lands here.","commonSituations":"Stripping CRT params (dp/dq/qi) from the JWK assuming they are optional; copying p and q from a different keypair than n; swapping dp and dq; trimming leading zero bytes inconsistently; a JWK pretty-printer mangling values; using a public JWK (no d/p/q) where earlier decode errors sometimes mask this one.","solutions":["Round-trip the key: import the original PEM/PKCS8 with importKey('pkcs8', ...), then exportKey('jwk') to get a guaranteed-consistent JWK","Include ALL members kty, n, e, d, p, q, dp, dq, qi from the same key, byte-for-byte","If CRT params are genuinely unavailable, import via PKCS8 PEM which lets Go recompute them, instead of a hand-built JWK","Check that each member keeps its leading zero bytes exactly as exported (do not strip '0x00' prefixes yourself)","Report/re-test after fixing: the error text after the colon names the exact failed relation (e.g. 'crypto/rsa: invalid prime' or consistency check)"],"exampleFix":"// before: hand-built JWK missing CRT params\nconst jwk = { kty: 'RSA', n, e, d, p, q }; // dp/dq/qi absent -> zero -> Validate fails\nconst key = await crypto.subtle.importKey('jwk', jwk, alg, true, ['decrypt']);\n\n// after: import PKCS8 PEM and let k6 derive/reuse valid CRT values\nconst key = await crypto.subtle.importKey('pkcs8', pemBytes, alg, true, ['decrypt']);\nconst fullJwk = await crypto.subtle.exportKey('jwk', key); // now complete & consistent","handlingStrategy":"try-catch","validationCode":"function jwkLooksComplete(jwk) {\n  return ['kty','n','e','d','p','q','dp','dq','qi'].every(k => typeof jwk[k] === 'string' && jwk[k].length > 0);\n}\nif (!jwkLooksComplete(jwk)) throw new Error('JWK missing private/CRT members; use exportKey output');","typeGuard":"const isCompleteRsaPrivateJwk = j => j.kty === 'RSA' && ['n','e','d','p','q','dp','dq','qi'].every(k => typeof j[k] === 'string' && j[k].length > 0);","tryCatchPattern":"try { key = await crypto.subtle.importKey('jwk', jwk, alg, true, usages); }\ncatch (e) { console.error('RSA JWK inconsistent:', e.message, '- re-export from the source key'); throw e; }","preventionTips":["Never hand-edit or trim JWK members, including leading zero bytes","Import via PKCS8/SPKI PEM when CRT params may be absent; Go recomputes them","Round-trip once (import then export) to prove a key is usable before load tests depend on it","Keep p, q, dp, dq, qi from the same keypair — mixing keys guarantees Validate failure"],"tags":["webcrypto","rsa","jwk","crypto","validation"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}