dbt-labs/dbt-core · error

Key is PKCS#1 (RSA private key). Snowflake requires PKCS#8 (

Error message

Key is PKCS#1 (RSA private key). Snowflake requires PKCS#8 (-----BEGIN PRIVATE KEY----- or -----BEGIN ENCRYPTED PRIVATE KEY-----).

Possible causes:
• You ran 'openssl pkcs8 -inform pem -outform der ...' without -topk8
• You wrapped a PKCS#1 DER with PKCS#8 headers
• You base64-encoded PEM text and treated it as DER

>   Generate a PKCS#8 key and update your user public key:
https://docs.snowflake.com/en/user-guide/key-pair-auth
While Snowflake recommends 3DES encryption, Fusion recommends using a modern algorithm such as AES-256:
>   $ openssl genrsa 2048 | openssl pkcs8 -topk8 -v2 aes-256-cbc -inform PEM -out rsa_key.p8

What it means

Raised from SnowflakeAuthIR::apply for the KeypairPath variant when a passphrase is supplied: the key file is read and passed through key_format::normalize_key, which rejects keys whose body is PKCS#1 'RSA PRIVATE KEY' rather than PKCS#8. Snowflake key-pair JWT auth requires PKCS#8 encoding (BEGIN PRIVATE KEY / BEGIN ENCRYPTED PRIVATE KEY), so a PKCS#1 key cannot be used to sign the JWT. The error text includes the common openssl mistakes and the recommended regeneration command.

Source

Thrown at crates/dbt-auth/src/snowflake/mod.rs:241

            }
            Self::KeypairPath {
                user,
                path,
                passphrase,
            } => {
                builder.with_username(user);
                builder.with_password(ADBC_STUB_PASSWORD);
                builder.with_named_option(snowflake::AUTH_TYPE, snowflake::auth_type::JWT)?;
                fs::metadata(path).map_err(|_| {
                    AuthError::config(format!("Private key file not found: '{path}'"))
                })?;
                if let Some(pass) = passphrase {
                    let key_content = fs::read_to_string(path).map_err(|_| {
                        AuthError::config(format!("Could not read from key file: '{path}'"))
                    })?;
                    let (normalized, status) = key_format::normalize_key(&key_content)?;
                    if let key_format::SnowflakeKeypairStatus::Warn3Des(msg) = status {
                        WARN_3DES_KEY_ONCE.call_once(|| warning_printer.warn(msg));
                    }
                    builder
                        .with_named_option(snowflake::JWT_PRIVATE_KEY_PKCS8_VALUE, normalized)?;
                    builder.with_named_option(snowflake::JWT_PRIVATE_KEY_PKCS8_PASSWORD, pass)?;
                } else {
                    builder.with_named_option(snowflake::JWT_PRIVATE_KEY, path)?;
                }
            }
            Self::KeypairInline {
                user,
                private_key,
                passphrase,
            } => {
                builder.with_username(user);
                builder.with_password(ADBC_STUB_PASSWORD);
                builder.with_named_option(snowflake::AUTH_TYPE, snowflake::auth_type::JWT)?;
                let (normalized, status) = key_format::normalize_key(private_key)?;
                if let key_format::SnowflakeKeypairStatus::Warn3Des(msg) = status {

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Regenerate the key as PKCS#8: openssl genrsa 2048 | openssl pkcs8 -topk8 -v2 aes-256-cbc -inform PEM -out rsa_key.p8
  2. Update the Snowflake user with the matching public key (ALTER USER ... SET RSA_PUBLIC_KEY=...) per https://docs.snowflake.com/en/user-guide/key-pair-auth
  3. Point private_key_path at the new rsa_key.p8 and keep private_key_passphrase set
  4. If you must keep the existing key, convert in place with: openssl pkcs8 -topk8 -nocrypt -in rsa_key.pem -out rsa_key.p8 (then re-encrypt if a passphrase is needed)

Example fix

# before
$ openssl genrsa 2048 | openssl pkcs8 -inform pem -outform der -out rsa_key.der   # no -topk8 => still PKCS#1
# after
$ openssl genrsa 2048 | openssl pkcs8 -topk8 -v2 aes-256-cbc -inform PEM -out rsa_key.p8
Defensive patterns

Strategy: validation

Validate before calling

head -1 "$PRIVATE_KEY_PATH" | grep -q 'BEGIN PRIVATE KEY' && echo OK || echo 'PKCS#1 key: regenerate with openssl genrsa 2048 | openssl pkcs8 -topk8 -v2 aes-256-cbc -inform PEM -out rsa_key.p8'

Prevention

When it happens

Trigger: Setting private_key_path (or the inline equivalent) plus private_key_passphrase in a Snowflake profile where the key file contains '-----BEGIN RSA PRIVATE KEY-----' (unencrypted PKCS#1) or a DER/PEM mismatch that resolves to PKCS#1.

Common situations: Following old Snowflake docs or blog posts that generate keys with plain 'openssl genrsa'; running 'openssl pkcs8 -inform pem -outform der' without -topk8 (that converts the key body but keeps PKCS#1 structure); wrapping a PKCS#1 DER with PKCS#8 headers by hand; base64-encoding PEM text and saving it as DER.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07). Data as JSON: /api/errors/e9bba2bcc01491b2. Report an issue: GitHub.