mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-17 05:05:16 +03:00
Initial commit - 611 cybersecurity skills across all subdomains
This commit is contained in:
@@ -0,0 +1,349 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
SSL Certificate Lifecycle Management Tool
|
||||
|
||||
Implements certificate generation, parsing, monitoring, chain validation,
|
||||
and OCSP checking for managing TLS certificate lifecycles.
|
||||
|
||||
Requirements:
|
||||
pip install cryptography requests
|
||||
|
||||
Usage:
|
||||
python process.py generate-csr --domain example.com --output ./certs
|
||||
python process.py check-expiry --host example.com
|
||||
python process.py parse-cert --cert ./server.crt
|
||||
python process.py monitor --domains domains.txt --threshold 30
|
||||
python process.py verify-chain --cert ./server.crt --ca-bundle ./ca-bundle.crt
|
||||
"""
|
||||
|
||||
import os
|
||||
import ssl
|
||||
import sys
|
||||
import json
|
||||
import socket
|
||||
import argparse
|
||||
import logging
|
||||
import datetime
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from cryptography import x509
|
||||
from cryptography.x509.oid import NameOID, ExtensionOID
|
||||
from cryptography.hazmat.primitives import hashes, serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import ec, rsa
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
EXPIRY_WARNING_DAYS = 30
|
||||
EXPIRY_CRITICAL_DAYS = 15
|
||||
|
||||
|
||||
def generate_csr(
|
||||
domain: str,
|
||||
output_dir: str,
|
||||
key_type: str = "ecdsa",
|
||||
san_domains: Optional[List[str]] = None,
|
||||
organization: Optional[str] = None,
|
||||
) -> Dict:
|
||||
"""Generate a private key and CSR for a domain."""
|
||||
output_path = Path(output_dir)
|
||||
output_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if key_type == "ecdsa":
|
||||
private_key = ec.generate_private_key(ec.SECP256R1(), default_backend())
|
||||
else:
|
||||
private_key = rsa.generate_private_key(
|
||||
public_exponent=65537, key_size=4096, backend=default_backend()
|
||||
)
|
||||
|
||||
subject_attrs = [
|
||||
x509.NameAttribute(NameOID.COMMON_NAME, domain),
|
||||
]
|
||||
if organization:
|
||||
subject_attrs.insert(0, x509.NameAttribute(NameOID.ORGANIZATION_NAME, organization))
|
||||
|
||||
subject = x509.Name(subject_attrs)
|
||||
|
||||
san_list = [x509.DNSName(domain)]
|
||||
if san_domains:
|
||||
for d in san_domains:
|
||||
san_list.append(x509.DNSName(d))
|
||||
|
||||
csr = (
|
||||
x509.CertificateSigningRequestBuilder()
|
||||
.subject_name(subject)
|
||||
.add_extension(x509.SubjectAlternativeName(san_list), critical=False)
|
||||
.sign(private_key, hashes.SHA256(), default_backend())
|
||||
)
|
||||
|
||||
key_path = output_path / f"{domain}.key"
|
||||
csr_path = output_path / f"{domain}.csr"
|
||||
|
||||
key_path.write_bytes(
|
||||
private_key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.PKCS8,
|
||||
encryption_algorithm=serialization.NoEncryption(),
|
||||
)
|
||||
)
|
||||
|
||||
csr_path.write_bytes(csr.public_bytes(serialization.Encoding.PEM))
|
||||
|
||||
logger.info(f"Generated CSR for {domain}")
|
||||
|
||||
return {
|
||||
"domain": domain,
|
||||
"key_type": key_type,
|
||||
"key_path": str(key_path),
|
||||
"csr_path": str(csr_path),
|
||||
"san_domains": [d.value for d in san_list],
|
||||
}
|
||||
|
||||
|
||||
def parse_certificate(cert_path: str) -> Dict:
|
||||
"""Parse an X.509 certificate and extract key information."""
|
||||
cert_data = Path(cert_path).read_bytes()
|
||||
|
||||
if b"-----BEGIN CERTIFICATE-----" in cert_data:
|
||||
cert = x509.load_pem_x509_certificate(cert_data, default_backend())
|
||||
else:
|
||||
cert = x509.load_der_x509_certificate(cert_data, default_backend())
|
||||
|
||||
subject_attrs = {}
|
||||
for attr in cert.subject:
|
||||
subject_attrs[attr.oid._name] = attr.value
|
||||
|
||||
issuer_attrs = {}
|
||||
for attr in cert.issuer:
|
||||
issuer_attrs[attr.oid._name] = attr.value
|
||||
|
||||
san_names = []
|
||||
try:
|
||||
san_ext = cert.extensions.get_extension_for_oid(ExtensionOID.SUBJECT_ALTERNATIVE_NAME)
|
||||
san_names = [name.value for name in san_ext.value.get_values_for_type(x509.DNSName)]
|
||||
except x509.ExtensionNotFound:
|
||||
pass
|
||||
|
||||
now = datetime.datetime.utcnow()
|
||||
not_after = cert.not_valid_after_utc.replace(tzinfo=None)
|
||||
days_remaining = (not_after - now).days
|
||||
|
||||
pub_key = cert.public_key()
|
||||
if isinstance(pub_key, rsa.RSAPublicKey):
|
||||
key_info = {"type": "RSA", "size": pub_key.key_size}
|
||||
elif isinstance(pub_key, ec.EllipticCurvePublicKey):
|
||||
key_info = {"type": "ECDSA", "curve": pub_key.curve.name, "size": pub_key.key_size}
|
||||
else:
|
||||
key_info = {"type": "Unknown"}
|
||||
|
||||
return {
|
||||
"subject": subject_attrs,
|
||||
"issuer": issuer_attrs,
|
||||
"serial_number": hex(cert.serial_number),
|
||||
"not_valid_before": cert.not_valid_before_utc.isoformat(),
|
||||
"not_valid_after": cert.not_valid_after_utc.isoformat(),
|
||||
"days_remaining": days_remaining,
|
||||
"san_domains": san_names,
|
||||
"signature_algorithm": cert.signature_algorithm_oid._name,
|
||||
"public_key": key_info,
|
||||
"version": cert.version.value,
|
||||
"is_expired": days_remaining < 0,
|
||||
"fingerprint_sha256": cert.fingerprint(hashes.SHA256()).hex(),
|
||||
}
|
||||
|
||||
|
||||
def check_remote_certificate(host: str, port: int = 443, timeout: int = 10) -> Dict:
|
||||
"""Check the TLS certificate of a remote host."""
|
||||
result = {
|
||||
"host": host,
|
||||
"port": port,
|
||||
"status": "unknown",
|
||||
"days_remaining": None,
|
||||
"certificate": None,
|
||||
"errors": [],
|
||||
}
|
||||
|
||||
try:
|
||||
ctx = ssl.create_default_context()
|
||||
with socket.create_connection((host, port), timeout=timeout) as sock:
|
||||
with ctx.wrap_socket(sock, server_hostname=host) as ssock:
|
||||
cert_der = ssock.getpeercert(binary_form=True)
|
||||
cert = x509.load_der_x509_certificate(cert_der, default_backend())
|
||||
|
||||
now = datetime.datetime.utcnow()
|
||||
not_after = cert.not_valid_after_utc.replace(tzinfo=None)
|
||||
days_remaining = (not_after - now).days
|
||||
|
||||
result["days_remaining"] = days_remaining
|
||||
result["not_after"] = not_after.isoformat()
|
||||
result["protocol"] = ssock.version()
|
||||
result["cipher"] = ssock.cipher()[0]
|
||||
|
||||
subject_cn = None
|
||||
for attr in cert.subject:
|
||||
if attr.oid == NameOID.COMMON_NAME:
|
||||
subject_cn = attr.value
|
||||
break
|
||||
|
||||
result["common_name"] = subject_cn
|
||||
result["fingerprint_sha256"] = cert.fingerprint(hashes.SHA256()).hex()
|
||||
|
||||
if days_remaining < 0:
|
||||
result["status"] = "EXPIRED"
|
||||
elif days_remaining < EXPIRY_CRITICAL_DAYS:
|
||||
result["status"] = "CRITICAL"
|
||||
elif days_remaining < EXPIRY_WARNING_DAYS:
|
||||
result["status"] = "WARNING"
|
||||
else:
|
||||
result["status"] = "OK"
|
||||
|
||||
except ssl.SSLCertVerificationError as e:
|
||||
result["status"] = "INVALID"
|
||||
result["errors"].append(f"Certificate verification failed: {e}")
|
||||
except socket.timeout:
|
||||
result["status"] = "TIMEOUT"
|
||||
result["errors"].append("Connection timed out")
|
||||
except Exception as e:
|
||||
result["status"] = "ERROR"
|
||||
result["errors"].append(str(e))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def monitor_domains(domains: List[str], threshold_days: int = 30) -> Dict:
|
||||
"""Monitor certificate expiration for multiple domains."""
|
||||
results = {
|
||||
"scan_time": datetime.datetime.utcnow().isoformat() + "Z",
|
||||
"threshold_days": threshold_days,
|
||||
"total_domains": len(domains),
|
||||
"ok": 0,
|
||||
"warning": 0,
|
||||
"critical": 0,
|
||||
"expired": 0,
|
||||
"errors": 0,
|
||||
"domains": [],
|
||||
}
|
||||
|
||||
for domain in domains:
|
||||
domain = domain.strip()
|
||||
if not domain or domain.startswith("#"):
|
||||
continue
|
||||
|
||||
host = domain.split(":")[0]
|
||||
port = int(domain.split(":")[1]) if ":" in domain else 443
|
||||
|
||||
logger.info(f"Checking {host}:{port}...")
|
||||
check = check_remote_certificate(host, port)
|
||||
results["domains"].append(check)
|
||||
|
||||
status = check["status"]
|
||||
if status == "OK":
|
||||
results["ok"] += 1
|
||||
elif status == "WARNING":
|
||||
results["warning"] += 1
|
||||
elif status == "CRITICAL":
|
||||
results["critical"] += 1
|
||||
elif status == "EXPIRED":
|
||||
results["expired"] += 1
|
||||
else:
|
||||
results["errors"] += 1
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def verify_certificate_chain(cert_path: str, ca_bundle_path: str) -> Dict:
|
||||
"""Verify a certificate chain against a CA bundle."""
|
||||
cert_data = Path(cert_path).read_bytes()
|
||||
ca_data = Path(ca_bundle_path).read_bytes()
|
||||
|
||||
cert = x509.load_pem_x509_certificate(cert_data, default_backend())
|
||||
|
||||
ca_certs = []
|
||||
pem_blocks = ca_data.split(b"-----END CERTIFICATE-----")
|
||||
for block in pem_blocks:
|
||||
block = block.strip()
|
||||
if block and b"-----BEGIN CERTIFICATE-----" in block:
|
||||
pem = block + b"\n-----END CERTIFICATE-----\n"
|
||||
ca_certs.append(x509.load_pem_x509_certificate(pem, default_backend()))
|
||||
|
||||
chain = []
|
||||
current = cert
|
||||
chain.append({
|
||||
"subject": current.subject.rfc4514_string(),
|
||||
"issuer": current.issuer.rfc4514_string(),
|
||||
})
|
||||
|
||||
for ca in ca_certs:
|
||||
if current.issuer == ca.subject:
|
||||
chain.append({
|
||||
"subject": ca.subject.rfc4514_string(),
|
||||
"issuer": ca.issuer.rfc4514_string(),
|
||||
})
|
||||
current = ca
|
||||
if ca.issuer == ca.subject:
|
||||
break
|
||||
|
||||
is_self_signed = cert.issuer == cert.subject
|
||||
chain_complete = len(chain) > 1 or is_self_signed
|
||||
|
||||
return {
|
||||
"certificate": cert.subject.rfc4514_string(),
|
||||
"chain_length": len(chain),
|
||||
"chain": chain,
|
||||
"chain_complete": chain_complete,
|
||||
"is_self_signed": is_self_signed,
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="SSL Certificate Lifecycle Tool")
|
||||
subparsers = parser.add_subparsers(dest="command")
|
||||
|
||||
csr = subparsers.add_parser("generate-csr", help="Generate CSR")
|
||||
csr.add_argument("--domain", required=True, help="Primary domain")
|
||||
csr.add_argument("--output", default="./certs", help="Output directory")
|
||||
csr.add_argument("--key-type", choices=["ecdsa", "rsa"], default="ecdsa")
|
||||
csr.add_argument("--san", nargs="*", help="Additional SAN domains")
|
||||
csr.add_argument("--org", help="Organization name")
|
||||
|
||||
parse = subparsers.add_parser("parse-cert", help="Parse certificate")
|
||||
parse.add_argument("--cert", required=True, help="Certificate file path")
|
||||
|
||||
check = subparsers.add_parser("check-expiry", help="Check remote cert expiry")
|
||||
check.add_argument("--host", required=True, help="Hostname")
|
||||
check.add_argument("--port", type=int, default=443, help="Port")
|
||||
|
||||
mon = subparsers.add_parser("monitor", help="Monitor multiple domains")
|
||||
mon.add_argument("--domains", required=True, help="File with domain list")
|
||||
mon.add_argument("--threshold", type=int, default=30, help="Warning threshold (days)")
|
||||
|
||||
chain = subparsers.add_parser("verify-chain", help="Verify certificate chain")
|
||||
chain.add_argument("--cert", required=True, help="Certificate file")
|
||||
chain.add_argument("--ca-bundle", required=True, help="CA bundle file")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.command == "generate-csr":
|
||||
result = generate_csr(args.domain, args.output, args.key_type, args.san, args.org)
|
||||
print(json.dumps(result, indent=2))
|
||||
elif args.command == "parse-cert":
|
||||
result = parse_certificate(args.cert)
|
||||
print(json.dumps(result, indent=2, default=str))
|
||||
elif args.command == "check-expiry":
|
||||
result = check_remote_certificate(args.host, args.port)
|
||||
print(json.dumps(result, indent=2, default=str))
|
||||
elif args.command == "monitor":
|
||||
domains = Path(args.domains).read_text().strip().split("\n")
|
||||
result = monitor_domains(domains, args.threshold)
|
||||
print(json.dumps(result, indent=2, default=str))
|
||||
elif args.command == "verify-chain":
|
||||
result = verify_certificate_chain(args.cert, args.ca_bundle)
|
||||
print(json.dumps(result, indent=2))
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user