Skip to content

Commit ad59a14

Browse files
sfc-gh-eworoshowsfc-gh-pczajka
authored andcommitted
Support base64-encoded DER private keys (#2134)
1 parent 9f0068b commit ad59a14

File tree

5 files changed

+38
-4
lines changed

5 files changed

+38
-4
lines changed

DESCRIPTION.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,23 @@ Source code is also available at: https://github.com/snowflakedb/snowflake-conne
88

99
# Release Notes
1010

11+
<<<<<<< HEAD
12+
=======
13+
- v3.12.5(TBD)
14+
- Added a feature to limit the sizes of IO-bound ThreadPoolExecutors during PUT and GET commands.
15+
- Adding support for the new PAT authentication method.
16+
- Updated README.md to include instructions on how to verify package signatures using `cosign`.
17+
- Updated the log level for cursor's chunk rowcount from INFO to DEBUG.
18+
- Added a feature to verify if the connection is still good enough to send queries over.
19+
- Added support for base64-encoded DER private key strings in the `private_key` authentication type.
20+
21+
- v3.12.4(December 3,2024)
22+
- Fixed a bug where multipart uploads to Azure would be missing their MD5 hashes.
23+
- Fixed a bug where OpenTelemetry header injection would sometimes cause Exceptions to be thrown.
24+
- Fixed a bug where OCSP checks would throw TypeError and make mainly GCP blob storage unreachable.
25+
- Bumped pyOpenSSL dependency from >=16.2.0,<25.0.0 to >=22.0.0,<25.0.0.
26+
27+
>>>>>>> 4b3ded11 (Support base64-encoded DER private keys (#2134))
1128
- v3.12.3(October 25,2024)
1229
- Improved the error message for SSL-related issues to provide clearer guidance when an SSL error occurs.
1330
- Improved error message for SQL execution cancellations caused by timeout.

src/snowflake/connector/auth/keypair.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class AuthByKeyPair(AuthByPlugin):
4343

4444
def __init__(
4545
self,
46-
private_key: bytes | RSAPrivateKey,
46+
private_key: bytes | str | RSAPrivateKey,
4747
lifetime_in_seconds: int = LIFETIME,
4848
**kwargs,
4949
) -> None:
@@ -75,7 +75,7 @@ def __init__(
7575
).total_seconds()
7676
)
7777

78-
self._private_key: bytes | RSAPrivateKey | None = private_key
78+
self._private_key: bytes | str | RSAPrivateKey | None = private_key
7979
self._jwt_token = ""
8080
self._jwt_token_exp = 0
8181
self._lifetime = timedelta(
@@ -105,6 +105,17 @@ def prepare(
105105

106106
now = datetime.now(timezone.utc).replace(tzinfo=None)
107107

108+
if isinstance(self._private_key, str):
109+
try:
110+
self._private_key = base64.b64decode(self._private_key)
111+
except Exception as e:
112+
raise ProgrammingError(
113+
msg=f"Failed to decode private key: {e}\nPlease provide a valid "
114+
"unencrypted rsa private key in base64-encoded DER format as a "
115+
"str object",
116+
errno=ER_INVALID_PRIVATE_KEY,
117+
)
118+
108119
if isinstance(self._private_key, bytes):
109120
try:
110121
private_key = load_der_private_key(

src/snowflake/connector/connection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ def _get_private_bytes_from_file(
184184
"backoff_policy": (DEFAULT_BACKOFF_POLICY, Callable),
185185
"passcode_in_password": (False, bool), # Snowflake MFA
186186
"passcode": (None, (type(None), str)), # Snowflake MFA
187-
"private_key": (None, (type(None), bytes, RSAPrivateKey)),
187+
"private_key": (None, (type(None), bytes, str, RSAPrivateKey)),
188188
"private_key_file": (None, (type(None), str)),
189189
"private_key_file_pwd": (None, (type(None), str, bytes)),
190190
"token": (None, (type(None), str)), # OAuth/JWT/PAT Token

test/integ/test_key_pair_authentication.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from __future__ import annotations
77

8+
import base64
89
import uuid
910
from datetime import datetime, timedelta, timezone
1011
from os import path
@@ -126,6 +127,11 @@ def fin():
126127
with snowflake.connector.connect(**db_config) as _:
127128
pass
128129

130+
# Ensure the base64-encoded version also works
131+
db_config["private_key"] = base64.b64encode(private_key_der)
132+
with snowflake.connector.connect(**db_config) as _:
133+
pass
134+
129135

130136
@pytest.mark.skipolddriver
131137
def test_multiple_key_pair(is_public_test, request, conn_cnx, db_parameters):

test/unit/test_auth_keypair.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def test_auth_keypair_bad_type():
107107
class Bad:
108108
pass
109109

110-
for bad_private_key in ("abcd", 1234, Bad()):
110+
for bad_private_key in (1234, Bad()):
111111
auth_instance = AuthByKeyPair(private_key=bad_private_key)
112112
with raises(TypeError) as ex:
113113
auth_instance.prepare(account=account, user=user)

0 commit comments

Comments
 (0)