2
votes

I generated an RSA key pair by following this and also used the following snippet to connect Snowflake.
The JWT token generated internally does seem to be valid based on jwt.io, but upon establishing a connection, the client throws the following error:

net.snowflake.client.jdbc.SnowflakeSQLException: JWT token is invalid.

What could cause the Snowflake server to return the error?

Sample Code with some modification:

import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.pkcs.PKCSException;

import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Paths;
import java.security.PrivateKey;
import java.security.Security;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.DriverManager;
import java.util.Properties;

public class TestJdbc
{
  // Path to the private key file that you generated earlier.
  private static final String PRIVATE_KEY_FILE = "/<path>/rsa_key.p8";

  public static class PrivateKeyReader
  {

    // If you generated an encrypted private key, implement this method to return
    // the passphrase for decrypting your private key.
    private static String getPrivateKeyPassphrase() {
      return "<private_key_passphrase>";
    }

    public static PrivateKey get(String filename)
            throws Exception
    {
      PrivateKeyInfo privateKeyInfo = null;
      Security.addProvider(new BouncyCastleProvider());
      // Read an object from the private key file.
      PEMParser pemParser = new PEMParser(new FileReader(Paths.get(filename).toFile()));
      Object pemObject = pemParser.readObject();
      if (pemObject instanceof PKCS8EncryptedPrivateKeyInfo) {
        // Handle the case where the private key is encrypted.
        PKCS8EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = (PKCS8EncryptedPrivateKeyInfo) pemObject;
        String passphrase = getPrivateKeyPassphrase();
        InputDecryptorProvider pkcs8Prov = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(passphrase.toCharArray());
        privateKeyInfo = encryptedPrivateKeyInfo.decryptPrivateKeyInfo(pkcs8Prov);
      } else if (pemObject instanceof PrivateKeyInfo) {
        // Handle the case where the private key is unencrypted.
        privateKeyInfo = (PrivateKeyInfo) pemObject;
      }
      pemParser.close();
      JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME);
      return converter.getPrivateKey(privateKeyInfo);
    }
  }

  public static void main(String[] args)
      throws Exception
  {
    String url = "jdbc:snowflake://<account>.snowflakecomputing.com";
    SnowflakeBasicDataSource ds = new SnowflakeBasicDataSource();
    ds.setWarehouse("<warehouse_name>");
    ds.setDatabaseName("<database_name>");
    ds.setSchema("<schema_name>");
    ds.setUser("<user>");
    ds.setPrivateKey(PrivateKeyReader.get(PRIVATE_KEY_FILE));
    ds.setRole("<role_name>");
    Connection conn = ds.getConnection();
    Statement stat = conn.createStatement();
    ResultSet res = stat.executeQuery("select 1");
    res.next();
    System.out.println(res.getString(1));
    conn.close();
  }
}

↓log output:

2021-01-21 02:26:50.442 n.s.c.core.SFSession FINE open:528 - input: server=https://<account>.snowflakecomputing.com:443/, account=<account>, user=<user>, password=****  role=null, database=<database>, schema=<schema>, warehouse=<warehouse>, validate_default_parameters=null, authenticator=SNOWFLAKE_JWT, ocsp_mode=FAIL_OPEN, passcode_in_password=null, passcode=****  private_key=(not null), use_proxy=null, proxy_host=null, proxy_port=null, proxy_user=null, proxy_password=****  disable_socks_proxy=null, application=null, app_id=JDBC, app_version=3.12.17, login_timeout=null, network_timeout=null, query_timeout=null, tracing=all, private_key_file=null, private_key_file_pwd=****  session_parameters: client_store_temporary_credential=null
2021-01-21 02:26:50.538 n.s.c.core.HttpUtil FINE executeRequestInternal:485 - Pool: [leased: 0; pending: 0; available: 2; max: 300] Executing: POST https://<account>.snowflakecomputing.com:443/session/v1/login-request?databaseName=<database>&schemaName=<schema>&warehouse=<warehouse>&requestId=351ecacf-e674-4a84-ac3c-388ca8ff84fa HTTP/1.1
2021-01-21 02:26:50.539 n.s.c.jdbc.RestRequest FINE execute:115 - Retry count: 0
2021-01-21 02:26:50.674 n.s.c.jdbc.RestRequest FINE execute:199 - HTTP response code: 200
2021-01-21 02:26:50.676 n.s.c.core.HttpUtil FINE executeRequestInternal:533 - Pool: [leased: 0; pending: 0; available: 2; max: 300] Request returned for: POST https://<account>.snowflakecomputing.com:443/session/v1/login-request?databaseName=<database>&schemaName=<schema>&warehouse=<warehouse>&requestId=351ecacf-e674-4a84-ac3c-388ca8ff84fa HTTP/1.1
2021-01-21 02:26:50.681 n.s.c.core.SessionUtil FINE newSession:582 - response = {
  "data" : {
  "nextAction" : null,
  "pwdChangeInfo" : null,
  "inFlightCtx" : null,
  "redirectUrl" : null,
  "licenseAgreementPDFFilePath" : null,
  "licenseAgreementHTMLFilePath" : null,
  "authnMethod" : "KEYPAIR",
  "oAuthSessionStorageData" : null,
  "relayState" : null
},
  "code" : "390144",
  "message" : "JWT token is invalid.",
  "success" : false,
  "headers" : null
}
2021-01-21 02:26:50.689 n.s.c.jdbc.SnowflakeSQLException FINE <init>:40 - Snowflake exception: JWT token is invalid., sqlState:08001, vendorCode:390,144, queryId:
2021-01-21 02:26:50.690 n.s.c.jdbc.SnowflakeBasicDataSource SEVERE getConnection:98 - Failed to create a connection for threeshake at jdbc:snowflake://<account>.snowflakecomputing.com: net.snowflake.client.jdbc.SnowflakeSQLException: JWT token is invalid.
2021-01-21 02:26:52.464 n.s.c.core.EventHandler FINE flushEventBuffer:424 - Flushing eventBuffer
2

2 Answers

1
votes

Have you seen this and tried going through the recommendations? One of the last bullets talks about using the latest JDBC driver, but also check that you have a supported JAVA version.

Also, were you able to generate any JDBC logs? The logs might give you some clues as well.

1
votes

Turns out when setting the account, no region segment should be included.
Say the full account name is "xy12345.us-east-1-gov.aws", the account property has to be set as follows.

ds.setAccount("xy12345")