grafana/k6 · error

failed to validate private key: %w

Error message

failed to validate private key: %w

What it means

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.

Source

Thrown at internal/js/modules/k6/webcrypto/jwk.go:397

	}

	privKey := &rsa.PrivateKey{
		PublicKey: pubKey,
		D:         new(big.Int).SetBytes(dBytes),
		Primes: []*big.Int{
			new(big.Int).SetBytes(pBytes),
			new(big.Int).SetBytes(qBytes),
		},
		Precomputed: rsa.PrecomputedValues{
			Dp:   new(big.Int).SetBytes(dpBytes),
			Dq:   new(big.Int).SetBytes(dqBytes),
			Qinv: new(big.Int).SetBytes(qiBytes),
		},
	}

	err = privKey.Validate()
	if err != nil {
		return nil, UnknownCryptoKeyType, 0, fmt.Errorf("failed to validate private key: %w", err)
	}

	return privKey, PrivateCryptoKeyType, pubKey.N.BitLen(), nil
}

func exportRSAJWK(key *CryptoKey) (any, error) {
	exported := &JsonWebKey{}
	exported.Set("kty", "RSA")

	switch rsaKey := key.handle.(type) {
	case *rsa.PrivateKey:
		exported.Set("n", base64URLEncode(rsaKey.N.Bytes()))
		exported.Set("e", base64URLEncode(big.NewInt(int64(rsaKey.E)).Bytes()))
		exported.Set("d", base64URLEncode(rsaKey.D.Bytes()))
		exported.Set("p", base64URLEncode(rsaKey.Primes[0].Bytes()))
		exported.Set("q", base64URLEncode(rsaKey.Primes[1].Bytes()))
		exported.Set("dp", base64URLEncode(rsaKey.Precomputed.Dp.Bytes()))
		exported.Set("dq", base64URLEncode(rsaKey.Precomputed.Dq.Bytes()))

View on GitHub (pinned to 93accf6570)

Solutions

  1. Round-trip the key: import the original PEM/PKCS8 with importKey('pkcs8', ...), then exportKey('jwk') to get a guaranteed-consistent JWK
  2. Include ALL members kty, n, e, d, p, q, dp, dq, qi from the same key, byte-for-byte
  3. If CRT params are genuinely unavailable, import via PKCS8 PEM which lets Go recompute them, instead of a hand-built JWK
  4. Check that each member keeps its leading zero bytes exactly as exported (do not strip '0x00' prefixes yourself)
  5. 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)

Example fix

// before: hand-built JWK missing CRT params
const jwk = { kty: 'RSA', n, e, d, p, q }; // dp/dq/qi absent -> zero -> Validate fails
const key = await crypto.subtle.importKey('jwk', jwk, alg, true, ['decrypt']);

// after: import PKCS8 PEM and let k6 derive/reuse valid CRT values
const key = await crypto.subtle.importKey('pkcs8', pemBytes, alg, true, ['decrypt']);
const fullJwk = await crypto.subtle.exportKey('jwk', key); // now complete & consistent
Defensive patterns

Strategy: try-catch

Validate before calling

function jwkLooksComplete(jwk) {
  return ['kty','n','e','d','p','q','dp','dq','qi'].every(k => typeof jwk[k] === 'string' && jwk[k].length > 0);
}
if (!jwkLooksComplete(jwk)) throw new Error('JWK missing private/CRT members; use exportKey output');

Type guard

const isCompleteRsaPrivateJwk = j => j.kty === 'RSA' && ['n','e','d','p','q','dp','dq','qi'].every(k => typeof j[k] === 'string' && j[k].length > 0);

Try / catch

try { key = await crypto.subtle.importKey('jwk', jwk, alg, true, usages); }
catch (e) { console.error('RSA JWK inconsistent:', e.message, '- re-export from the source key'); throw e; }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/69fcb3d9e4cbeb2b. Report an issue: GitHub.