snowflakedb / snowflake-jdbc

Snowflake JDBC Driver
Apache License 2.0
178 stars 171 forks source link

Unable to connect with key pair authentication if private key password contains special characters #1186

Closed StevenMassaro closed 10 months ago

StevenMassaro commented 2 years ago

When using key pair authentication to connect to snowflake through jdbc, and using a special character (like #) in the URL causes an exception: Private key provided is invalid or not supported: rsa_key_bad2.p8: Cannot retrieve the PKCS8EncodedKeySpec

To reproduce this issue, set up key pair auth access to snowflake by following this page, and make sure to choose a password like pass#ord when generating the private key. (You may need to use these instructions to generate your keys: https://community.snowflake.com/s/article/Private-key-provided-is-invalid-or-not-supported-rsa-key-p8--data-isn-t-an-object-ID)

Then, using the newly generated private and public keys, try to connect to Snowflake using this sample Java program, making sure to replace the placeholder values with your values for URL, KEYFILE, and KEYPWD:

import java.sql.*;
import java.util.Properties;

public class Main {
    public static void main(String[] args) throws Exception {
        // get connection
        System.out.println("Create JDBC connection");
        Connection connection = getConnection();
        System.out.println("Done creating JDBC connection\n");

        // create statement
        System.out.println("Create JDBC statement");
        Statement statement = connection.createStatement();
        System.out.println("Done creating JDBC statement\n");

        // create a table
        System.out.println("Create demo table");
        statement.executeUpdate("create or replace table demo(c1 string)");
        System.out.println("Done creating demo table\n");

        // insert a row
        System.out.println("Insert 'hello world'");
        statement.executeUpdate("insert into demo values ('hello world')");
        System.out.println("Done inserting 'hello world'\n");

        // query the data
        System.out.println("Query demo");
        ResultSet resultSet = statement.executeQuery("select * from demo");
        System.out.println("Metadata:");
        System.out.println("================================");

        // fetch metadata
        ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
        System.out.println("Number of columns=" + resultSetMetaData.getColumnCount());
        for (int colIdx = 0; colIdx < resultSetMetaData.getColumnCount(); colIdx++) {
            System.out.println(
                    "Column " + colIdx + ": type=" + resultSetMetaData.getColumnTypeName(colIdx + 1));
        }

        // fetch data
        System.out.println("\nData:");
        System.out.println("================================");
        int rowIdx = 0;
        while (resultSet.next()) {
            System.out.println("row " + rowIdx + ", column 0: " + resultSet.getString(1));
        }
        resultSet.close();
        statement.close();
        connection.close();
    }

    private static Connection getConnection() throws SQLException, ClassNotFoundException {
        // build connection properties
        Class.forName("net.snowflake.client.jdbc.SnowflakeDriver");
        Properties properties = new Properties();
        properties.put("user", ""); // replace "" with your user name
        properties.put("warehouse", ""); // replace "" with target warehouse name
        properties.put("db", ""); // replace "" with target database name
        properties.put("schema", ""); // replace "" with target schema name
        String connectStr = "jdbc:snowflake://URL?private_key_file=KEYFILE&private_key_file_pwd=KEYPWD";
        return DriverManager.getConnection(connectStr, properties);
    }
}

You should get the following exception:

Exception in thread "main" net.snowflake.client.jdbc.SnowflakeSQLLoggedException: Private key provided is invalid or not supported: C:/dev/temp/DAT-11888/rsa_key_bad2.p8: Cannot retrieve the PKCS8EncodedKeySpec
    at net.snowflake.client.jdbc.DefaultSFConnectionHandler.initialize(DefaultSFConnectionHandler.java:109)
    at net.snowflake.client.jdbc.DefaultSFConnectionHandler.initializeConnection(DefaultSFConnectionHandler.java:79)
    at net.snowflake.client.jdbc.SnowflakeConnectionV1.initConnectionWithImpl(SnowflakeConnectionV1.java:116)
    at net.snowflake.client.jdbc.SnowflakeConnectionV1.<init>(SnowflakeConnectionV1.java:96)
    at net.snowflake.client.jdbc.SnowflakeDriver.connect(SnowflakeDriver.java:176)
    at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:677)
    at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:189)
    at Main.getConnection(Main.java:63)
    at Main.main(Main.java:8)
StevenMassaro commented 2 years ago

I couldn't find a way to escape the #. I tried URL encoding it, since the Snowflake driver will decode the values: https://github.com/snowflakedb/snowflake-jdbc/blob/master/src/main/java/net/snowflake/client/jdbc/SnowflakeConnectString.java#L91-L92, but that causes the following error: JWT token is invalid.

sfc-gh-spanaite commented 1 year ago

@StevenMassaro Are you still able to reproduce the issue with the latest JDBC driver version? And if yes, can you please provide me with the OS version and OpenSSL version?

sfc-gh-spanaite commented 10 months ago

Closing since no updates since Oct last year.