mcp-security-framework 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. mcp_security_framework/__init__.py +96 -0
  2. mcp_security_framework/cli/__init__.py +18 -0
  3. mcp_security_framework/cli/cert_cli.py +511 -0
  4. mcp_security_framework/cli/security_cli.py +791 -0
  5. mcp_security_framework/constants.py +209 -0
  6. mcp_security_framework/core/__init__.py +61 -0
  7. mcp_security_framework/core/auth_manager.py +1011 -0
  8. mcp_security_framework/core/cert_manager.py +1663 -0
  9. mcp_security_framework/core/permission_manager.py +735 -0
  10. mcp_security_framework/core/rate_limiter.py +602 -0
  11. mcp_security_framework/core/security_manager.py +943 -0
  12. mcp_security_framework/core/ssl_manager.py +735 -0
  13. mcp_security_framework/examples/__init__.py +75 -0
  14. mcp_security_framework/examples/django_example.py +615 -0
  15. mcp_security_framework/examples/fastapi_example.py +472 -0
  16. mcp_security_framework/examples/flask_example.py +506 -0
  17. mcp_security_framework/examples/gateway_example.py +803 -0
  18. mcp_security_framework/examples/microservice_example.py +690 -0
  19. mcp_security_framework/examples/standalone_example.py +576 -0
  20. mcp_security_framework/middleware/__init__.py +250 -0
  21. mcp_security_framework/middleware/auth_middleware.py +292 -0
  22. mcp_security_framework/middleware/fastapi_auth_middleware.py +447 -0
  23. mcp_security_framework/middleware/fastapi_middleware.py +757 -0
  24. mcp_security_framework/middleware/flask_auth_middleware.py +465 -0
  25. mcp_security_framework/middleware/flask_middleware.py +591 -0
  26. mcp_security_framework/middleware/mtls_middleware.py +439 -0
  27. mcp_security_framework/middleware/rate_limit_middleware.py +403 -0
  28. mcp_security_framework/middleware/security_middleware.py +507 -0
  29. mcp_security_framework/schemas/__init__.py +109 -0
  30. mcp_security_framework/schemas/config.py +694 -0
  31. mcp_security_framework/schemas/models.py +709 -0
  32. mcp_security_framework/schemas/responses.py +686 -0
  33. mcp_security_framework/tests/__init__.py +0 -0
  34. mcp_security_framework/utils/__init__.py +121 -0
  35. mcp_security_framework/utils/cert_utils.py +525 -0
  36. mcp_security_framework/utils/crypto_utils.py +475 -0
  37. mcp_security_framework/utils/validation_utils.py +571 -0
  38. mcp_security_framework-0.1.0.dist-info/METADATA +411 -0
  39. mcp_security_framework-0.1.0.dist-info/RECORD +76 -0
  40. mcp_security_framework-0.1.0.dist-info/WHEEL +5 -0
  41. mcp_security_framework-0.1.0.dist-info/entry_points.txt +3 -0
  42. mcp_security_framework-0.1.0.dist-info/top_level.txt +2 -0
  43. tests/__init__.py +0 -0
  44. tests/test_cli/__init__.py +0 -0
  45. tests/test_cli/test_cert_cli.py +379 -0
  46. tests/test_cli/test_security_cli.py +657 -0
  47. tests/test_core/__init__.py +0 -0
  48. tests/test_core/test_auth_manager.py +582 -0
  49. tests/test_core/test_cert_manager.py +795 -0
  50. tests/test_core/test_permission_manager.py +395 -0
  51. tests/test_core/test_rate_limiter.py +626 -0
  52. tests/test_core/test_security_manager.py +841 -0
  53. tests/test_core/test_ssl_manager.py +532 -0
  54. tests/test_examples/__init__.py +8 -0
  55. tests/test_examples/test_fastapi_example.py +264 -0
  56. tests/test_examples/test_flask_example.py +238 -0
  57. tests/test_examples/test_standalone_example.py +292 -0
  58. tests/test_integration/__init__.py +0 -0
  59. tests/test_integration/test_auth_flow.py +502 -0
  60. tests/test_integration/test_certificate_flow.py +527 -0
  61. tests/test_integration/test_fastapi_integration.py +341 -0
  62. tests/test_integration/test_flask_integration.py +398 -0
  63. tests/test_integration/test_standalone_integration.py +493 -0
  64. tests/test_middleware/__init__.py +0 -0
  65. tests/test_middleware/test_fastapi_middleware.py +523 -0
  66. tests/test_middleware/test_flask_middleware.py +582 -0
  67. tests/test_middleware/test_security_middleware.py +493 -0
  68. tests/test_schemas/__init__.py +0 -0
  69. tests/test_schemas/test_config.py +811 -0
  70. tests/test_schemas/test_models.py +879 -0
  71. tests/test_schemas/test_responses.py +1054 -0
  72. tests/test_schemas/test_serialization.py +493 -0
  73. tests/test_utils/__init__.py +0 -0
  74. tests/test_utils/test_cert_utils.py +510 -0
  75. tests/test_utils/test_crypto_utils.py +603 -0
  76. tests/test_utils/test_validation_utils.py +477 -0
