{"record":{"id":"c53ed86c7b548b91","repo":"grafana/k6","slug":"failed-to-decode-private-exponent-w","errorCode":null,"errorMessage":"failed to decode private exponent: %w","messagePattern":"failed to decode private exponent: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/webcrypto/jwk.go","lineNumber":358,"sourceCode":"\tif err != nil {\n\t\treturn nil, UnknownCryptoKeyType, 0, fmt.Errorf(\"failed to decode exponent: %w\", err)\n\t}\n\n\t// convert exponent to an integer\n\teInt := new(big.Int).SetBytes(eBytes).Int64()\n\tpubKey := rsa.PublicKey{\n\t\tN: new(big.Int).SetBytes(nBytes),\n\t\tE: int(eInt),\n\t}\n\n\t// if the private exponent is missing, return the public key\n\tif jwk.D == \"\" {\n\t\treturn pubKey, PublicCryptoKeyType, pubKey.N.BitLen(), nil\n\t}\n\n\tdBytes, err := base64URLDecode(jwk.D)\n\tif err != nil {\n\t\treturn nil, UnknownCryptoKeyType, 0, fmt.Errorf(\"failed to decode private exponent: %w\", err)\n\t}\n\tpBytes, err := base64URLDecode(jwk.P)\n\tif err != nil {\n\t\treturn nil, UnknownCryptoKeyType, 0, fmt.Errorf(\"failed to decode first prime factor: %w\", err)\n\t}\n\tqBytes, err := base64URLDecode(jwk.Q)\n\tif err != nil {\n\t\treturn nil, UnknownCryptoKeyType, 0, fmt.Errorf(\"failed to decode second prime factor: %w\", err)\n\t}\n\tdpBytes, err := base64URLDecode(jwk.Dp)\n\tif err != nil {\n\t\treturn nil, UnknownCryptoKeyType, 0, fmt.Errorf(\"failed to decode first exponent: %w\", err)\n\t}\n\tdqBytes, err := base64URLDecode(jwk.Dq)\n\tif err != nil {\n\t\treturn nil, UnknownCryptoKeyType, 0, fmt.Errorf(\"failed to decode second exponent: %w\", err)\n\t}\n\tqiBytes, err := base64URLDecode(jwk.Qi)","sourceCodeStart":340,"sourceCodeEnd":376,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/webcrypto/jwk.go#L340-L376","documentation":"When an RSA JWK contains a non-empty d field it is treated as a private key, and d (the private exponent) is decoded with base64.RawURLEncoding. This error means d failed that decoding — padding characters, wrong alphabet, whitespace, or an impossible length. It is raised from crypto.subtle.importKey('jwk', ...) for RSA algorithms.","triggerScenarios":"d in standard/padded base64; d containing '+' or '/'; d truncated by secret-store size limits or templating; d with embedded newlines from PEM-style wrapping.","commonSituations":"Private JWKs sourced from vaults that re-encode or wrap values; manual assembly of private JWKs from PEM dumps; secrets passed through environment variables with mangled characters.","solutions":["Convert d to unpadded base64url before import","Remove whitespace and padding from the value","Check the decoded length: for RSA-2048, d is typically 256 bytes","If d is unavailable/corrupt but only public operations are needed, drop d (and the other private fields) to import a public key"],"exampleFix":"// before\nconst jwk = { kty: 'RSA', n, e: 'AQAB', d: paddedD, p, q, dp, dq, qi };\n// after\nconst toB64u = (s) => s.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '').replace(/\\s+/g, '');\nconst jwk = { kty: 'RSA', n, e: 'AQAB', d: toB64u(paddedD), p, q, dp, dq, qi };","handlingStrategy":"validation","validationCode":"const B64URL = /^[A-Za-z0-9_-]+$/;\nif (jwk.d !== undefined && jwk.d !== '') {\n  const d = String(jwk.d).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '').replace(/\\s+/g, '');\n  if (!B64URL.test(d)) throw new Error('private exponent d is not unpadded base64url');\n  jwk = { ...jwk, d };\n}","typeGuard":"function isB64uPrivateExponent(s) {\n  return typeof s === 'string' && /^[A-Za-z0-9_-]+$/.test(s) && s.length >= 40;\n}","tryCatchPattern":"try {\n  key = await crypto.subtle.importKey('jwk', jwk, rsaAlg, true, usages);\n} catch (e) {\n  if (e.message.includes('failed to decode private exponent')) {\n    jwk = { ...jwk, d: toB64u(jwk.d) };\n    key = await crypto.subtle.importKey('jwk', jwk, rsaAlg, true, usages);\n  } else throw e;\n}","preventionTips":["Normalize d to unpadded base64url when loading secrets","If d is unusable and only public operations are needed, strip all private fields","Verify d survived secret-store transport intact (length check)"],"tags":["webcrypto","jwk","import","rsa","base64url","private-key","k6"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}