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,735 @@
1
+ """
2
+ SSL/TLS Manager Module
3
+
4
+ This module provides comprehensive SSL/TLS management for the MCP Security
5
+ Framework. It handles SSL context creation, certificate validation, and
6
+ TLS configuration management.
7
+
8
+ Key Features:
9
+ - SSL context creation for servers and clients
10
+ - Certificate validation and verification
11
+ - TLS version and cipher management
12
+ - Certificate information extraction
13
+ - SSL configuration management
14
+ - Certificate chain validation
15
+
16
+ Classes:
17
+ SSLManager: Main SSL/TLS management class
18
+ SSLContextBuilder: Builder for SSL contexts
19
+ CertificateValidator: Certificate validation utilities
20
+
21
+ Author: MCP Security Team
22
+ Version: 1.0.0
23
+ License: MIT
24
+ """
25
+
26
+ import logging
27
+ import ssl
28
+ from pathlib import Path
29
+ from typing import Dict, List, Optional, Union
30
+
31
+ from cryptography import x509
32
+ from cryptography.hazmat.primitives import hashes, serialization
33
+ from cryptography.hazmat.primitives.asymmetric import rsa
34
+
35
+ from ..schemas.config import SSLConfig
36
+ from ..schemas.models import CertificateInfo
37
+ from ..utils.cert_utils import (
38
+ extract_certificate_info,
39
+ get_certificate_expiry,
40
+ is_certificate_self_signed,
41
+ parse_certificate,
42
+ validate_certificate_chain,
43
+ )
44
+
45
+
46
+ class SSLManager:
47
+ """
48
+ SSL/TLS Management Class
49
+
50
+ This class provides comprehensive SSL/TLS management capabilities including
51
+ SSL context creation, certificate validation, and TLS configuration.
52
+
53
+ The SSLManager handles:
54
+ - Server and client SSL context creation
55
+ - Certificate validation and verification
56
+ - TLS version and cipher management
57
+ - Certificate information extraction
58
+ - SSL configuration management
59
+ - Certificate chain building and validation
60
+
61
+ Attributes:
62
+ config (SSLConfig): SSL configuration settings
63
+ logger (Logger): Logger instance for SSL operations
64
+ _contexts (Dict): Cache of created SSL contexts
65
+ _certificate_cache (Dict): Cache of certificate information
66
+
67
+ Example:
68
+ >>> config = SSLConfig(enabled=True, cert_file="server.crt")
69
+ >>> ssl_manager = SSLManager(config)
70
+ >>> context = ssl_manager.create_server_context()
71
+
72
+ Raises:
73
+ SSLConfigurationError: When SSL configuration is invalid
74
+ CertificateValidationError: When certificate validation fails
75
+ """
76
+
77
+ def __init__(self, config: SSLConfig):
78
+ """
79
+ Initialize SSL Manager.
80
+
81
+ Args:
82
+ config (SSLConfig): SSL configuration settings containing
83
+ certificate paths, TLS versions, and verification settings.
84
+ Must be a valid SSLConfig instance with proper certificate
85
+ file paths and TLS configuration.
86
+
87
+ Raises:
88
+ SSLConfigurationError: If configuration is invalid or certificate
89
+ files are not accessible.
90
+
91
+ Example:
92
+ >>> config = SSLConfig(enabled=True, cert_file="server.crt")
93
+ >>> ssl_manager = SSLManager(config)
94
+ """
95
+ self.config = config
96
+ self.logger = logging.getLogger(__name__)
97
+ self._contexts: Dict[str, ssl.SSLContext] = {}
98
+ self._certificate_cache: Dict[str, CertificateInfo] = {}
99
+
100
+ # Validate configuration
101
+ self._validate_configuration()
102
+
103
+ self.logger.info(
104
+ "SSLManager initialized successfully",
105
+ extra={
106
+ "enabled": config.enabled,
107
+ "min_tls_version": config.min_tls_version,
108
+ "verify_mode": config.verify_mode,
109
+ },
110
+ )
111
+
112
+ def create_server_context(
113
+ self,
114
+ cert_file: Optional[str] = None,
115
+ key_file: Optional[str] = None,
116
+ ca_cert_file: Optional[str] = None,
117
+ verify_mode: Optional[str] = None,
118
+ min_version: Optional[str] = None,
119
+ ) -> ssl.SSLContext:
120
+ """
121
+ Create SSL context for server operations.
122
+
123
+ This method creates and configures an SSL context suitable for server
124
+ operations. It handles certificate loading, key management, and TLS
125
+ configuration according to the provided parameters.
126
+
127
+ Args:
128
+ cert_file (Optional[str]): Path to server certificate file.
129
+ If None, uses certificate from config. Must be a valid PEM
130
+ or DER certificate file path.
131
+ key_file (Optional[str]): Path to server private key file.
132
+ If None, uses key from config. Must be a valid PEM or DER
133
+ private key file path.
134
+ ca_cert_file (Optional[str]): Path to CA certificate file for
135
+ client certificate verification. If None, uses CA from config.
136
+ Must be a valid PEM certificate file path.
137
+ verify_mode (Optional[str]): SSL verification mode. Valid values:
138
+ - "CERT_NONE": No certificate verification
139
+ - "CERT_OPTIONAL": Certificate verification optional
140
+ - "CERT_REQUIRED": Certificate verification required
141
+ If None, uses verify_mode from config.
142
+ min_version (Optional[str]): Minimum TLS version. Valid values:
143
+ - "TLSv1.0": TLS 1.0
144
+ - "TLSv1.1": TLS 1.1
145
+ - "TLSv1.2": TLS 1.2
146
+ - "TLSv1.3": TLS 1.3
147
+ If None, uses min_tls_version from config.
148
+
149
+ Returns:
150
+ ssl.SSLContext: Configured SSL context for server operations.
151
+ The context is properly configured with certificates, keys,
152
+ and TLS settings for secure server communication.
153
+
154
+ Raises:
155
+ SSLConfigurationError: If SSL configuration is invalid or
156
+ certificate/key files cannot be loaded.
157
+ CertificateValidationError: If certificate validation fails.
158
+ FileNotFoundError: If certificate or key files are not found.
159
+ PermissionError: If certificate or key files are not readable.
160
+
161
+ Example:
162
+ >>> ssl_manager = SSLManager(config)
163
+ >>> context = ssl_manager.create_server_context(
164
+ ... cert_file="server.crt",
165
+ ... key_file="server.key",
166
+ ... verify_mode="CERT_REQUIRED"
167
+ ... )
168
+ >>> # Use context for HTTPS server
169
+ """
170
+ try:
171
+ # Use config values if not provided
172
+ cert_file = cert_file or self.config.cert_file
173
+ key_file = key_file or self.config.key_file
174
+ ca_cert_file = ca_cert_file or self.config.ca_cert_file
175
+ verify_mode = verify_mode or self.config.verify_mode
176
+ min_version = min_version or self.config.min_tls_version
177
+
178
+ # Validate required files
179
+ if not cert_file:
180
+ raise SSLConfigurationError("Server certificate file is required")
181
+ if not key_file:
182
+ raise SSLConfigurationError("Server private key file is required")
183
+
184
+ # Check cache
185
+ cache_key = f"server_{cert_file}_{key_file}_{ca_cert_file}_{verify_mode}_{min_version}"
186
+ if cache_key in self._contexts:
187
+ return self._contexts[cache_key]
188
+
189
+ # Create SSL context
190
+ context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
191
+
192
+ # Configure verification mode
193
+ context.verify_mode = self._get_verify_mode(verify_mode)
194
+
195
+ # Set minimum TLS version
196
+ context.minimum_version = self._get_tls_version(min_version)
197
+
198
+ # Load certificate and key
199
+ context.load_cert_chain(cert_file, key_file)
200
+
201
+ # Load CA certificate if provided
202
+ if ca_cert_file:
203
+ context.load_verify_locations(ca_cert_file)
204
+
205
+ # Configure cipher suites
206
+ if self.config.cipher_suite:
207
+ context.set_ciphers(self.config.cipher_suite)
208
+
209
+ # Cache context
210
+ self._contexts[cache_key] = context
211
+
212
+ self.logger.info(
213
+ "Server SSL context created successfully",
214
+ extra={
215
+ "cert_file": cert_file,
216
+ "key_file": key_file,
217
+ "verify_mode": verify_mode,
218
+ "min_version": min_version,
219
+ },
220
+ )
221
+
222
+ return context
223
+
224
+ except Exception as e:
225
+ self.logger.error(
226
+ "Failed to create server SSL context",
227
+ extra={"cert_file": cert_file, "key_file": key_file, "error": str(e)},
228
+ )
229
+ raise SSLConfigurationError(
230
+ f"Failed to create server SSL context: {str(e)}"
231
+ )
232
+
233
+ def create_client_context(
234
+ self,
235
+ ca_cert_file: Optional[str] = None,
236
+ client_cert_file: Optional[str] = None,
237
+ client_key_file: Optional[str] = None,
238
+ verify_mode: Optional[str] = None,
239
+ min_version: Optional[str] = None,
240
+ ) -> ssl.SSLContext:
241
+ """
242
+ Create SSL context for client operations.
243
+
244
+ This method creates and configures an SSL context suitable for client
245
+ operations. It handles certificate loading and TLS configuration for
246
+ secure client connections.
247
+
248
+ Args:
249
+ ca_cert_file (Optional[str]): Path to CA certificate file for
250
+ server certificate verification. If None, uses CA from config.
251
+ Must be a valid PEM certificate file path.
252
+ client_cert_file (Optional[str]): Path to client certificate file
253
+ for client authentication. If None, uses client cert from config.
254
+ Must be a valid PEM certificate file path.
255
+ client_key_file (Optional[str]): Path to client private key file.
256
+ If None, uses client key from config. Must be a valid PEM
257
+ private key file path.
258
+ verify_mode (Optional[str]): SSL verification mode. Valid values:
259
+ - "CERT_NONE": No certificate verification
260
+ - "CERT_OPTIONAL": Certificate verification optional
261
+ - "CERT_REQUIRED": Certificate verification required
262
+ If None, uses verify_mode from config.
263
+ min_version (Optional[str]): Minimum TLS version. Valid values:
264
+ - "TLSv1.0": TLS 1.0
265
+ - "TLSv1.1": TLS 1.1
266
+ - "TLSv1.2": TLS 1.2
267
+ - "TLSv1.3": TLS 1.3
268
+ If None, uses min_tls_version from config.
269
+
270
+ Returns:
271
+ ssl.SSLContext: Configured SSL context for client operations.
272
+ The context is properly configured for secure client communication.
273
+
274
+ Raises:
275
+ SSLConfigurationError: If SSL configuration is invalid or
276
+ certificate files cannot be loaded.
277
+ CertificateValidationError: If certificate validation fails.
278
+ FileNotFoundError: If certificate files are not found.
279
+ PermissionError: If certificate files are not readable.
280
+
281
+ Example:
282
+ >>> ssl_manager = SSLManager(config)
283
+ >>> context = ssl_manager.create_client_context(
284
+ ... ca_cert_file="ca.crt",
285
+ ... client_cert_file="client.crt",
286
+ ... client_key_file="client.key"
287
+ ... )
288
+ >>> # Use context for HTTPS client
289
+ """
290
+ try:
291
+ # Use config values if not provided
292
+ ca_cert_file = ca_cert_file or self.config.ca_cert_file
293
+ client_cert_file = client_cert_file or self.config.client_cert_file
294
+ client_key_file = client_key_file or self.config.client_key_file
295
+ verify_mode = verify_mode or self.config.verify_mode
296
+ min_version = min_version or self.config.min_tls_version
297
+
298
+ # Check cache
299
+ cache_key = f"client_{ca_cert_file}_{client_cert_file}_{client_key_file}_{verify_mode}_{min_version}"
300
+ if cache_key in self._contexts:
301
+ return self._contexts[cache_key]
302
+
303
+ # Create SSL context
304
+ context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
305
+
306
+ # Configure verification mode
307
+ context.verify_mode = self._get_verify_mode(verify_mode)
308
+
309
+ # Set minimum TLS version
310
+ context.minimum_version = self._get_tls_version(min_version)
311
+
312
+ # Load CA certificate if provided
313
+ if ca_cert_file:
314
+ context.load_verify_locations(ca_cert_file)
315
+
316
+ # Load client certificate and key if provided
317
+ if client_cert_file and client_key_file:
318
+ context.load_cert_chain(client_cert_file, client_key_file)
319
+
320
+ # Configure cipher suites
321
+ if self.config.cipher_suite:
322
+ context.set_ciphers(self.config.cipher_suite)
323
+
324
+ # Cache context
325
+ self._contexts[cache_key] = context
326
+
327
+ self.logger.info(
328
+ "Client SSL context created successfully",
329
+ extra={
330
+ "ca_cert_file": ca_cert_file,
331
+ "client_cert_file": client_cert_file,
332
+ "verify_mode": verify_mode,
333
+ "min_version": min_version,
334
+ },
335
+ )
336
+
337
+ return context
338
+
339
+ except Exception as e:
340
+ self.logger.error(
341
+ "Failed to create client SSL context",
342
+ extra={
343
+ "ca_cert_file": ca_cert_file,
344
+ "client_cert_file": client_cert_file,
345
+ "error": str(e),
346
+ },
347
+ )
348
+ raise SSLConfigurationError(
349
+ f"Failed to create client SSL context: {str(e)}"
350
+ )
351
+
352
+ def validate_certificate(self, cert_path: str) -> bool:
353
+ """
354
+ Validate certificate file.
355
+
356
+ This method validates a certificate file by checking its format,
357
+ parsing it, and verifying basic certificate properties.
358
+
359
+ Args:
360
+ cert_path (str): Path to certificate file to validate.
361
+ Must be a valid PEM or DER certificate file path.
362
+
363
+ Returns:
364
+ bool: True if certificate is valid, False otherwise.
365
+ Returns True when certificate can be parsed and has
366
+ valid basic properties.
367
+
368
+ Raises:
369
+ FileNotFoundError: If certificate file is not found.
370
+ PermissionError: If certificate file is not readable.
371
+
372
+ Example:
373
+ >>> ssl_manager = SSLManager(config)
374
+ >>> is_valid = ssl_manager.validate_certificate("server.crt")
375
+ >>> if is_valid:
376
+ ... print("Certificate is valid")
377
+ >>> else:
378
+ ... print("Certificate is invalid")
379
+ """
380
+ try:
381
+ # Check if file exists
382
+ if not Path(cert_path).exists():
383
+ self.logger.error(f"Certificate file not found: {cert_path}")
384
+ return False
385
+
386
+ # Parse certificate
387
+ cert = parse_certificate(cert_path)
388
+
389
+ # Basic validation
390
+ if not cert:
391
+ return False
392
+
393
+ # Check if certificate is expired
394
+ expiry_info = get_certificate_expiry(cert_path)
395
+ if expiry_info["is_expired"]:
396
+ self.logger.warning(
397
+ "Certificate is expired",
398
+ extra={
399
+ "cert_path": cert_path,
400
+ "expiry_date": expiry_info["not_after"],
401
+ },
402
+ )
403
+ return False
404
+
405
+ self.logger.info(
406
+ "Certificate validation successful", extra={"cert_path": cert_path}
407
+ )
408
+
409
+ return True
410
+
411
+ except Exception as e:
412
+ self.logger.error(
413
+ "Certificate validation failed",
414
+ extra={"cert_path": cert_path, "error": str(e)},
415
+ )
416
+ return False
417
+
418
+ def get_certificate_info(self, cert_path: str) -> CertificateInfo:
419
+ """
420
+ Get detailed certificate information.
421
+
422
+ This method extracts comprehensive information from a certificate
423
+ including subject, issuer, validity dates, and extensions.
424
+
425
+ Args:
426
+ cert_path (str): Path to certificate file. Must be a valid
427
+ PEM or DER certificate file path.
428
+
429
+ Returns:
430
+ CertificateInfo: Certificate information object containing:
431
+ - subject: Certificate subject
432
+ - issuer: Certificate issuer
433
+ - serial_number: Certificate serial number
434
+ - not_before: Certificate validity start date
435
+ - not_after: Certificate validity end date
436
+ - key_algorithm: Public key algorithm
437
+ - key_size: Public key size in bits
438
+ - signature_algorithm: Signature algorithm
439
+ - extensions: Certificate extensions
440
+ - is_self_signed: Whether certificate is self-signed
441
+ - fingerprint_sha1: SHA1 fingerprint
442
+ - fingerprint_sha256: SHA256 fingerprint
443
+
444
+ Raises:
445
+ CertificateValidationError: If certificate cannot be parsed
446
+ or information extraction fails.
447
+ FileNotFoundError: If certificate file is not found.
448
+ PermissionError: If certificate file is not readable.
449
+
450
+ Example:
451
+ >>> ssl_manager = SSLManager(config)
452
+ >>> info = ssl_manager.get_certificate_info("server.crt")
453
+ >>> print(f"Subject: {info.subject}")
454
+ >>> print(f"Expires: {info.not_after}")
455
+ >>> print(f"Key size: {info.key_size} bits")
456
+ """
457
+ try:
458
+ # Check cache first
459
+ if cert_path in self._certificate_cache:
460
+ return self._certificate_cache[cert_path]
461
+
462
+ # Extract certificate information
463
+ cert_data = extract_certificate_info(cert_path)
464
+
465
+ # Create CertificateInfo object
466
+ cert_info = CertificateInfo(
467
+ subject=cert_data.get("subject", ""),
468
+ issuer=cert_data.get("issuer", ""),
469
+ serial_number=cert_data.get("serial_number", ""),
470
+ not_before=cert_data.get("not_before"),
471
+ not_after=cert_data.get("not_after"),
472
+ key_algorithm=cert_data.get("public_key_algorithm", ""),
473
+ key_size=cert_data.get("key_size", 0),
474
+ signature_algorithm=cert_data.get("signature_algorithm", ""),
475
+ extensions=cert_data.get("extensions", {}),
476
+ is_self_signed=is_certificate_self_signed(cert_path),
477
+ fingerprint_sha1=cert_data.get("fingerprint_sha1", ""),
478
+ fingerprint_sha256=cert_data.get("fingerprint_sha256", ""),
479
+ )
480
+
481
+ # Cache the result
482
+ self._certificate_cache[cert_path] = cert_info
483
+
484
+ self.logger.info(
485
+ "Certificate information extracted successfully",
486
+ extra={"cert_path": cert_path},
487
+ )
488
+
489
+ return cert_info
490
+
491
+ except Exception as e:
492
+ self.logger.error(
493
+ "Failed to get certificate information",
494
+ extra={"cert_path": cert_path, "error": str(e)},
495
+ )
496
+ raise CertificateValidationError(
497
+ f"Failed to get certificate information: {str(e)}"
498
+ )
499
+
500
+ def validate_certificate_chain(self, cert_path: str, ca_cert_path: str) -> bool:
501
+ """
502
+ Validate certificate chain against CA certificate.
503
+
504
+ This method validates a certificate chain by checking if the
505
+ certificate is signed by the provided CA certificate.
506
+
507
+ Args:
508
+ cert_path (str): Path to certificate to validate. Must be a
509
+ valid PEM or DER certificate file path.
510
+ ca_cert_path (str): Path to CA certificate. Must be a valid
511
+ PEM or DER certificate file path.
512
+
513
+ Returns:
514
+ bool: True if certificate chain is valid, False otherwise.
515
+ Returns True when certificate is properly signed by the
516
+ CA certificate.
517
+
518
+ Raises:
519
+ FileNotFoundError: If certificate files are not found.
520
+ PermissionError: If certificate files are not readable.
521
+
522
+ Example:
523
+ >>> ssl_manager = SSLManager(config)
524
+ >>> is_valid = ssl_manager.validate_certificate_chain(
525
+ ... "server.crt", "ca.crt"
526
+ ... )
527
+ >>> if is_valid:
528
+ ... print("Certificate chain is valid")
529
+ >>> else:
530
+ ... print("Certificate chain is invalid")
531
+ """
532
+ try:
533
+ # Validate certificate chain
534
+ is_valid = validate_certificate_chain(cert_path, ca_cert_path)
535
+
536
+ self.logger.info(
537
+ "Certificate chain validation completed",
538
+ extra={
539
+ "cert_path": cert_path,
540
+ "ca_cert_path": ca_cert_path,
541
+ "is_valid": is_valid,
542
+ },
543
+ )
544
+
545
+ return is_valid
546
+
547
+ except Exception as e:
548
+ self.logger.error(
549
+ "Certificate chain validation failed",
550
+ extra={
551
+ "cert_path": cert_path,
552
+ "ca_cert_path": ca_cert_path,
553
+ "error": str(e),
554
+ },
555
+ )
556
+ return False
557
+
558
+ def check_certificate_expiry(self, cert_path: str) -> Dict:
559
+ """
560
+ Check certificate expiry information.
561
+
562
+ This method provides detailed information about certificate
563
+ expiry including days until expiry and expiry status.
564
+
565
+ Args:
566
+ cert_path (str): Path to certificate file. Must be a valid
567
+ PEM or DER certificate file path.
568
+
569
+ Returns:
570
+ Dict: Certificate expiry information containing:
571
+ - not_after: Certificate expiry date
572
+ - not_before: Certificate validity start date
573
+ - days_until_expiry: Days until certificate expires
574
+ - is_expired: Whether certificate is expired
575
+ - expires_soon: Whether certificate expires within 30 days
576
+ - status: Expiry status (valid, expires_soon, expired)
577
+ - total_seconds_until_expiry: Seconds until expiry
578
+
579
+ Raises:
580
+ CertificateValidationError: If certificate cannot be parsed
581
+ or expiry information extraction fails.
582
+ FileNotFoundError: If certificate file is not found.
583
+ PermissionError: If certificate file is not readable.
584
+
585
+ Example:
586
+ >>> ssl_manager = SSLManager(config)
587
+ >>> expiry_info = ssl_manager.check_certificate_expiry("server.crt")
588
+ >>> if expiry_info["is_expired"]:
589
+ ... print("Certificate is expired!")
590
+ >>> elif expiry_info["expires_soon"]:
591
+ ... print(f"Certificate expires in {expiry_info['days_until_expiry']} days")
592
+ """
593
+ try:
594
+ expiry_info = get_certificate_expiry(cert_path)
595
+
596
+ self.logger.info(
597
+ "Certificate expiry check completed",
598
+ extra={
599
+ "cert_path": cert_path,
600
+ "days_until_expiry": expiry_info["days_until_expiry"],
601
+ "status": expiry_info["status"],
602
+ },
603
+ )
604
+
605
+ return expiry_info
606
+
607
+ except Exception as e:
608
+ self.logger.error(
609
+ "Certificate expiry check failed",
610
+ extra={"cert_path": cert_path, "error": str(e)},
611
+ )
612
+ raise CertificateValidationError(
613
+ f"Certificate expiry check failed: {str(e)}"
614
+ )
615
+
616
+ def clear_cache(self) -> None:
617
+ """Clear SSL context and certificate caches."""
618
+ self._contexts.clear()
619
+ self._certificate_cache.clear()
620
+ self.logger.info("SSL caches cleared")
621
+
622
+ @property
623
+ def is_ssl_enabled(self) -> bool:
624
+ """
625
+ Check if SSL/TLS is enabled in the configuration.
626
+
627
+ This property indicates whether SSL/TLS functionality is enabled
628
+ based on the current configuration settings.
629
+
630
+ Returns:
631
+ bool: True if SSL/TLS is enabled, False otherwise.
632
+ Returns True when config.enabled is True and valid
633
+ certificate/key files are configured.
634
+
635
+ Example:
636
+ >>> ssl_manager = SSLManager(config)
637
+ >>> if ssl_manager.is_ssl_enabled:
638
+ ... context = ssl_manager.create_server_context()
639
+ ... print("SSL context created successfully")
640
+ >>> else:
641
+ ... print("SSL is not properly configured")
642
+ """
643
+ return (
644
+ self.config.enabled
645
+ and self.config.cert_file
646
+ and self.config.key_file
647
+ and Path(self.config.cert_file).exists()
648
+ and Path(self.config.key_file).exists()
649
+ )
650
+
651
+ @property
652
+ def supported_tls_versions(self) -> List[str]:
653
+ """
654
+ Get list of supported TLS versions.
655
+
656
+ Returns:
657
+ List[str]: List of supported TLS version strings.
658
+ """
659
+ return ["TLSv1.0", "TLSv1.1", "TLSv1.2", "TLSv1.3"]
660
+
661
+ @property
662
+ def default_cipher_suite(self) -> str:
663
+ """
664
+ Get default cipher suite configuration.
665
+
666
+ Returns:
667
+ str: Default cipher suite string.
668
+ """
669
+ return self.config.cipher_suite or "ECDHE-RSA-AES256-GCM-SHA384"
670
+
671
+ def _validate_configuration(self) -> None:
672
+ """Validate SSL configuration."""
673
+ if not self.config.enabled:
674
+ return
675
+
676
+ # Check certificate files if SSL is enabled
677
+ if self.config.cert_file and not Path(self.config.cert_file).exists():
678
+ raise SSLConfigurationError(
679
+ f"Certificate file not found: {self.config.cert_file}"
680
+ )
681
+
682
+ if self.config.key_file and not Path(self.config.key_file).exists():
683
+ raise SSLConfigurationError(
684
+ f"Private key file not found: {self.config.key_file}"
685
+ )
686
+
687
+ if self.config.ca_cert_file and not Path(self.config.ca_cert_file).exists():
688
+ raise SSLConfigurationError(
689
+ f"CA certificate file not found: {self.config.ca_cert_file}"
690
+ )
691
+
692
+ def _get_verify_mode(self, verify_mode: str) -> int:
693
+ """Convert verify mode string to SSL constant."""
694
+ verify_modes = {
695
+ "CERT_NONE": ssl.CERT_NONE,
696
+ "CERT_OPTIONAL": ssl.CERT_OPTIONAL,
697
+ "CERT_REQUIRED": ssl.CERT_REQUIRED,
698
+ }
699
+
700
+ if verify_mode not in verify_modes:
701
+ raise SSLConfigurationError(f"Invalid verify mode: {verify_mode}")
702
+
703
+ return verify_modes[verify_mode]
704
+
705
+ def _get_tls_version(self, version: str) -> int:
706
+ """Convert TLS version string to SSL constant."""
707
+ tls_versions = {
708
+ "TLSv1.0": ssl.TLSVersion.TLSv1,
709
+ "TLSv1.1": ssl.TLSVersion.TLSv1_1,
710
+ "TLSv1.2": ssl.TLSVersion.TLSv1_2,
711
+ "TLSv1.3": ssl.TLSVersion.TLSv1_3,
712
+ }
713
+
714
+ if version not in tls_versions:
715
+ raise SSLConfigurationError(f"Invalid TLS version: {version}")
716
+
717
+ return tls_versions[version]
718
+
719
+
720
+ class SSLConfigurationError(Exception):
721
+ """Raised when SSL configuration is invalid."""
722
+
723
+ def __init__(self, message: str, error_code: int = -32001):
724
+ self.message = message
725
+ self.error_code = error_code
726
+ super().__init__(self.message)
727
+
728
+
729
+ class CertificateValidationError(Exception):
730
+ """Raised when certificate validation fails."""
731
+
732
+ def __init__(self, message: str, error_code: int = -32002):
733
+ self.message = message
734
+ self.error_code = error_code
735
+ super().__init__(self.message)