2525from gevent .select import POLLIN , POLLOUT
2626from ssh import options
2727from ssh .session import Session , SSH_READ_PENDING , SSH_WRITE_PENDING
28- from ssh .key import import_privkey_file
28+ from ssh .key import import_privkey_file , import_cert_file , copy_cert_to_privkey
2929from ssh .exceptions import EOF
3030from ssh .error_codes import SSH_AGAIN
3131
3232from ..base .single import BaseSSHClient
3333from ...exceptions import AuthenticationError , SessionError , Timeout
3434from ...constants import DEFAULT_RETRIES , RETRY_DELAY
35+ from ..common import _validate_pkey_path
3536
3637
3738logger = logging .getLogger (__name__ )
@@ -43,6 +44,7 @@ class SSHClient(BaseSSHClient):
4344 def __init__ (self , host ,
4445 user = None , password = None , port = None ,
4546 pkey = None ,
47+ cert_file = None ,
4648 num_retries = DEFAULT_RETRIES ,
4749 retry_delay = RETRY_DELAY ,
4850 allow_agent = True , timeout = None ,
@@ -64,6 +66,13 @@ def __init__(self, host,
6466 be either absolute path or relative to user home directory
6567 like ``~/<path>``.
6668 :type pkey: str
69+ :param cert_file: Public key signed certificate file to use for
70+ authentication. The corresponding private key must also be provided
71+ via ``pkey`` parameter.
72+ For example ``pkey='id_rsa',cert_file='id_rsa-cert.pub'`` for RSA
73+ signed certificate.
74+ Path must be absolute or relative to user home directory.
75+ :type cert_file: str
6776 :param num_retries: (Optional) Number of connection and authentication
6877 attempts before the client gives up. Defaults to 3.
6978 :type num_retries: int
@@ -96,6 +105,7 @@ def __init__(self, host,
96105 :raises: :py:class:`pssh.exceptions.PKeyFileError` on errors finding
97106 provided private key.
98107 """
108+ self .cert_file = _validate_pkey_path (cert_file , host )
99109 self .gssapi_auth = gssapi_auth
100110 self .gssapi_server_identity = gssapi_server_identity
101111 self .gssapi_client_identity = gssapi_client_identity
@@ -194,10 +204,18 @@ def _password_auth(self):
194204 raise AuthenticationError ("Password authentication failed - %s" , ex )
195205
196206 def _pkey_auth (self , pkey , password = None ):
197- password = b'' if not password else password
198207 pkey = import_privkey_file (pkey , passphrase = password )
208+ if self .cert_file is not None :
209+ logger .debug ("Certificate file set - trying certificate authentication" )
210+ self ._import_cert_file (pkey )
199211 self .session .userauth_publickey (pkey )
200212
213+ def _import_cert_file (self , pkey ):
214+ cert_key = import_cert_file (self .cert_file )
215+ self .session .userauth_try_publickey (cert_key )
216+ copy_cert_to_privkey (cert_key , pkey )
217+ logger .debug ("Imported certificate file %s for pkey %s" , self .cert_file , self .pkey )
218+
201219 def open_session (self ):
202220 """Open new channel from session."""
203221 logger .debug ("Opening new channel on %s" , self .host )
0 commit comments