mirror of
https://github.com/httpie/cli.git
synced 2024-11-21 23:33:12 +01:00
Implement support for private key passphrases
This commit is contained in:
parent
98688b2f2d
commit
15013fd609
@ -5,6 +5,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
|
|
||||||
## [3.0.3.dev0](https://github.com/httpie/httpie/compare/3.0.2...HEAD) (Unreleased)
|
## [3.0.3.dev0](https://github.com/httpie/httpie/compare/3.0.2...HEAD) (Unreleased)
|
||||||
|
|
||||||
|
- Added support for specifying certificate private key passphrases through `--cert-key-pass` and prompts. ([#946](https://github.com/httpie/httpie/issues/946))
|
||||||
- Fixed escaping of integer indexes with multiple backslashes in the nested JSON builder. ([#1285](https://github.com/httpie/httpie/issues/1285))
|
- Fixed escaping of integer indexes with multiple backslashes in the nested JSON builder. ([#1285](https://github.com/httpie/httpie/issues/1285))
|
||||||
- Fixed displaying of status code without a status message on non-`auto` themes. ([#1300](https://github.com/httpie/httpie/issues/1300))
|
- Fixed displaying of status code without a status message on non-`auto` themes. ([#1300](https://github.com/httpie/httpie/issues/1300))
|
||||||
- Fixed redundant issuance of stdin detection warnings on some rare cases due to underlying implementation. ([#1303](https://github.com/httpie/httpie/pull/1303))
|
- Fixed redundant issuance of stdin detection warnings on some rare cases due to underlying implementation. ([#1303](https://github.com/httpie/httpie/pull/1303))
|
||||||
|
@ -1489,6 +1489,21 @@ path of the key file with `--cert-key`:
|
|||||||
$ http --cert=client.crt --cert-key=client.key https://example.org
|
$ http --cert=client.crt --cert-key=client.key https://example.org
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If the given private key requires a passphrase, HTTPie will automatically detect it
|
||||||
|
and ask it through a prompt:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ http --cert=client.pem --cert-key=client.key https://example.org
|
||||||
|
http: passphrase for client.key: ****
|
||||||
|
```
|
||||||
|
|
||||||
|
If you don't want to see a prompt, you can supply the passphrase with the `--cert-key-pass`
|
||||||
|
argument:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ http --cert=client.pem --cert-key=client.key --cert-key-pass=my_password https://example.org
|
||||||
|
```
|
||||||
|
|
||||||
### SSL version
|
### SSL version
|
||||||
|
|
||||||
Use the `--ssl=<PROTOCOL>` option to specify the desired protocol version to use.
|
Use the `--ssl=<PROTOCOL>` option to specify the desired protocol version to use.
|
||||||
|
@ -10,7 +10,8 @@ from urllib.parse import urlsplit
|
|||||||
from requests.utils import get_netrc_auth
|
from requests.utils import get_netrc_auth
|
||||||
|
|
||||||
from .argtypes import (
|
from .argtypes import (
|
||||||
AuthCredentials, KeyValueArgType, PARSED_DEFAULT_FORMAT_OPTIONS,
|
AuthCredentials, SSLCredentials, KeyValueArgType,
|
||||||
|
PARSED_DEFAULT_FORMAT_OPTIONS,
|
||||||
parse_auth,
|
parse_auth,
|
||||||
parse_format_options,
|
parse_format_options,
|
||||||
)
|
)
|
||||||
@ -148,6 +149,7 @@ class HTTPieArgumentParser(BaseHTTPieArgumentParser):
|
|||||||
self._parse_items()
|
self._parse_items()
|
||||||
self._process_url()
|
self._process_url()
|
||||||
self._process_auth()
|
self._process_auth()
|
||||||
|
self._process_ssl_cert()
|
||||||
|
|
||||||
if self.args.raw is not None:
|
if self.args.raw is not None:
|
||||||
self._body_from_input(self.args.raw)
|
self._body_from_input(self.args.raw)
|
||||||
@ -236,6 +238,19 @@ class HTTPieArgumentParser(BaseHTTPieArgumentParser):
|
|||||||
self.env.stdout = self.env.devnull
|
self.env.stdout = self.env.devnull
|
||||||
self.env.apply_warnings_filter()
|
self.env.apply_warnings_filter()
|
||||||
|
|
||||||
|
def _process_ssl_cert(self):
|
||||||
|
from httpie.ssl_ import _is_key_file_encrypted
|
||||||
|
|
||||||
|
if self.args.cert_key_pass is None:
|
||||||
|
self.args.cert_key_pass = SSLCredentials(None)
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.args.cert_key is not None
|
||||||
|
and self.args.cert_key_pass.value is None
|
||||||
|
and _is_key_file_encrypted(self.args.cert_key)
|
||||||
|
):
|
||||||
|
self.args.cert_key_pass.prompt_password(self.args.cert_key)
|
||||||
|
|
||||||
def _process_auth(self):
|
def _process_auth(self):
|
||||||
# TODO: refactor & simplify this method.
|
# TODO: refactor & simplify this method.
|
||||||
self.args.auth_plugin = None
|
self.args.auth_plugin = None
|
||||||
|
@ -130,16 +130,11 @@ class KeyValueArgType:
|
|||||||
return tokens
|
return tokens
|
||||||
|
|
||||||
|
|
||||||
class AuthCredentials(KeyValueArg):
|
class PromptMixin:
|
||||||
"""Represents parsed credentials."""
|
def _prompt_password(self, prompt: str) -> str:
|
||||||
|
prompt_text = f'http: {prompt}: '
|
||||||
def has_password(self) -> bool:
|
|
||||||
return self.value is not None
|
|
||||||
|
|
||||||
def prompt_password(self, host: str):
|
|
||||||
prompt_text = f'http: password for {self.key}@{host}: '
|
|
||||||
try:
|
try:
|
||||||
self.value = self._getpass(prompt_text)
|
return self._getpass(prompt_text)
|
||||||
except (EOFError, KeyboardInterrupt):
|
except (EOFError, KeyboardInterrupt):
|
||||||
sys.stderr.write('\n')
|
sys.stderr.write('\n')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
@ -150,6 +145,26 @@ class AuthCredentials(KeyValueArg):
|
|||||||
return getpass.getpass(str(prompt))
|
return getpass.getpass(str(prompt))
|
||||||
|
|
||||||
|
|
||||||
|
class SSLCredentials(PromptMixin):
|
||||||
|
"""Represents the passphrase for the certificate's key."""
|
||||||
|
|
||||||
|
def __init__(self, value: Optional[str]) -> None:
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def prompt_password(self, key_file: str) -> None:
|
||||||
|
self.value = self._prompt_password(f'passphrase for {key_file}')
|
||||||
|
|
||||||
|
|
||||||
|
class AuthCredentials(KeyValueArg, PromptMixin):
|
||||||
|
"""Represents parsed credentials."""
|
||||||
|
|
||||||
|
def has_password(self) -> bool:
|
||||||
|
return self.value is not None
|
||||||
|
|
||||||
|
def prompt_password(self, host: str) -> None:
|
||||||
|
self.value = self._prompt_password(f'password for {self.key}@{host}:')
|
||||||
|
|
||||||
|
|
||||||
class AuthCredentialsArgType(KeyValueArgType):
|
class AuthCredentialsArgType(KeyValueArgType):
|
||||||
"""A key-value arg type that parses credentials."""
|
"""A key-value arg type that parses credentials."""
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ from textwrap import dedent, wrap
|
|||||||
from .. import __doc__, __version__
|
from .. import __doc__, __version__
|
||||||
from .argparser import HTTPieArgumentParser
|
from .argparser import HTTPieArgumentParser
|
||||||
from .argtypes import (
|
from .argtypes import (
|
||||||
KeyValueArgType, SessionNameValidator,
|
KeyValueArgType, SessionNameValidator, SSLCredentials,
|
||||||
readable_file_arg, response_charset_type, response_mime_type,
|
readable_file_arg, response_charset_type, response_mime_type,
|
||||||
)
|
)
|
||||||
from .constants import (
|
from .constants import (
|
||||||
@ -803,6 +803,17 @@ ssl.add_argument(
|
|||||||
'''
|
'''
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ssl.add_argument(
|
||||||
|
'--cert-key-pass',
|
||||||
|
default=None,
|
||||||
|
type=SSLCredentials,
|
||||||
|
help='''
|
||||||
|
The passphrase to be used to with the given private key. Only needed if --cert-key
|
||||||
|
is given and the key file requires a passphrase.
|
||||||
|
|
||||||
|
'''
|
||||||
|
)
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
# Troubleshooting
|
# Troubleshooting
|
||||||
#######################################################################
|
#######################################################################
|
||||||
|
@ -19,7 +19,7 @@ from .encoding import UTF8
|
|||||||
from .models import RequestsMessage
|
from .models import RequestsMessage
|
||||||
from .plugins.registry import plugin_manager
|
from .plugins.registry import plugin_manager
|
||||||
from .sessions import get_httpie_session
|
from .sessions import get_httpie_session
|
||||||
from .ssl_ import AVAILABLE_SSL_VERSION_ARG_MAPPING, HTTPieHTTPSAdapter
|
from .ssl_ import AVAILABLE_SSL_VERSION_ARG_MAPPING, HTTPieCertificate, HTTPieHTTPSAdapter
|
||||||
from .uploads import (
|
from .uploads import (
|
||||||
compress_request, prepare_request_body,
|
compress_request, prepare_request_body,
|
||||||
get_multipart_data_and_content_type,
|
get_multipart_data_and_content_type,
|
||||||
@ -262,7 +262,14 @@ def make_send_kwargs_mergeable_from_env(args: argparse.Namespace) -> dict:
|
|||||||
if args.cert:
|
if args.cert:
|
||||||
cert = args.cert
|
cert = args.cert
|
||||||
if args.cert_key:
|
if args.cert_key:
|
||||||
cert = cert, args.cert_key
|
# Having a client certificate key passphrase is not supported
|
||||||
|
# by requests. So we are using our own transportation structure
|
||||||
|
# which is compatible with their format (a tuple of minimum two
|
||||||
|
# items).
|
||||||
|
#
|
||||||
|
# See: https://github.com/psf/requests/issues/2519
|
||||||
|
cert = HTTPieCertificate(cert, args.cert_key, args.cert_key_pass.value)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'proxies': {p.key: p.value for p in args.proxy},
|
'proxies': {p.key: p.value for p in args.proxy},
|
||||||
'stream': True,
|
'stream': True,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import ssl
|
import ssl
|
||||||
|
from typing import NamedTuple, Optional
|
||||||
|
|
||||||
from httpie.adapters import HTTPAdapter
|
from httpie.adapters import HTTPAdapter
|
||||||
# noinspection PyPackageRequirements
|
# noinspection PyPackageRequirements
|
||||||
@ -24,6 +25,17 @@ AVAILABLE_SSL_VERSION_ARG_MAPPING = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class HTTPieCertificate(NamedTuple):
|
||||||
|
cert_file: Optional[str] = None
|
||||||
|
key_file: Optional[str] = None
|
||||||
|
key_password: Optional[str] = None
|
||||||
|
|
||||||
|
def to_raw_cert(self):
|
||||||
|
"""Synthesize a requests-compatible (2-item tuple of cert and key file)
|
||||||
|
object from HTTPie's internal representation of a certificate."""
|
||||||
|
return (self.cert_file, self.key_file)
|
||||||
|
|
||||||
|
|
||||||
class HTTPieHTTPSAdapter(HTTPAdapter):
|
class HTTPieHTTPSAdapter(HTTPAdapter):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -47,6 +59,13 @@ class HTTPieHTTPSAdapter(HTTPAdapter):
|
|||||||
kwargs['ssl_context'] = self._ssl_context
|
kwargs['ssl_context'] = self._ssl_context
|
||||||
return super().proxy_manager_for(*args, **kwargs)
|
return super().proxy_manager_for(*args, **kwargs)
|
||||||
|
|
||||||
|
def cert_verify(self, conn, url, verify, cert):
|
||||||
|
if isinstance(cert, HTTPieCertificate):
|
||||||
|
conn.key_password = cert.key_password
|
||||||
|
cert = cert.to_raw_cert()
|
||||||
|
|
||||||
|
return super().cert_verify(conn, url, verify, cert)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _create_ssl_context(
|
def _create_ssl_context(
|
||||||
verify: bool,
|
verify: bool,
|
||||||
@ -61,3 +80,17 @@ class HTTPieHTTPSAdapter(HTTPAdapter):
|
|||||||
# in `super().cert_verify()`.
|
# in `super().cert_verify()`.
|
||||||
cert_reqs=ssl.CERT_REQUIRED if verify else ssl.CERT_NONE
|
cert_reqs=ssl.CERT_REQUIRED if verify else ssl.CERT_NONE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_key_file_encrypted(key_file):
|
||||||
|
"""Detects if a key file is encrypted or not.
|
||||||
|
|
||||||
|
Copy of the internal urllib function (urllib3.util.ssl_)"""
|
||||||
|
|
||||||
|
with open(key_file, "r") as f:
|
||||||
|
for line in f:
|
||||||
|
# Look for Proc-Type: 4,ENCRYPTED
|
||||||
|
if "ENCRYPTED" in line:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
42
tests/client_certs/password_protected/client.key
Normal file
42
tests/client_certs/password_protected/client.key
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
Proc-Type: 4,ENCRYPTED
|
||||||
|
DEK-Info: AES-128-CBC,93DA845817852FB576163AA829C232E9
|
||||||
|
|
||||||
|
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
|
||||||
|
-----END RSA PRIVATE KEY-----
|
26
tests/client_certs/password_protected/client.pem
Normal file
26
tests/client_certs/password_protected/client.pem
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEazCCAtOgAwIBAgIUIWojJySwCenyvcJrZY1aKOMQfTUwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||||
|
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMjAzMDExMzM5MzRaFw00OTA3
|
||||||
|
MTcxMzM5MzRaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
|
||||||
|
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggGiMA0GCSqGSIb3DQEB
|
||||||
|
AQUAA4IBjwAwggGKAoIBgQC0+8GOIhknLgLmWEQGgdDJffntbDfGdtG7JFUFfvuN
|
||||||
|
sibTHL4RPNhe+UrT/LR+JBfIfmdeb+NlmKzsTeR0+8PkX5ZjXMShf5icghVukK7G
|
||||||
|
OoQS7olQqlGzpIX76VqktVr4tFNXmMJeBO0NIaXHm0ASsoz3fIfDx9ttJETPs6lm
|
||||||
|
Wv/PUPemvtUgcbAb+kjz9QqcUV8B1xcCvQma6NSpxcmJHqAuI6HkdbDzyedKuuSi
|
||||||
|
M6yNFjh3EJjsufReQgkcfDsqh+RA3zQoIyPXLNqjzGD71z24jUtvIxb5ZNEtv0xp
|
||||||
|
5zCOCavuRNNyKGvvnlIeyup7bMe0QIds566miG49osVpPVvVmg+q+w2YYAE+7svb
|
||||||
|
nJp7NYn2tryRqsmvnASLVQD6T9wTWUa8w/tT1+ltnhfqbwDcVACzsw/U4FFwcfWw
|
||||||
|
5BnUcJacoDkj/3TCqgkA8XFe1/DVU8XCcsvEaoLzwHhHu2+QDpqal8rNouyTpFGA
|
||||||
|
/wioVBQGpksPZjl8lumsz3kCAwEAAaNTMFEwHQYDVR0OBBYEFGJhl1BPOXCVqRo3
|
||||||
|
U/ruuedvlDqsMB8GA1UdIwQYMBaAFGJhl1BPOXCVqRo3U/ruuedvlDqsMA8GA1Ud
|
||||||
|
EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggGBAE9NtrE5qSHlK9DXdH6bPW6z
|
||||||
|
GO9uWBl3rJVtqVvPoH8RxJG/jtaD/Pnc3MkIxMoliCleNK6BdYVGV9u8x9W1jQo8
|
||||||
|
H+mnH3/Ise8ZY1zpblZJF0z9xs5sWW7qO8U06GmJWRSPn3LKEZjLsNmThhUW09wN
|
||||||
|
8EZX914zCWtzCrUTNg8Au1Dz9zA9ScfpCVPhKORTCnrpoTL6iXsPxmCx+5awmNLE
|
||||||
|
uh9kw4NScEyq33RTPosMpwSMlXGRuASltx/J7Rn0DNR0r1p0XzDS4CG1iDwXHlEF
|
||||||
|
MwsOvSahNyz5RInrU3cgN70tafoRIHScLYycnRml8dydxrDoFgdJk5sI4zgq24Sg
|
||||||
|
TktTq9ShrT4yQX+lrGS6eZQK/YZEBPD7BdTLYp3vlfYQMJ4Jz9SyQ8b9/9jIFVFS
|
||||||
|
dFfWiCqEuhTvGfptAzYX+K9OaegZnIk3X7R6O+YQ3oHCbLbnV3bpKlgNnOKBwa2X
|
||||||
|
kJ5GRp+rZOJ97yjrspKjpR5tNCiJnp7NnnA5VA6mfw==
|
||||||
|
-----END CERTIFICATE-----
|
@ -6,6 +6,8 @@ import pytest_httpbin.certs
|
|||||||
import requests.exceptions
|
import requests.exceptions
|
||||||
import urllib3
|
import urllib3
|
||||||
|
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
from httpie.ssl_ import AVAILABLE_SSL_VERSION_ARG_MAPPING, DEFAULT_SSL_CIPHERS
|
from httpie.ssl_ import AVAILABLE_SSL_VERSION_ARG_MAPPING, DEFAULT_SSL_CIPHERS
|
||||||
from httpie.status import ExitStatus
|
from httpie.status import ExitStatus
|
||||||
|
|
||||||
@ -32,6 +34,15 @@ CLIENT_CERT = str(CERTS_ROOT / 'client.crt')
|
|||||||
CLIENT_KEY = str(CERTS_ROOT / 'client.key')
|
CLIENT_KEY = str(CERTS_ROOT / 'client.key')
|
||||||
CLIENT_PEM = str(CERTS_ROOT / 'client.pem')
|
CLIENT_PEM = str(CERTS_ROOT / 'client.pem')
|
||||||
|
|
||||||
|
# In case of a regeneration, use the following commands
|
||||||
|
# in the PWD_TESTS_ROOT:
|
||||||
|
# $ openssl genrsa -aes128 -passout pass:password 3072 > client.pem
|
||||||
|
# $ openssl req -new -x509 -nodes -days 10000 -key client.pem > client.pem
|
||||||
|
PWD_TESTS_ROOT = CERTS_ROOT / 'password_protected'
|
||||||
|
PWD_CLIENT_PEM = str(PWD_TESTS_ROOT / 'client.pem')
|
||||||
|
PWD_CLIENT_KEY = str(PWD_TESTS_ROOT / 'client.key')
|
||||||
|
PWD_CLIENT_PASS = 'password'
|
||||||
|
PWD_CLIENT_INVALID_PASS = PWD_CLIENT_PASS + 'invalid'
|
||||||
|
|
||||||
# We test against a local httpbin instance which uses a self-signed cert.
|
# We test against a local httpbin instance which uses a self-signed cert.
|
||||||
# Requests without --verify=<CA_BUNDLE> will fail with a verification error.
|
# Requests without --verify=<CA_BUNDLE> will fail with a verification error.
|
||||||
@ -165,3 +176,37 @@ def test_pyopenssl_presence():
|
|||||||
else:
|
else:
|
||||||
assert urllib3.util.ssl_.IS_PYOPENSSL
|
assert urllib3.util.ssl_.IS_PYOPENSSL
|
||||||
assert urllib3.util.IS_PYOPENSSL
|
assert urllib3.util.IS_PYOPENSSL
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch('httpie.cli.argtypes.SSLCredentials._prompt_password',
|
||||||
|
new=lambda self, prompt: PWD_CLIENT_PASS)
|
||||||
|
def test_password_protected_cert_prompt(httpbin_secure):
|
||||||
|
r = http(httpbin_secure + '/get',
|
||||||
|
'--cert', PWD_CLIENT_PEM,
|
||||||
|
'--cert-key', PWD_CLIENT_KEY)
|
||||||
|
assert HTTP_OK in r
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch('httpie.cli.argtypes.SSLCredentials._prompt_password',
|
||||||
|
new=lambda self, prompt: PWD_CLIENT_INVALID_PASS)
|
||||||
|
def test_password_protected_cert_prompt_invalid(httpbin_secure):
|
||||||
|
with pytest.raises(ssl_errors):
|
||||||
|
http(httpbin_secure + '/get',
|
||||||
|
'--cert', PWD_CLIENT_PEM,
|
||||||
|
'--cert-key', PWD_CLIENT_KEY)
|
||||||
|
|
||||||
|
|
||||||
|
def test_password_protected_cert_cli_arg(httpbin_secure):
|
||||||
|
r = http(httpbin_secure + '/get',
|
||||||
|
'--cert', PWD_CLIENT_PEM,
|
||||||
|
'--cert-key', PWD_CLIENT_KEY,
|
||||||
|
'--cert-key-pass', PWD_CLIENT_PASS)
|
||||||
|
assert HTTP_OK in r
|
||||||
|
|
||||||
|
|
||||||
|
def test_password_protected_cert_cli_arg_invalid(httpbin_secure):
|
||||||
|
with pytest.raises(ssl_errors):
|
||||||
|
http(httpbin_secure + '/get',
|
||||||
|
'--cert', PWD_CLIENT_PEM,
|
||||||
|
'--cert-key', PWD_CLIENT_KEY,
|
||||||
|
'--cert-key-pass', PWD_CLIENT_INVALID_PASS)
|
||||||
|
Loading…
Reference in New Issue
Block a user