sshkey-tools


Namesshkey-tools JSON
Version 0.10.3 PyPI version JSON
download
home_pagehttps://github.com/scheiblingco/sshkey-tools
SummaryA Python module for generating, parsing and handling OpenSSH keys and certificates
upload_time2024-01-23 14:01:23
maintainer
docs_urlNone
authorLars Scheibling
requires_python>=3.6
licenseGnuPG 3.0
keywords python openssh ssh keys sshkey ssh-keygen ssh-certificate certificate parser decoder
VCS
bugtrack_url
requirements click cryptography bcrypt PrettyTable pytimeparse2
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ![SSHKey Tools](./assets/sshkey-tools-plain.svg)
# sshkey-tools
[![PyPI version](https://badge.fury.io/py/sshkey-tools.svg)](https://badge.fury.io/py/sshkey-tools)
![Linting](https://github.com/scheiblingco/sshkey-tools/actions/workflows/linting.yml/badge.svg)
![Testing-Dev](https://github.com/scheiblingco/sshkey-tools/actions/workflows/dev_testing.yml/badge.svg)
![Testing-Build](https://github.com/scheiblingco/sshkey-tools/actions/workflows/deploy_testing.yml/badge.svg)
![Build](https://github.com/scheiblingco/sshkey-tools/actions/workflows/publish.yml/badge.svg)
[![CodeQL](https://github.com/scheiblingco/sshkey-tools/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/scheiblingco/sshkey-tools/actions/workflows/github-code-scanning/codeql)



Python package for managing OpenSSH keypairs and certificates ([protocol.CERTKEYS](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys)). Supported functionality includes:

## Notice
The DSA algorithm has been deprecated and is removed in pyca/cryptography 41.x, meaning **version 0.9.* of this package will be the last to support DSA keys and certificates** for SSH. If there is any demand to reintroduce DSA support, please open an issue regarding this and we'll look into it. 

For now, **0.9.* will be restricted to version <41.1 of the cryptography package** and **0.10 will have its DSA support removed**. We've introduced a deprecation notice in version 0.9.3.

### Background
The DSA algorithm is considered deprecated and will be removed in a future version. If possible, use RSA, [(ECDSA)](https://billatnapier.medium.com/ecdsa-weakness-where-nonces-are-reused-2be63856a01a) or ED25519 as a first-hand choice.

### Notice from OpenSSH:
```
OpenSSH 7.0 and greater similarly disable the ssh-dss (DSA) public key algorithm. It too is weak and we recommend against its use. It can be re-enabled using the HostKeyAlgorithms configuration option: sshd_config(5) HostKeyAlgorithms
```

[ECDSA has some flaws](https://billatnapier.medium.com/ecdsa-weakness-where-nonces-are-reused-2be63856a01a), especially when using short nonces or re-using nonces, it can still be used but exercise some caution in regards to nonces/re-signing identical data multiple times.

# Features
### SSH Keys
- Supports RSA, ECDSA and ED25519 keys
- Import existing keys from file, string, byte data or [pyca/cryptography](https://github.com/pyca/cryptography) class
- Generate new keys
- Get public key from private keys
- Sign bytestrings with private keys
- Export to file, string or bytes
- Generate fingerprint

### OpenSSH Certificates
- Supports RSA, ECDSA and ED25519 certificates
- Import existing certificates from file, string or bytes
- Verify certificate signature against internal or separate public key
- Create new certificates from CA private key and subject public key
- Create new certificates using old certificate as template
- Sign certificates
- Export certificates to file, string or bytes

# Roadmap
See issues for planned features and fixes

# Installation

## With pip

```bash
pip3 install sshkey-tools
# or
pip3 install -e git+https://github.com/scheiblingco/sshkey-tools.git
```

## From source

```bash
git clone https://github.com/scheiblingco/sshkey-tools
cd sshkey-tools
pip3 install ./
```

# Documentation
You can find the full documentation at [scheiblingco.github.io/sshkey-tools/](https://scheiblingco.github.io/sshkey-tools/)

## Building the documentation
```bash
pdoc3 src/sshkey_tools/ -o docs --html
cp -rf docs/sshkey_tools/* docs/
rm -rf docs/sshkey_tools
```

## SSH Keypairs (generating, loading, exporting)
```python
# Import the certificate classes
from sshkey_tools.keys import (
    RsaPrivateKey,
    EcdsaPrivateKey,
    Ed25519PrivateKey,
    EcdsaCurves
)
#
## Generating keys
#

# For all keys except ED25519, the key size/curve can be manually specified
# Generate RSA (default is 4096 bits)
rsa_priv = RsaPrivateKey.generate()
rsa_priv = RsaPrivateKey.generate(2048)

# Generate DSA keys (since SSH only supports 1024-bit keys, this is the default)
# DEPRECATED
# dsa_priv = DsaPrivateKey.generate()

# Generate ECDSA keys (The default curve is P521)
ecdsa_priv = EcdsaPrivateKey.generate()
ecdsa_priv = EcdsaPrivateKey.generate(EcdsaCurves.P256)

# Generate ED25519 keys (fixed key size)
ed25519_priv = Ed25519PrivateKey.generate()

#
## Loading keys
#

# Keys can be loaded either via the specific class:
rsa_priv = RsaPrivateKey.from_file("/path/to/key", "OptionalSecurePassword")

# or via the general class, in case the type is not known in advance
rsa_priv = PrivateKey.from_file("/path/to/key", "OptionalSecurePassword")

# The import functions are .from_file(), .from_string() and .from_class() and are valid for both PublicKey and PrivateKey-classes
rsa_priv = PrivateKey.from_string("-----BEGIN OPENSSH PRIVATE KEY...........END -----", "OptionalSecurePassword")
rsa_priv = PrivateKey.from_class(pyca_cryptography_class)

# The different keys can also be loaded from their numbers, e.g. RSA Pubkey:
rsa_priv = PublicKey.from_numbers(65537, 123123123....1)

#
## Key functionality
#

# The public key for any loaded or generated private key is available in the .public_key attribute
ed25519_pub = ed25519_priv.public_key

# The private keys can be exported using to_bytes, to_string or to_file
rsa_priv.to_bytes("OptionalSecurePassword")
rsa_priv.to_string("OptionalSecurePassword", "utf-8")
rsa_priv.to_file("/path/to/file", "OptionalSecurePassword", "utf-8")

# The public keys also have .to_string() and .to_file(), but .to_bytes() is divided into .serialize() and .raw_bytes()
# The comment can be set before export by changing the public_key.comment-attribute
rsa_priv.public_key.comment = "Comment@Comment"

# This will return the serialized public key as found in an OpenSSH keyfile
rsa_priv.public_key.serialize()
b"ssh-rsa AAAA......... Comment@Comment"

# This will return the raw bytes of the key (base64-decoded middle portion)
rsa_priv.public_key.raw_bytes()
b"\0xc\0a\........"
```

## SSH Key Signatures
The loaded private key objects can be used to sign bytestrings, and the public keys can be used to verify signatures on those
```python
from sshkey_tools.keys import RsaPrivateKey, RsaPublicKey
from sshkey_tools.fields import RsaAlgs

signable_data = b'This is a message that will be signed'

privkey = RsaPrivateKey.generate()
pubkey = RsaPrivateKey.public_key

# Sign the data
signature = privkey.sign(signable_data)

# When using an RSA key for the signature, you can specify the hashing algorithm
# The default algorithm is SHA512
signature = privkey.sign(signable_data, RsaAlgs.SHA512)

# Verify the signature (Throws exception if invalid)
pubkey.verify(signable_data, signature)
```

## OpenSSH Certificates
### Introduction
Certificates are a way to handle access management/PAM for OpenSSH with the ability to dynamically grant access during a specific time, to specific servers and/or with specific attributes. There are a couple of upsides to using certificates instead of public/private keys, mainly: 

- Additional Security: Certificate authentication for OpenSSH is built as an extension of public key authentication, enabling additional features on top of key-based access control.
- Short-term access: The user has to request a certificate for their keypair, which together with the private key grants access to the server. Without the certificate the user can't connect to the server - giving you control over how, when and from where the user can connect.
- Hostkey Verification: Certificiates can be issued for the OpenSSH Server, adding the CA public key to the clients enables you to establish servers as trusted without the hostkey warning.
- RBAC: Control which servers or users (principals) a keypair has access to, and specify the required principals for access to certain functionality on the server side.
- Logging: Key ID and Serial fields for tracking of issued certificates
- CRL: Revoke certificates prematurely if they are compromised

### Structure
The original OpenSSH certificate format is a block of parameters, encoded and packed to a bytestring. In this package, the fields have been divided into three parts. For a more detailed information about the format, see [PROTOCOL.certkeys](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys).

### Certificate Header
|Attribute|Type(Length)|Key|Example Value|Description|
|---|---|---|---|---|
|Public Key/Certificate type|string(fixed)|pubkey_type|ssh-rsa-sha2-512-cert-v01@openssh.com|The private key (and certificate) type, derived from the public key for which the certificate is created (Automatically set upon creation)|
|Subject public key|bytestring(variable)|public_key|\x00\x00\x00..........|The public key for which the certificate is created (Automatically set upon creation)|
|Nonce|string|nonce(variable, typically 16 or 32 bytes)|abcdefghijklmnopqrstuvwxyz|A random string included to make attacks that depend on inducing collisions in the signature hash infeasible. (Default is automatically set, can be changed with Certificate.header.nonce = "abcdefg..."|

### Certificate Fields
|Attribute|Type(Length)|Key|Example Value|Description|
|---|---|---|---|---|
|Serial|Integer(64-bit)|serial|1234567890|An optional certificate serial number set by the CA to provide an abbreviated way to refer to certificates from that CA. If a CA does not wish to number its certificates, it must set this field to zero.|
|Certificate type|Integer(1 or 2)|cert_type|1|The type of the certificate, 1 for user certificates, 2 for host certificates|
|Key ID|string(variable)|key_id|someuser@somehost|Free-form text field that is filled in by the CA at the time of signing; the intention is that the contents of this field are used to identify the identity principal in log messages.|
|Valid Principals|List(string(variable))|principals|['some-user', 'some-group', production-webservers']|These principals list the names for which this certificate is valid hostnames for SSH_CERT_TYPE_HOST certificates and usernames for  SH_CERT_TYPE_USER certificates. As a special case, a zero-length "valid principals" field means the certificate is valid for any principal of the specified type.|
|Valid After|Timestamp|valid_after|datetime.now()|Timestamp for the start of the validity period for the certificate|
|Valid Before|Timestamp|valid_before|datetime.now()+timedelta(hours=8) or 1658322031|Timestamp for the end of the validity period for the certificate. Needs to be larger than valid_after, can be a string (ex. 2d, 2w, 1h4m, 99d) or forever (MAX_INT64)|
|Critical Options|Dict(string, string)|critical_options|[]|Zero or more of the available critical options (see below)|
|Extensions|Dict(string, string)/List/Tuple/Set|extensions|[]|Zero or more of the available extensions (see below)|


#### Critical Options
|Name|Format|Description|
|---|---|---|
|force-command|string|Specifies a command that is executed (replacing any the user specified on the ssh command-line) whenever this key is used for authentication.|
|source-address|string|Comma-separated list of source addresses from which this certificate is accepted for authentication. Addresses are specified in CIDR format (nn.nn.nn.nn/nn or hhhh::hhhh/nn). If this option is not present, then certificates may be presented from any source address.|
|verify-required|empty|Flag indicating that signatures made with this certificate must assert FIDO user verification (e.g. PIN or biometric). This option only makes sense for the U2F/FIDO security key types that support this feature in their signature formats.|

#### Extensions
|Name|Format|Description|
|---|---|---|
|no-touch-required|empty|Flag indicating that signatures made with this certificate need not assert FIDO user presence. This option only makes sense for the U2F/FIDO security key types that support this feature in their signature formats.|
|permit-X11-forwarding|empty|Flag indicating that X11 forwarding should be permitted. X11 forwarding will be refused if this option is absent.|
|permit-agent-forwarding|empty|Flag indicating that agent forwarding should be allowed. Agent forwarding must not be permitted unless this option is present.|
|permit-port-forwarding|empty|Flag indicating that port-forwarding should be allowed. If this option is not present, then no port forwarding will be allowed.|
|permit-pty|empty|Flag indicating that PTY allocation should be permitted. In the absence of this option PTY allocation will be disabled.|
|permit-user-rc|empty|Flag indicating that execution of ~/.ssh/rc should be permitted. Execution of this script will not be permitted if this option is not present.|


### Certificate Body
|Attribute|Type(Length)|Key|Example Value|Description|
|---|---|---|---|---|
|Reserved|string(0)|reserved|""|Reserved for future use, must be empty (automatically set upon signing)|
|CA Public Key|bytestring(variable)|ca_pubkey|\x00\x00\x00..........|The public key of the CA that issued this certificate (automatically set upon signing)|
|Signature|bytestring(variable)|signature|\x00\x00\x00..........|The signature of the certificate, created by the CA (automatically set upon signing)|

## Creating, signing and verifying certificates
```python
# Every certificate needs two parts, the subject (user or host) public key and the CA Private key
from sshkey_tools.cert import SSHCertificate, CertificateFields, Ed25519Certificate
from sshkey_tools.keys import Ed25519PrivateKey
from datetime import datetime, timedelta

subject_pubkey = Ed25519PrivateKey.generate().public_key
ca_privkey = Ed25519PrivateKey.generate()

# There are multiple ways to create a certificate, either by creating the certificate body field object first and then creating the certificate, or creating the certificate and setting the fields one by one

# Create certificate body fields
cert_fields = CertificateFields(
    serial=1234567890,
    cert_type=1,
    key_id="someuser@somehost",
    principals=["some-user", "some-group", "production-webservers"],
    valid_after=datetime.now(),
    valid_before=datetime.now() + timedelta(hours=8),
    critical_options=[],
    extensions=[
        "permit-pty",
        "permit-X11-forwarding",
        "permit-agent-forwarding",
    ],
)

# Create certificate from existing fields
certificate = SSHCertificate(
    subject_pubkey=subject_pubkey,
    ca_privkey=ca_privkey,
    fields=cert_fields,
)

# Start with a blank certificate by calling the general class
certificate = SSHCertificate.create(
    subject_pubkey=subject_pubkey,
    ca_privkey=ca_privkey
)

# You can also call the specialized classes directly, for the general class the .create-function needs to be used
certificate = Ed25519Certificate(
    subject_pubkey=subject_pubkey,
    ca_privkey=ca_privkey
)

# Manually set the fields
certificate.fields.serial = 1234567890
certificate.fields.cert_type = 1
certificate.fields.key_id = "someuser@somehost"
certificate.fields.principals = ["some-user", "some-group", "production-webservers"]
certificate.fields.valid_after = datetime.now()
certificate.fields.valid_before = datetime.now() + timedelta(hours=8)
certificate.fields.critical_options = []
certificate.fields.extensions = [
    "allow-pty",
    "permit-X11-forwarding",
    "permit-agent-forwarding",
]

# Check if the certificate is ready to be signed
certificate.can_sign()

# Sign the certificate
certificate.sign()

# Verify the certificate against the included public key (insecure, but useful for testing)
certificate.verify()

# Verify the certificate against a public key that is not included in the certificate
certificate.verify(ca_privkey.public_key)

# Raise an exception if the certificate is invalid
certificate.verify(ca_privkey.public_key, True)

# Export the certificate to file/string
certificate.to_file('filename-cert.pub')
cert_str = certificate.to_string()

```
## Loading, re-creating and verifying existing certificates
```python
from sshkey_tools.cert import SSHCertificate, CertificateFields, Ed25519Certificate
from sshkey_tools.keys import PublicKey, PrivateKey
from datetime import datetime, timedelta

# Load a certificate from file or string
# This will return the correct certificate type based on the contents of the certificate
certificate = SSHCertificate.from_file('filename-cert.pub')
certificate = SSHCertificate.from_string(cert_str)

type(certificate) # sshkey_tools.cert.Ed25519Certificate

# Verify the certificate signature against the included public key (insecure, but useful for testing)
certificate.verify()

# Verify the certificate signature against a public key
pubkey = PublicKey.from_file('filename-pubkey.pub')
certificate.verify(pubkey)

# Raise an exception if the certificate is invalid
certificate.verify(pubkey, True)

# Use the loaded certificate as a template to create a new one
new_ca = PrivateKey.from_file('filename-ca')
certificate.replace_ca(new_ca)
certificate.sign()

```

## Changelog
### 0.9.1
- Updated documentation
- Fix for bug where exception would occur when trying to export a key without a comment set

### 0.9
- Adjustments to certificate field handling for easier usage/syntax autocompletion
- Updated testing
- Removed method for changing RSA hash method (now default SHA512)

### 0.8.2
- Fixed bug where an RSA certificate would send the RSA alg to the sign() function of another key type

### 0.8.1
- Changed versioning for out-of-github installation/packaging
- Moved documentation to HTML (PDOC3)
- Added verification of certificate signature
- Added option to choose RSA hashing algorithm for signing
- Removed test files
- Added documentation deployment CD for GH pages

### 0.8
- Initial public release

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/scheiblingco/sshkey-tools",
    "name": "sshkey-tools",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.6",
    "maintainer_email": "",
    "keywords": "python openssh ssh keys sshkey ssh-keygen ssh-certificate certificate parser decoder",
    "author": "Lars Scheibling",
    "author_email": "lars@scheibling.se",
    "download_url": "https://files.pythonhosted.org/packages/ae/30/ff58299227744ed63476fc56a198afc5ce3b078c1ff8bbe7f5c4da6f7572/sshkey-tools-0.10.3.tar.gz",
    "platform": null,
    "description": "![SSHKey Tools](./assets/sshkey-tools-plain.svg)\n# sshkey-tools\n[![PyPI version](https://badge.fury.io/py/sshkey-tools.svg)](https://badge.fury.io/py/sshkey-tools)\n![Linting](https://github.com/scheiblingco/sshkey-tools/actions/workflows/linting.yml/badge.svg)\n![Testing-Dev](https://github.com/scheiblingco/sshkey-tools/actions/workflows/dev_testing.yml/badge.svg)\n![Testing-Build](https://github.com/scheiblingco/sshkey-tools/actions/workflows/deploy_testing.yml/badge.svg)\n![Build](https://github.com/scheiblingco/sshkey-tools/actions/workflows/publish.yml/badge.svg)\n[![CodeQL](https://github.com/scheiblingco/sshkey-tools/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/scheiblingco/sshkey-tools/actions/workflows/github-code-scanning/codeql)\n\n\n\nPython package for managing OpenSSH keypairs and certificates ([protocol.CERTKEYS](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys)). Supported functionality includes:\n\n## Notice\nThe DSA algorithm has been deprecated and is removed in pyca/cryptography 41.x, meaning **version 0.9.* of this package will be the last to support DSA keys and certificates** for SSH. If there is any demand to reintroduce DSA support, please open an issue regarding this and we'll look into it. \n\nFor now, **0.9.* will be restricted to version <41.1 of the cryptography package** and **0.10 will have its DSA support removed**. We've introduced a deprecation notice in version 0.9.3.\n\n### Background\nThe DSA algorithm is considered deprecated and will be removed in a future version. If possible, use RSA, [(ECDSA)](https://billatnapier.medium.com/ecdsa-weakness-where-nonces-are-reused-2be63856a01a) or ED25519 as a first-hand choice.\n\n### Notice from OpenSSH:\n```\nOpenSSH 7.0 and greater similarly disable the ssh-dss (DSA) public key algorithm. It too is weak and we recommend against its use. It can be re-enabled using the HostKeyAlgorithms configuration option: sshd_config(5) HostKeyAlgorithms\n```\n\n[ECDSA has some flaws](https://billatnapier.medium.com/ecdsa-weakness-where-nonces-are-reused-2be63856a01a), especially when using short nonces or re-using nonces, it can still be used but exercise some caution in regards to nonces/re-signing identical data multiple times.\n\n# Features\n### SSH Keys\n- Supports RSA, ECDSA and ED25519 keys\n- Import existing keys from file, string, byte data or [pyca/cryptography](https://github.com/pyca/cryptography) class\n- Generate new keys\n- Get public key from private keys\n- Sign bytestrings with private keys\n- Export to file, string or bytes\n- Generate fingerprint\n\n### OpenSSH Certificates\n- Supports RSA, ECDSA and ED25519 certificates\n- Import existing certificates from file, string or bytes\n- Verify certificate signature against internal or separate public key\n- Create new certificates from CA private key and subject public key\n- Create new certificates using old certificate as template\n- Sign certificates\n- Export certificates to file, string or bytes\n\n# Roadmap\nSee issues for planned features and fixes\n\n# Installation\n\n## With pip\n\n```bash\npip3 install sshkey-tools\n# or\npip3 install -e git+https://github.com/scheiblingco/sshkey-tools.git\n```\n\n## From source\n\n```bash\ngit clone https://github.com/scheiblingco/sshkey-tools\ncd sshkey-tools\npip3 install ./\n```\n\n# Documentation\nYou can find the full documentation at [scheiblingco.github.io/sshkey-tools/](https://scheiblingco.github.io/sshkey-tools/)\n\n## Building the documentation\n```bash\npdoc3 src/sshkey_tools/ -o docs --html\ncp -rf docs/sshkey_tools/* docs/\nrm -rf docs/sshkey_tools\n```\n\n## SSH Keypairs (generating, loading, exporting)\n```python\n# Import the certificate classes\nfrom sshkey_tools.keys import (\n    RsaPrivateKey,\n    EcdsaPrivateKey,\n    Ed25519PrivateKey,\n    EcdsaCurves\n)\n#\n## Generating keys\n#\n\n# For all keys except ED25519, the key size/curve can be manually specified\n# Generate RSA (default is 4096 bits)\nrsa_priv = RsaPrivateKey.generate()\nrsa_priv = RsaPrivateKey.generate(2048)\n\n# Generate DSA keys (since SSH only supports 1024-bit keys, this is the default)\n# DEPRECATED\n# dsa_priv = DsaPrivateKey.generate()\n\n# Generate ECDSA keys (The default curve is P521)\necdsa_priv = EcdsaPrivateKey.generate()\necdsa_priv = EcdsaPrivateKey.generate(EcdsaCurves.P256)\n\n# Generate ED25519 keys (fixed key size)\ned25519_priv = Ed25519PrivateKey.generate()\n\n#\n## Loading keys\n#\n\n# Keys can be loaded either via the specific class:\nrsa_priv = RsaPrivateKey.from_file(\"/path/to/key\", \"OptionalSecurePassword\")\n\n# or via the general class, in case the type is not known in advance\nrsa_priv = PrivateKey.from_file(\"/path/to/key\", \"OptionalSecurePassword\")\n\n# The import functions are .from_file(), .from_string() and .from_class() and are valid for both PublicKey and PrivateKey-classes\nrsa_priv = PrivateKey.from_string(\"-----BEGIN OPENSSH PRIVATE KEY...........END -----\", \"OptionalSecurePassword\")\nrsa_priv = PrivateKey.from_class(pyca_cryptography_class)\n\n# The different keys can also be loaded from their numbers, e.g. RSA Pubkey:\nrsa_priv = PublicKey.from_numbers(65537, 123123123....1)\n\n#\n## Key functionality\n#\n\n# The public key for any loaded or generated private key is available in the .public_key attribute\ned25519_pub = ed25519_priv.public_key\n\n# The private keys can be exported using to_bytes, to_string or to_file\nrsa_priv.to_bytes(\"OptionalSecurePassword\")\nrsa_priv.to_string(\"OptionalSecurePassword\", \"utf-8\")\nrsa_priv.to_file(\"/path/to/file\", \"OptionalSecurePassword\", \"utf-8\")\n\n# The public keys also have .to_string() and .to_file(), but .to_bytes() is divided into .serialize() and .raw_bytes()\n# The comment can be set before export by changing the public_key.comment-attribute\nrsa_priv.public_key.comment = \"Comment@Comment\"\n\n# This will return the serialized public key as found in an OpenSSH keyfile\nrsa_priv.public_key.serialize()\nb\"ssh-rsa AAAA......... Comment@Comment\"\n\n# This will return the raw bytes of the key (base64-decoded middle portion)\nrsa_priv.public_key.raw_bytes()\nb\"\\0xc\\0a\\........\"\n```\n\n## SSH Key Signatures\nThe loaded private key objects can be used to sign bytestrings, and the public keys can be used to verify signatures on those\n```python\nfrom sshkey_tools.keys import RsaPrivateKey, RsaPublicKey\nfrom sshkey_tools.fields import RsaAlgs\n\nsignable_data = b'This is a message that will be signed'\n\nprivkey = RsaPrivateKey.generate()\npubkey = RsaPrivateKey.public_key\n\n# Sign the data\nsignature = privkey.sign(signable_data)\n\n# When using an RSA key for the signature, you can specify the hashing algorithm\n# The default algorithm is SHA512\nsignature = privkey.sign(signable_data, RsaAlgs.SHA512)\n\n# Verify the signature (Throws exception if invalid)\npubkey.verify(signable_data, signature)\n```\n\n## OpenSSH Certificates\n### Introduction\nCertificates are a way to handle access management/PAM for OpenSSH with the ability to dynamically grant access during a specific time, to specific servers and/or with specific attributes. There are a couple of upsides to using certificates instead of public/private keys, mainly: \n\n- Additional Security: Certificate authentication for OpenSSH is built as an extension of public key authentication, enabling additional features on top of key-based access control.\n- Short-term access: The user has to request a certificate for their keypair, which together with the private key grants access to the server. Without the certificate the user can't connect to the server - giving you control over how, when and from where the user can connect.\n- Hostkey Verification: Certificiates can be issued for the OpenSSH Server, adding the CA public key to the clients enables you to establish servers as trusted without the hostkey warning.\n- RBAC: Control which servers or users (principals) a keypair has access to, and specify the required principals for access to certain functionality on the server side.\n- Logging: Key ID and Serial fields for tracking of issued certificates\n- CRL: Revoke certificates prematurely if they are compromised\n\n### Structure\nThe original OpenSSH certificate format is a block of parameters, encoded and packed to a bytestring. In this package, the fields have been divided into three parts. For a more detailed information about the format, see [PROTOCOL.certkeys](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys).\n\n### Certificate Header\n|Attribute|Type(Length)|Key|Example Value|Description|\n|---|---|---|---|---|\n|Public Key/Certificate type|string(fixed)|pubkey_type|ssh-rsa-sha2-512-cert-v01@openssh.com|The private key (and certificate) type, derived from the public key for which the certificate is created (Automatically set upon creation)|\n|Subject public key|bytestring(variable)|public_key|\\x00\\x00\\x00..........|The public key for which the certificate is created (Automatically set upon creation)|\n|Nonce|string|nonce(variable, typically 16 or 32 bytes)|abcdefghijklmnopqrstuvwxyz|A random string included to make attacks that depend on inducing collisions in the signature hash infeasible. (Default is automatically set, can be changed with Certificate.header.nonce = \"abcdefg...\"|\n\n### Certificate Fields\n|Attribute|Type(Length)|Key|Example Value|Description|\n|---|---|---|---|---|\n|Serial|Integer(64-bit)|serial|1234567890|An optional certificate serial number set by the CA to provide an abbreviated way to refer to certificates from that CA. If a CA does not wish to number its certificates, it must set this field to zero.|\n|Certificate type|Integer(1 or 2)|cert_type|1|The type of the certificate, 1 for user certificates, 2 for host certificates|\n|Key ID|string(variable)|key_id|someuser@somehost|Free-form text field that is filled in by the CA at the time of signing; the intention is that the contents of this field are used to identify the identity principal in log messages.|\n|Valid Principals|List(string(variable))|principals|['some-user', 'some-group', production-webservers']|These principals list the names for which this certificate is valid hostnames for SSH_CERT_TYPE_HOST certificates and usernames for  SH_CERT_TYPE_USER certificates. As a special case, a zero-length \"valid principals\" field means the certificate is valid for any principal of the specified type.|\n|Valid After|Timestamp|valid_after|datetime.now()|Timestamp for the start of the validity period for the certificate|\n|Valid Before|Timestamp|valid_before|datetime.now()+timedelta(hours=8) or 1658322031|Timestamp for the end of the validity period for the certificate. Needs to be larger than valid_after, can be a string (ex. 2d, 2w, 1h4m, 99d) or forever (MAX_INT64)|\n|Critical Options|Dict(string, string)|critical_options|[]|Zero or more of the available critical options (see below)|\n|Extensions|Dict(string, string)/List/Tuple/Set|extensions|[]|Zero or more of the available extensions (see below)|\n\n\n#### Critical Options\n|Name|Format|Description|\n|---|---|---|\n|force-command|string|Specifies a command that is executed (replacing any the user specified on the ssh command-line) whenever this key is used for authentication.|\n|source-address|string|Comma-separated list of source addresses from which this certificate is accepted for authentication. Addresses are specified in CIDR format (nn.nn.nn.nn/nn or hhhh::hhhh/nn). If this option is not present, then certificates may be presented from any source address.|\n|verify-required|empty|Flag indicating that signatures made with this certificate must assert FIDO user verification (e.g. PIN or biometric). This option only makes sense for the U2F/FIDO security key types that support this feature in their signature formats.|\n\n#### Extensions\n|Name|Format|Description|\n|---|---|---|\n|no-touch-required|empty|Flag indicating that signatures made with this certificate need not assert FIDO user presence. This option only makes sense for the U2F/FIDO security key types that support this feature in their signature formats.|\n|permit-X11-forwarding|empty|Flag indicating that X11 forwarding should be permitted. X11 forwarding will be refused if this option is absent.|\n|permit-agent-forwarding|empty|Flag indicating that agent forwarding should be allowed. Agent forwarding must not be permitted unless this option is present.|\n|permit-port-forwarding|empty|Flag indicating that port-forwarding should be allowed. If this option is not present, then no port forwarding will be allowed.|\n|permit-pty|empty|Flag indicating that PTY allocation should be permitted. In the absence of this option PTY allocation will be disabled.|\n|permit-user-rc|empty|Flag indicating that execution of ~/.ssh/rc should be permitted. Execution of this script will not be permitted if this option is not present.|\n\n\n### Certificate Body\n|Attribute|Type(Length)|Key|Example Value|Description|\n|---|---|---|---|---|\n|Reserved|string(0)|reserved|\"\"|Reserved for future use, must be empty (automatically set upon signing)|\n|CA Public Key|bytestring(variable)|ca_pubkey|\\x00\\x00\\x00..........|The public key of the CA that issued this certificate (automatically set upon signing)|\n|Signature|bytestring(variable)|signature|\\x00\\x00\\x00..........|The signature of the certificate, created by the CA (automatically set upon signing)|\n\n## Creating, signing and verifying certificates\n```python\n# Every certificate needs two parts, the subject (user or host) public key and the CA Private key\nfrom sshkey_tools.cert import SSHCertificate, CertificateFields, Ed25519Certificate\nfrom sshkey_tools.keys import Ed25519PrivateKey\nfrom datetime import datetime, timedelta\n\nsubject_pubkey = Ed25519PrivateKey.generate().public_key\nca_privkey = Ed25519PrivateKey.generate()\n\n# There are multiple ways to create a certificate, either by creating the certificate body field object first and then creating the certificate, or creating the certificate and setting the fields one by one\n\n# Create certificate body fields\ncert_fields = CertificateFields(\n    serial=1234567890,\n    cert_type=1,\n    key_id=\"someuser@somehost\",\n    principals=[\"some-user\", \"some-group\", \"production-webservers\"],\n    valid_after=datetime.now(),\n    valid_before=datetime.now() + timedelta(hours=8),\n    critical_options=[],\n    extensions=[\n        \"permit-pty\",\n        \"permit-X11-forwarding\",\n        \"permit-agent-forwarding\",\n    ],\n)\n\n# Create certificate from existing fields\ncertificate = SSHCertificate(\n    subject_pubkey=subject_pubkey,\n    ca_privkey=ca_privkey,\n    fields=cert_fields,\n)\n\n# Start with a blank certificate by calling the general class\ncertificate = SSHCertificate.create(\n    subject_pubkey=subject_pubkey,\n    ca_privkey=ca_privkey\n)\n\n# You can also call the specialized classes directly, for the general class the .create-function needs to be used\ncertificate = Ed25519Certificate(\n    subject_pubkey=subject_pubkey,\n    ca_privkey=ca_privkey\n)\n\n# Manually set the fields\ncertificate.fields.serial = 1234567890\ncertificate.fields.cert_type = 1\ncertificate.fields.key_id = \"someuser@somehost\"\ncertificate.fields.principals = [\"some-user\", \"some-group\", \"production-webservers\"]\ncertificate.fields.valid_after = datetime.now()\ncertificate.fields.valid_before = datetime.now() + timedelta(hours=8)\ncertificate.fields.critical_options = []\ncertificate.fields.extensions = [\n    \"allow-pty\",\n    \"permit-X11-forwarding\",\n    \"permit-agent-forwarding\",\n]\n\n# Check if the certificate is ready to be signed\ncertificate.can_sign()\n\n# Sign the certificate\ncertificate.sign()\n\n# Verify the certificate against the included public key (insecure, but useful for testing)\ncertificate.verify()\n\n# Verify the certificate against a public key that is not included in the certificate\ncertificate.verify(ca_privkey.public_key)\n\n# Raise an exception if the certificate is invalid\ncertificate.verify(ca_privkey.public_key, True)\n\n# Export the certificate to file/string\ncertificate.to_file('filename-cert.pub')\ncert_str = certificate.to_string()\n\n```\n## Loading, re-creating and verifying existing certificates\n```python\nfrom sshkey_tools.cert import SSHCertificate, CertificateFields, Ed25519Certificate\nfrom sshkey_tools.keys import PublicKey, PrivateKey\nfrom datetime import datetime, timedelta\n\n# Load a certificate from file or string\n# This will return the correct certificate type based on the contents of the certificate\ncertificate = SSHCertificate.from_file('filename-cert.pub')\ncertificate = SSHCertificate.from_string(cert_str)\n\ntype(certificate) # sshkey_tools.cert.Ed25519Certificate\n\n# Verify the certificate signature against the included public key (insecure, but useful for testing)\ncertificate.verify()\n\n# Verify the certificate signature against a public key\npubkey = PublicKey.from_file('filename-pubkey.pub')\ncertificate.verify(pubkey)\n\n# Raise an exception if the certificate is invalid\ncertificate.verify(pubkey, True)\n\n# Use the loaded certificate as a template to create a new one\nnew_ca = PrivateKey.from_file('filename-ca')\ncertificate.replace_ca(new_ca)\ncertificate.sign()\n\n```\n\n## Changelog\n### 0.9.1\n- Updated documentation\n- Fix for bug where exception would occur when trying to export a key without a comment set\n\n### 0.9\n- Adjustments to certificate field handling for easier usage/syntax autocompletion\n- Updated testing\n- Removed method for changing RSA hash method (now default SHA512)\n\n### 0.8.2\n- Fixed bug where an RSA certificate would send the RSA alg to the sign() function of another key type\n\n### 0.8.1\n- Changed versioning for out-of-github installation/packaging\n- Moved documentation to HTML (PDOC3)\n- Added verification of certificate signature\n- Added option to choose RSA hashing algorithm for signing\n- Removed test files\n- Added documentation deployment CD for GH pages\n\n### 0.8\n- Initial public release\n",
    "bugtrack_url": null,
    "license": "GnuPG 3.0",
    "summary": "A Python module for generating, parsing and handling OpenSSH keys and certificates",
    "version": "0.10.3",
    "project_urls": {
        "Homepage": "https://github.com/scheiblingco/sshkey-tools"
    },
    "split_keywords": [
        "python",
        "openssh",
        "ssh",
        "keys",
        "sshkey",
        "ssh-keygen",
        "ssh-certificate",
        "certificate",
        "parser",
        "decoder"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "7da92dded3a85d7b3718b4e9b8852db3feef0e6f9df616295d88a42820248295",
                "md5": "aab762ea1576fc744e0a351358f6cce7",
                "sha256": "bc94ea902959e5b87012418803307390bb671ea48fd67afa19807bd4bb9a4147"
            },
            "downloads": -1,
            "filename": "sshkey_tools-0.10.3-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "aab762ea1576fc744e0a351358f6cce7",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.6",
            "size": 42710,
            "upload_time": "2024-01-23T14:01:20",
            "upload_time_iso_8601": "2024-01-23T14:01:20.438124Z",
            "url": "https://files.pythonhosted.org/packages/7d/a9/2dded3a85d7b3718b4e9b8852db3feef0e6f9df616295d88a42820248295/sshkey_tools-0.10.3-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "ae30ff58299227744ed63476fc56a198afc5ce3b078c1ff8bbe7f5c4da6f7572",
                "md5": "e882efa12eb162f639714568cfef462f",
                "sha256": "61126f9b486b941c7ffb24dc18c4dcc7016f8abd71938f8530a3a7b285edd5e9"
            },
            "downloads": -1,
            "filename": "sshkey-tools-0.10.3.tar.gz",
            "has_sig": false,
            "md5_digest": "e882efa12eb162f639714568cfef462f",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.6",
            "size": 46049,
            "upload_time": "2024-01-23T14:01:23",
            "upload_time_iso_8601": "2024-01-23T14:01:23.715825Z",
            "url": "https://files.pythonhosted.org/packages/ae/30/ff58299227744ed63476fc56a198afc5ce3b078c1ff8bbe7f5c4da6f7572/sshkey-tools-0.10.3.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-01-23 14:01:23",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "scheiblingco",
    "github_project": "sshkey-tools",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [
        {
            "name": "click",
            "specs": [
                [
                    "~=",
                    "8.1"
                ]
            ]
        },
        {
            "name": "cryptography",
            "specs": [
                [
                    "~=",
                    "41.0.0"
                ]
            ]
        },
        {
            "name": "bcrypt",
            "specs": [
                [
                    "~=",
                    "4.1"
                ]
            ]
        },
        {
            "name": "PrettyTable",
            "specs": [
                [
                    "~=",
                    "3.9"
                ]
            ]
        },
        {
            "name": "pytimeparse2",
            "specs": [
                [
                    "~=",
                    "1.1"
                ]
            ]
        }
    ],
    "lcname": "sshkey-tools"
}
        
Elapsed time: 0.16672s