OtterMind/Chat2DB · error · BusinessException

api.privateKeyNotFound

api.privateKeyNotFound

Error message

api.privateKeyNotFound

What it means

BusinessException 'api.privateKeyNotFound' from stringToPrivateKey when the organization-token string cannot be Base64-decoded into a valid PKCS8 RSA private key. The KeyFactory/KeySpec step throws, meaning the supplied key material is malformed or not actually a private key.

Source

Thrown at chat2db-community-server/chat2db-community-domain/chat2db-community-domain-core/src/main/java/ai/chat2db/community/domain/core/impl/db/DbWorkspaceDataSourceServiceImpl.java:231

        if (StringUtils.isNotBlank(dataSource.getHost())) {
            dataSource.setHost(decryptToken(dataSource.getHost(), privateKey));
        }
        if (StringUtils.isNotBlank(dataSource.getUrl())) {
            dataSource.setUrl(decryptToken(dataSource.getUrl(), privateKey));
        }
        if (StringUtils.isNotBlank(dataSource.getUser())) {
            dataSource.setUser(decryptToken(dataSource.getUser(), privateKey));
        }
    }

    private PrivateKey stringToPrivateKey(String privateKeyString) {
        try {
            byte[] keyBytes = Base64.getDecoder().decode(privateKeyString);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return keyFactory.generatePrivate(keySpec);
        } catch (Exception e) {
            throw new BusinessException("api.privateKeyNotFound");
        }
    }

    private String decryptToken(String encryptedToken, PrivateKey privateKey) {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedToken));
            return new String(decryptedBytes, StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error("decrypt token error", e);
            throw new BusinessException("api.decryptPasswordError");
        }
    }

    private String decryptString(String password) {
        if (password == null || password.isEmpty()) {
            return password;

View on GitHub (pinned to 5ee1e990e7)

Solutions

  1. Re-authenticate to obtain a fresh organization token matching the server's current key pair.
  2. Confirm the token is a PKCS8 private key (Base64 of DER, not PEM-wrapped or a public key).
  3. Check for whitespace/newlines accidentally included in the token value.
  4. Verify the server-side key provisioning generated a valid RSA private key.
Defensive patterns

Strategy: try-catch

Validate before calling

// sanity check the token shape before use
String tok = ctx.getOrganizationToken();
if (tok == null || tok.length() < 100) throw new IllegalArgumentException("organization token looks invalid");
try { Base64.getDecoder().decode(tok); } catch (IllegalArgumentException ex) { throw new IllegalArgumentException("token is not base64"); }

Try / catch

try { PrivateKey k = stringToPrivateKey(token); }
catch (BusinessException e) {
    // recover by forcing re-auth rather than continuing with a bad key
    throw new NeedLoggedInBusinessException();
}

Prevention

When it happens

Trigger: decryptSensitiveFields receives a non-null organizationToken that is corrupt, truncated, not PKCS8 DER, or is a public key string. Base64.getDecoder().decode or KeyFactory.generatePrivate then fails.

Common situations: Org token overwritten with a placeholder; token from a different key pair (public key only); copy/paste truncation or whitespace in the token; key regenerated server-side but client holds a stale token.

Related errors


AI-assisted analysis of OtterMind/Chat2DB@5ee1e990e7 (2026-08-14). Data as JSON: /api/errors/3945f3dabc632087. Report an issue: GitHub.