@@ -0,0 +1,96 @@
1
+ """
2
+ MCP Security Framework
3
+
4
+ Universal security framework for microservices with SSL/TLS, authentication,
5
+ authorization, and rate limiting.
6
+
7
+ This framework provides comprehensive security capabilities for Python applications,
8
+ including multi-method authentication, SSL/TLS management, role-based authorization,
9
+ and rate limiting. It supports multiple web frameworks including FastAPI, Flask,
10
+ and Django, as well as standalone usage.
11
+
12
+ Key Features:
13
+ - Multi-method Authentication (API keys, JWT tokens, X.509 certificates)
14
+ - SSL/TLS Management (Server and client certificate handling)
15
+ - Role-based Authorization (Flexible permission system with role hierarchy)
16
+ - Rate Limiting (Configurable request rate limiting)
17
+ - Framework Agnostic (Works with FastAPI, Flask, Django, and standalone)
18
+ - CLI Tools (Certificate management and security testing)
19
+ - Comprehensive Logging (Security event logging and monitoring)
20
+
21
+ Example Usage:
22
+ >>> from mcp_security_framework import SecurityManager, SecurityConfig
23
+ >>> from mcp_security_framework.schemas.config import AuthConfig
24
+ >>>
25
+ >>> config = SecurityConfig(
26
+ ... auth=AuthConfig(
27
+ ... enabled=True,
28
+ ... methods=["api_key"],
29
+ ... api_keys={"admin": "admin_key_123"}
30
+ ... )
31
+ ... )
32
+ >>>
33
+ >>> security_manager = SecurityManager(config)
34
+ >>> result = security_manager.validate_request({
35
+ ... "api_key": "admin_key_123",
36
+ ... "required_permissions": ["read", "write"]
37
+ ... })
38
+ >>>
39
+ >>> if result.is_valid:
40
+ ... print("Access granted!")
41
+ >>> else:
42
+ ... print(f"Access denied: {result.error_message}")
43
+
44
+ Author: Vasiliy Zdanovskiy
45
+ Version: 0.1.0
46
+ License: MIT
47
+ """
48
+
49
+ from mcp_security_framework.core.security_manager import SecurityManager
50
+ from mcp_security_framework.core.auth_manager import AuthManager
51
+ from mcp_security_framework.core.cert_manager import CertificateManager
52
+ from mcp_security_framework.core.permission_manager import PermissionManager
53
+ from mcp_security_framework.core.rate_limiter import RateLimiter
54
+ from mcp_security_framework.schemas.config import SecurityConfig, AuthConfig, SSLConfig, PermissionConfig, RateLimitConfig
55
+ from mcp_security_framework.schemas.models import AuthResult, ValidationResult, CertificateInfo, CertificatePair
56
+ from mcp_security_framework.schemas.responses import SecurityResponse, ErrorResponse, SuccessResponse
57
+
58
+ # Version information
59
+ __version__ = "0.1.0"
60
+ __author__ = "Vasiliy Zdanovskiy"
61
+ __email__ = "vasilyvz@gmail.com"
62
+ __license__ = "MIT"
63
+
64
+ # Main exports
65
+ __all__ = [
66
+ # Core managers
67
+ "SecurityManager",
68
+ "AuthManager",
69
+ "CertificateManager",
70
+ "PermissionManager",
71
+ "RateLimiter",
72
+
73
+ # Configuration schemas
74
+ "SecurityConfig",
75
+ "AuthConfig",
76
+ "SSLConfig",
77
+ "PermissionConfig",
78
+ "RateLimitConfig",
79
+
80
+ # Data models
81
+ "AuthResult",
82
+ "ValidationResult",
83
+ "CertificateInfo",
84
+ "CertificatePair",
85
+
86
+ # Response schemas
87
+ "SecurityResponse",
88
+ "ErrorResponse",
89
+ "SuccessResponse",
90
+
91
+ # Version info
92
+ "__version__",
93
+ "__author__",
94
+ "__email__",
95
+ "__license__",
96
+ ]
@@ -0,0 +1,18 @@
1
+ """
2
+ CLI Module
3
+
4
+ This module provides command-line interface tools for the MCP Security Framework.
5
+
6
+ Available CLI tools:
7
+ - cert_cli: Certificate management CLI
8
+ - security_cli: Security management CLI
9
+
10
+ Author: MCP Security Team
11
+ Version: 1.0.0
12
+ License: MIT
13
+ """
14
+
15
+ from .cert_cli import cert_cli
16
+ from .security_cli import security_cli
17
+
18
+ __all__ = ['cert_cli', 'security_cli']
@@ -0,0 +1,511 @@
1
+ """
2
+ Certificate Management CLI Module
3
+
4
+ This module provides command-line interface for certificate management
5
+ operations including creation, validation, and management of certificates.
6
+
7
+ Key Features:
8
+ - Root CA certificate creation
9
+ - Server certificate generation
10
+ - Client certificate generation
11
+ - Certificate validation and verification
12
+ - Certificate information display
13
+ - Certificate revocation
14
+
15
+ Commands:
16
+ create-ca: Create root CA certificate
17
+ create-server: Create server certificate
18
+ create-client: Create client certificate
19
+ validate: Validate certificate
20
+ info: Display certificate information
21
+ revoke: Revoke certificate
22
+
23
+ Author: MCP Security Team
24
+ Version: 1.0.0
25
+ License: MIT
26
+ """
27
+
28
+ import click
29
+ import json
30
+ import os
31
+ from pathlib import Path
32
+ from typing import Optional
33
+
34
+ from ..core.cert_manager import CertificateManager
35
+ from ..schemas.config import CertificateConfig, CAConfig, ServerCertConfig, ClientCertConfig
36
+ from ..schemas.models import CertificateType
37
+ from ..schemas.config import IntermediateCAConfig
38
+
39
+
40
+ @click.group()
41
+ @click.option('--config', '-c', 'config_path',
42
+ help='Path to certificate configuration file')
43
+ @click.option('--verbose', '-v', is_flag=True,
44
+ help='Enable verbose output')
45
+ @click.pass_context
46
+ def cert_cli(ctx, config_path: Optional[str], verbose: bool):
47
+ """
48
+ Certificate Management CLI
49
+
50
+ Manage certificates including creation, validation, and management.
51
+ """
52
+ ctx.ensure_object(dict)
53
+ ctx.obj['verbose'] = verbose
54
+
55
+ # Load configuration
56
+ if config_path and os.path.exists(config_path):
57
+ with open(config_path, 'r') as f:
58
+ config_data = json.load(f)
59
+ ctx.obj['config'] = CertificateConfig(**config_data)
60
+ else:
61
+ # Use default configuration
62
+ ctx.obj['config'] = CertificateConfig(
63
+ cert_storage_path="./certs",
64
+ key_storage_path="./keys",
65
+ ca_cert_path="./certs/ca_cert.pem",
66
+ ca_key_path="./keys/ca_key.pem"
67
+ )
68
+
69
+ # Create certificate manager
70
+ ctx.obj['cert_manager'] = CertificateManager(ctx.obj['config'])
71
+
72
+
73
+ @cert_cli.command()
74
+ @click.option('--common-name', '-cn', required=True,
75
+ help='Common name for the CA certificate')
76
+ @click.option('--organization', '-o', required=True,
77
+ help='Organization name')
78
+ @click.option('--country', '-c', required=True,
79
+ help='Country code (e.g., US, GB)')
80
+ @click.option('--state', '-s',
81
+ help='State or province')
82
+ @click.option('--locality', '-l',
83
+ help='Locality or city')
84
+ @click.option('--email', '-e',
85
+ help='Email address')
86
+ @click.option('--validity-years', '-y', default=10,
87
+ help='Certificate validity in years')
88
+ @click.option('--key-size', '-k', default=2048,
89
+ help='RSA key size')
90
+ @click.pass_context
91
+ def create_ca(ctx, common_name: str, organization: str, country: str,
92
+ state: Optional[str], locality: Optional[str],
93
+ email: Optional[str], validity_years: int, key_size: int):
94
+ """
95
+ Create a root CA certificate.
96
+
97
+ This command creates a new root Certificate Authority (CA) certificate
98
+ that can be used to sign other certificates.
99
+ """
100
+ try:
101
+ config = ctx.obj['config']
102
+ cert_manager = ctx.obj['cert_manager']
103
+ verbose = ctx.obj['verbose']
104
+
105
+ # Create CA configuration
106
+ ca_config = CAConfig(
107
+ common_name=common_name,
108
+ organization=organization,
109
+ country=country,
110
+ state=state,
111
+ locality=locality,
112
+ email=email,
113
+ validity_years=validity_years,
114
+ key_size=key_size
115
+ )
116
+
117
+ if verbose:
118
+ click.echo(f"Creating CA certificate with configuration:")
119
+ click.echo(f" Common Name: {common_name}")
120
+ click.echo(f" Organization: {organization}")
121
+ click.echo(f" Country: {country}")
122
+ click.echo(f" Validity: {validity_years} years")
123
+ click.echo(f" Key Size: {key_size} bits")
124
+
125
+ # Create CA certificate
126
+ cert_pair = cert_manager.create_root_ca(ca_config)
127
+
128
+ click.echo(f"✅ CA certificate created successfully!")
129
+ click.echo(f" Certificate: {cert_pair.certificate_path}")
130
+ click.echo(f" Private Key: {cert_pair.private_key_path}")
131
+ click.echo(f" Serial Number: {cert_pair.serial_number}")
132
+ click.echo(f" Valid Until: {cert_pair.not_after}")
133
+
134
+ except Exception as e:
135
+ click.echo(f"❌ Failed to create CA certificate: {str(e)}", err=True)
136
+ raise click.Abort()
137
+
138
+
139
+ @cert_cli.command()
140
+ @click.option('--common-name', '-cn', required=True,
141
+ help='Common name for the server certificate')
142
+ @click.option('--organization', '-o', required=True,
143
+ help='Organization name')
144
+ @click.option('--country', '-c', required=True,
145
+ help='Country code (e.g., US, GB)')
146
+ @click.option('--state', '-s',
147
+ help='State or province')
148
+ @click.option('--locality', '-l',
149
+ help='Locality or city')
150
+ @click.option('--email', '-e',
151
+ help='Email address')
152
+ @click.option('--validity-days', '-d', default=365,
153
+ help='Certificate validity in days')
154
+ @click.option('--key-size', '-k', default=2048,
155
+ help='RSA key size')
156
+ @click.option('--san', multiple=True,
157
+ help='Subject Alternative Names (can be specified multiple times)')
158
+ @click.pass_context
159
+ def create_server(ctx, common_name: str, organization: str, country: str,
160
+ state: Optional[str], locality: Optional[str],
161
+ email: Optional[str], validity_days: int, key_size: int,
162
+ san: tuple):
163
+ """
164
+ Create a server certificate.
165
+
166
+ This command creates a new server certificate signed by the configured CA.
167
+ """
168
+ try:
169
+ config = ctx.obj['config']
170
+ cert_manager = ctx.obj['cert_manager']
171
+ verbose = ctx.obj['verbose']
172
+
173
+ # Create server configuration
174
+ server_config = ServerCertConfig(
175
+ common_name=common_name,
176
+ organization=organization,
177
+ country=country,
178
+ state=state,
179
+ locality=locality,
180
+ email=email,
181
+ validity_days=validity_days,
182
+ key_size=key_size,
183
+ subject_alt_names=list(san) if san else None
184
+ )
185
+
186
+ if verbose:
187
+ click.echo(f"Creating server certificate with configuration:")
188
+ click.echo(f" Common Name: {common_name}")
189
+ click.echo(f" Organization: {organization}")
190
+ click.echo(f" Country: {country}")
191
+ click.echo(f" Validity: {validity_days} days")
192
+ click.echo(f" Key Size: {key_size} bits")
193
+ if san:
194
+ click.echo(f" SAN: {', '.join(san)}")
195
+
196
+ # Create server certificate
197
+ cert_pair = cert_manager.create_server_certificate(server_config)
198
+
199
+ click.echo(f"✅ Server certificate created successfully!")
200
+ click.echo(f" Certificate: {cert_pair.certificate_path}")
201
+ click.echo(f" Private Key: {cert_pair.private_key_path}")
202
+ click.echo(f" Serial Number: {cert_pair.serial_number}")
203
+ click.echo(f" Valid Until: {cert_pair.not_after}")
204
+
205
+ except Exception as e:
206
+ click.echo(f"❌ Failed to create server certificate: {str(e)}", err=True)
207
+ raise click.Abort()
208
+
209
+
210
+ @cert_cli.command()
211
+ @click.option('--common-name', '-cn', required=True,
212
+ help='Common name for the client certificate')
213
+ @click.option('--organization', '-o', required=True,
214
+ help='Organization name')
215
+ @click.option('--country', '-c', required=True,
216
+ help='Country code (e.g., US, GB)')
217
+ @click.option('--state', '-s',
218
+ help='State or province')
219
+ @click.option('--locality', '-l',
220
+ help='Locality or city')
221
+ @click.option('--email', '-e',
222
+ help='Email address')
223
+ @click.option('--validity-days', '-d', default=365,
224
+ help='Certificate validity in days')
225
+ @click.option('--key-size', '-k', default=2048,
226
+ help='RSA key size')
227
+ @click.option('--roles', multiple=True,
228
+ help='Roles to assign to the client (can be specified multiple times)')
229
+ @click.option('--permissions', multiple=True,
230
+ help='Permissions to assign to the client (can be specified multiple times)')
231
+ @click.pass_context
232
+ def create_client(ctx, common_name: str, organization: str, country: str,
233
+ state: Optional[str], locality: Optional[str],
234
+ email: Optional[str], validity_days: int, key_size: int,
235
+ roles: tuple, permissions: tuple):
236
+ """
237
+ Create a client certificate.
238
+
239
+ This command creates a new client certificate signed by the configured CA.
240
+ """
241
+ try:
242
+ config = ctx.obj['config']
243
+ cert_manager = ctx.obj['cert_manager']
244
+ verbose = ctx.obj['verbose']
245
+
246
+ # Create client configuration
247
+ client_config = ClientCertConfig(
248
+ common_name=common_name,
249
+ organization=organization,
250
+ country=country,
251
+ state=state,
252
+ locality=locality,
253
+ email=email,
254
+ validity_days=validity_days,
255
+ key_size=key_size,
256
+ roles=list(roles) if roles else None,
257
+ permissions=list(permissions) if permissions else None
258
+ )
259
+
260
+ if verbose:
261
+ click.echo(f"Creating client certificate with configuration:")
262
+ click.echo(f" Common Name: {common_name}")
263
+ click.echo(f" Organization: {organization}")
264
+ click.echo(f" Country: {country}")
265
+ click.echo(f" Validity: {validity_days} days")
266
+ click.echo(f" Key Size: {key_size} bits")
267
+ if roles:
268
+ click.echo(f" Roles: {', '.join(roles)}")
269
+ if permissions:
270
+ click.echo(f" Permissions: {', '.join(permissions)}")
271
+
272
+ # Create client certificate
273
+ cert_pair = cert_manager.create_client_certificate(client_config)
274
+
275
+ click.echo(f"✅ Client certificate created successfully!")
276
+ click.echo(f" Certificate: {cert_pair.certificate_path}")
277
+ click.echo(f" Private Key: {cert_pair.private_key_path}")
278
+ click.echo(f" Serial Number: {cert_pair.serial_number}")
279
+ click.echo(f" Valid Until: {cert_pair.not_after}")
280
+
281
+ except Exception as e:
282
+ click.echo(f"❌ Failed to create client certificate: {str(e)}", err=True)
283
+ raise click.Abort()
284
+
285
+
286
+ @cert_cli.command()
287
+ @click.argument('cert_path', type=click.Path(exists=True))
288
+ @click.option('--ca-cert', type=click.Path(exists=True),
289
+ help='Path to CA certificate for validation')
290
+ @click.pass_context
291
+ def validate(ctx, cert_path: str, ca_cert: Optional[str]):
292
+ """
293
+ Validate a certificate.
294
+
295
+ This command validates a certificate and optionally checks it against a CA.
296
+ """
297
+ try:
298
+ config = ctx.obj['config']
299
+ cert_manager = ctx.obj['cert_manager']
300
+ verbose = ctx.obj['verbose']
301
+
302
+ if verbose:
303
+ click.echo(f"Validating certificate: {cert_path}")
304
+ if ca_cert:
305
+ click.echo(f"Using CA certificate: {ca_cert}")
306
+
307
+ # Validate certificate
308
+ is_valid = cert_manager.validate_certificate_chain(cert_path, ca_cert)
309
+
310
+ if is_valid:
311
+ click.echo(f"✅ Certificate is valid!")
312
+ else:
313
+ click.echo(f"❌ Certificate validation failed!", err=True)
314
+ raise click.Abort()
315
+
316
+ except Exception as e:
317
+ click.echo(f"❌ Certificate validation failed: {str(e)}", err=True)
318
+ raise click.Abort()
319
+
320
+
321
+ @cert_cli.command()
322
+ @click.argument('cert_path', type=click.Path(exists=True))
323
+ @click.pass_context
324
+ def info(ctx, cert_path: str):
325
+ """
326
+ Display certificate information.
327
+
328
+ This command displays detailed information about a certificate.
329
+ """
330
+ try:
331
+ config = ctx.obj['config']
332
+ cert_manager = ctx.obj['cert_manager']
333
+ verbose = ctx.obj['verbose']
334
+
335
+ if verbose:
336
+ click.echo(f"Getting certificate information: {cert_path}")
337
+
338
+ # Get certificate info
339
+ cert_info = cert_manager.get_certificate_info(cert_path)
340
+
341
+ click.echo(f"Certificate Information:")
342
+ click.echo(f" Subject: {cert_info.subject}")
343
+ click.echo(f" Issuer: {cert_info.issuer}")
344
+ click.echo(f" Serial Number: {cert_info.serial_number}")
345
+ click.echo(f" Valid From: {cert_info.not_before}")
346
+ click.echo(f" Valid Until: {cert_info.not_after}")
347
+ click.echo(f" Key Size: {cert_info.key_size} bits")
348
+ click.echo(f" Certificate Type: {cert_info.certificate_type}")
349
+
350
+ if cert_info.subject_alt_names:
351
+ click.echo(f" Subject Alternative Names: {', '.join(cert_info.subject_alt_names)}")
352
+
353
+ except Exception as e:
354
+ click.echo(f"❌ Failed to get certificate information: {str(e)}", err=True)
355
+ raise click.Abort()
356
+
357
+
358
+ @cert_cli.command()
359
+ @click.option('--common-name', '-cn', required=True,
360
+ help='Common name for the intermediate CA certificate')
361
+ @click.option('--organization', '-o', required=True,
362
+ help='Organization name')
363
+ @click.option('--country', '-c', required=True,
364
+ help='Country code (e.g., US, GB)')
365
+ @click.option('--state', '-s',
366
+ help='State or province')
367
+ @click.option('--locality', '-l',
368
+ help='Locality or city')
369
+ @click.option('--email', '-e',
370
+ help='Email address')
371
+ @click.option('--validity-years', '-y', default=5,
372
+ help='Certificate validity in years')
373
+ @click.option('--key-size', '-k', default=2048,
374
+ help='RSA key size')
375
+ @click.option('--parent-ca-cert', '-p', required=True,
376
+ help='Path to parent CA certificate')
377
+ @click.option('--parent-ca-key', '-pk', required=True,
378
+ help='Path to parent CA private key')
379
+ @click.pass_context
380
+ def create_intermediate_ca(ctx, common_name: str, organization: str, country: str,
381
+ state: Optional[str], locality: Optional[str],
382
+ email: Optional[str], validity_years: int, key_size: int,
383
+ parent_ca_cert: str, parent_ca_key: str):
384
+ """
385
+ Create an intermediate CA certificate.
386
+
387
+ This command creates a new intermediate Certificate Authority (CA) certificate
388
+ signed by a parent CA certificate.
389
+ """
390
+ try:
391
+ config = ctx.obj['config']
392
+ cert_manager = ctx.obj['cert_manager']
393
+ verbose = ctx.obj['verbose']
394
+
395
+ # Create intermediate CA configuration
396
+ intermediate_config = IntermediateCAConfig(
397
+ common_name=common_name,
398
+ organization=organization,
399
+ country=country,
400
+ state=state,
401
+ locality=locality,
402
+ email=email,
403
+ validity_years=validity_years,
404
+ key_size=key_size,
405
+ parent_ca_cert=parent_ca_cert,
406
+ parent_ca_key=parent_ca_key
407
+ )
408
+
409
+ if verbose:
410
+ click.echo(f"Creating intermediate CA certificate with configuration:")
411
+ click.echo(f" Common Name: {common_name}")
412
+ click.echo(f" Organization: {organization}")
413
+ click.echo(f" Country: {country}")
414
+ click.echo(f" Validity: {validity_years} years")
415
+ click.echo(f" Key Size: {key_size} bits")
416
+ click.echo(f" Parent CA Cert: {parent_ca_cert}")
417
+ click.echo(f" Parent CA Key: {parent_ca_key}")
418
+
419
+ # Create intermediate CA certificate
420
+ cert_pair = cert_manager.create_intermediate_ca(intermediate_config)
421
+
422
+ click.echo(f"✅ Intermediate CA certificate created successfully!")
423
+ click.echo(f" Certificate: {cert_pair.certificate_path}")
424
+ click.echo(f" Private Key: {cert_pair.private_key_path}")
425
+ click.echo(f" Serial Number: {cert_pair.serial_number}")
426
+ click.echo(f" Valid Until: {cert_pair.not_after}")
427
+
428
+ except Exception as e:
429
+ click.echo(f"❌ Failed to create intermediate CA certificate: {str(e)}", err=True)
430
+ raise click.Abort()
431
+
432
+
433
+ @cert_cli.command()
434
+ @click.option('--ca-cert', '-c', required=True,
435
+ help='Path to CA certificate')
436
+ @click.option('--ca-key', '-k', required=True,
437
+ help='Path to CA private key')
438
+ @click.option('--output', '-o',
439
+ help='Output path for CRL file')
440
+ @click.option('--validity-days', '-d', default=30,
441
+ help='CRL validity in days')
442
+ @click.pass_context
443
+ def create_crl(ctx, ca_cert: str, ca_key: str, output: Optional[str], validity_days: int):
444
+ """
445
+ Create a Certificate Revocation List (CRL).
446
+
447
+ This command creates a Certificate Revocation List (CRL) from the CA
448
+ certificate and private key.
449
+ """
450
+ try:
451
+ config = ctx.obj['config']
452
+ cert_manager = ctx.obj['cert_manager']
453
+ verbose = ctx.obj['verbose']
454
+
455
+ if verbose:
456
+ click.echo(f"Creating CRL with configuration:")
457
+ click.echo(f" CA Certificate: {ca_cert}")
458
+ click.echo(f" CA Private Key: {ca_key}")
459
+ click.echo(f" Validity: {validity_days} days")
460
+ if output:
461
+ click.echo(f" Output: {output}")
462
+
463
+ # Create CRL
464
+ crl_path = cert_manager.create_crl(ca_cert, ca_key, output, validity_days)
465
+
466
+ click.echo(f"✅ CRL created successfully!")
467
+ click.echo(f" CRL Path: {crl_path}")
468
+ click.echo(f" Validity: {validity_days} days")
469
+
470
+ except Exception as e:
471
+ click.echo(f"❌ Failed to create CRL: {str(e)}", err=True)
472
+ raise click.Abort()
473
+
474
+
475
+ @cert_cli.command()
476
+ @click.argument('serial_number')
477
+ @click.option('--reason', '-r', default='unspecified',
478
+ help='Reason for revocation')
479
+ @click.pass_context
480
+ def revoke(ctx, serial_number: str, reason: str):
481
+ """
482
+ Revoke a certificate.
483
+
484
+ This command revokes a certificate by adding it to the Certificate
485
+ Revocation List (CRL).
486
+ """
487
+ try:
488
+ config = ctx.obj['config']
489
+ cert_manager = ctx.obj['cert_manager']
490
+ verbose = ctx.obj['verbose']
491
+
492
+ if verbose:
493
+ click.echo(f"Revoking certificate with serial number: {serial_number}")
494
+ click.echo(f"Reason: {reason}")
495
+
496
+ # Revoke certificate
497
+ success = cert_manager.revoke_certificate(serial_number, reason)
498
+
499
+ if success:
500
+ click.echo(f"✅ Certificate revoked successfully!")
501
+ else:
502
+ click.echo(f"❌ Failed to revoke certificate!", err=True)
503
+ raise click.Abort()
504
+
505
+ except Exception as e:
506
+ click.echo(f"❌ Failed to revoke certificate: {str(e)}", err=True)
507
+ raise click.Abort()
508
+
509
+
510
+ if __name__ == '__main__':
511
+ cert_cli()