kiwi-vpn/api/kiwi_vpn_api/easyrsa.py

160 lines
4.2 KiB
Python
Raw Normal View History

2022-03-30 21:18:54 +00:00
"""
Python interface to EasyRSA CA.
"""
2022-03-22 00:34:06 +00:00
import subprocess
from datetime import datetime
from pathlib import Path
from OpenSSL import crypto
from passlib import pwd
2022-03-30 22:27:17 +00:00
from .config import CertificateAlgo, Config, Settings
2022-03-22 00:34:06 +00:00
2022-03-30 21:18:54 +00:00
class EasyRSA:
"""
Represents an EasyRSA PKI.
"""
@property
def pki_directory(self) -> Path:
return Settings._.data_dir.joinpath("pki")
@property
def ca_password(self) -> str:
config = Config._
if (ca_password := config.crypto.ca_password) is None:
ca_password = pwd.genword(
length=32,
charset="ascii_62",
)
2022-03-22 00:34:06 +00:00
2022-03-30 21:18:54 +00:00
config.crypto.ca_password = ca_password
config.save()
2022-03-22 00:34:06 +00:00
2022-03-30 21:18:54 +00:00
return config.crypto.ca_password
2022-03-22 00:34:06 +00:00
def __easyrsa(
self,
*easyrsa_args: str,
) -> subprocess.CompletedProcess:
return subprocess.run(
[
"easyrsa", "--batch",
2022-03-30 21:18:54 +00:00
f"--pki-dir={self.pki_directory}",
2022-03-22 00:34:06 +00:00
*easyrsa_args,
],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
check=True,
)
def __build_cert(
self,
cert_filename: Path,
2022-03-30 22:27:17 +00:00
expiry_days: int | None,
2022-03-22 00:34:06 +00:00
*easyrsa_args: str,
) -> crypto.X509:
2022-03-30 22:27:17 +00:00
config = Config._
extra_args: tuple[str] = tuple()
if expiry_days is not None:
extra_args += tuple([f"--days={expiry_days}"])
if (algo := config.crypto.cert_algo) is not None:
if algo is CertificateAlgo.rsa2048:
extra_args += ("--use-algo=rsa", "--keysize=2048")
elif algo is CertificateAlgo.rsa4096:
extra_args += ("--use-algo=rsa", "--keysize=4096")
elif algo is CertificateAlgo.secp256r1:
extra_args += ("--use-algo=ec", "--curve=secp256r1")
elif algo is CertificateAlgo.secp384r1:
extra_args += ("--use-algo=ec", "--curve=secp384r1")
elif algo is CertificateAlgo.ed25519:
extra_args += ("--use-algo=ed", "--curve=ed25519")
else:
raise ValueError(f"Unexpected algorithm: {algo}")
self.__easyrsa(
*extra_args,
*easyrsa_args
)
2022-03-22 00:34:06 +00:00
with open(
2022-03-30 21:18:54 +00:00
self.pki_directory.joinpath(cert_filename), "r"
2022-03-22 00:34:06 +00:00
) as cert_file:
return crypto.load_certificate(
crypto.FILETYPE_PEM, cert_file.read()
)
def init_pki(self) -> bool:
self.__easyrsa("init-pki")
2022-03-30 22:27:17 +00:00
def build_ca(self) -> crypto.X509:
2022-03-30 21:18:54 +00:00
config = Config._
2022-03-30 22:27:17 +00:00
server_dn = config.server_dn
2022-03-30 21:18:54 +00:00
2022-03-24 23:27:35 +00:00
cert = self.__build_cert(
2022-03-22 00:34:06 +00:00
Path("ca.crt"),
2022-03-30 22:27:17 +00:00
config.crypto.ca_expiry_days,
2022-03-22 00:34:06 +00:00
2022-03-30 21:18:54 +00:00
f"--passout=pass:{self.ca_password}",
f"--passin=pass:{self.ca_password}",
2022-03-22 00:34:06 +00:00
2022-03-30 22:27:17 +00:00
"--dn-mode=org",
f"--req-c={server_dn.country.value}",
f"--req-st={server_dn.state.value}",
f"--req-city={server_dn.city.value}",
f"--req-org={server_dn.organization.value}",
f"--req-ou={server_dn.organizational_unit.value}",
f"--req-email={server_dn.email.value}",
f"--req-cn={server_dn.common_name}",
2022-03-22 00:57:09 +00:00
2022-03-22 00:34:06 +00:00
"build-ca",
)
2022-03-30 21:18:54 +00:00
# self.__easyrsa("gen-dh")
2022-03-24 23:27:35 +00:00
return cert
2022-03-22 00:34:06 +00:00
def issue(
self,
2022-03-30 22:27:17 +00:00
cert_type: str = "client",
2022-03-22 00:57:09 +00:00
cn: str = "kiwi-vpn-client",
2022-03-22 00:34:06 +00:00
) -> crypto.X509:
2022-03-30 21:18:54 +00:00
config = Config._
2022-03-22 00:34:06 +00:00
return self.__build_cert(
Path(f"issued/{cn}.crt"),
2022-03-30 22:27:17 +00:00
config.crypto.cert_expiry_days,
2022-03-22 00:34:06 +00:00
2022-03-30 21:18:54 +00:00
f"--passin=pass:{self.ca_password}",
2022-03-22 00:34:06 +00:00
f"build-{cert_type}-full",
cn,
"nopass",
)
if __name__ == "__main__":
2022-03-30 21:18:54 +00:00
easy_rsa = EasyRSA()
2022-03-22 00:57:09 +00:00
easy_rsa.init_pki()
2022-03-22 00:34:06 +00:00
2022-03-30 21:18:54 +00:00
ca = easy_rsa.build_ca()
2022-03-22 00:57:09 +00:00
server = easy_rsa.issue(cert_type="server", cn="kiwi-vpn-server")
client = easy_rsa.issue(cert_type="client", cn="kiwi-vpn-client")
2022-03-22 00:34:06 +00:00
date_format, encoding = "%Y%m%d%H%M%SZ", "ascii"
2022-03-22 00:57:09 +00:00
for cert in [ca, server, client]:
print(cert.get_subject().CN)
print(cert.get_signature_algorithm().decode(encoding))
print(datetime.strptime(
cert.get_notAfter().decode(encoding), date_format))