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
- Regenerate the key as PKCS#8: openssl genrsa 2048 | openssl pkcs8 -topk8 -v2 aes-256-cbc -inform PEM -out rsa_key.p8
- 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
- Point private_key_path at the new rsa_key.p8 and keep private_key_passphrase set
- 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
- Always generate keys with the -topk8 flag and AES-256 encryption
- Verify the PEM header is 'BEGIN PRIVATE KEY' or 'BEGIN ENCRYPTED PRIVATE KEY' before committing to profiles.yml
- Never base64-encode PEM text as DER or hand-wrap PKCS#1 DER with PKCS#8 headers
- Store the generation command in team docs to avoid legacy genrsa-only keys
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
- parse_constraints: {e}
- describe_relation is not supported for relation type {other:
- from_config: Only available for Snowflake and Redshift
- valid regex
- column name normalization preserves schema compatibility
AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07).
Data as JSON: /api/errors/e9bba2bcc01491b2.
Report an issue: GitHub.