Skip to content

Commit f1738f9

Browse files
Adds 5_hello_world_rsa example + integration test
1 parent 9ff8f5d commit f1738f9

7 files changed

Lines changed: 646 additions & 0 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Hello World with RSA Encryption Example
2+
3+
This example demonstrates how to use RSA encryption with INTERSECT services.
4+
5+
## Overview
6+
7+
RSA encryption provides end-to-end encryption for messages between clients and services:
8+
- Clients encrypt requests using the service's public key
9+
- Services encrypt responses using the client's public key
10+
- Each party decrypts messages using their own private key
11+
12+
## Files
13+
14+
- `hello_service.py` - Service implementation with RSA encryption enabled
15+
- `hello_client.py` - Client implementation that sends RSA encrypted messages
16+
- `hello_service_schema.json` - Service schema defining available operations
17+
18+
## Key Features
19+
20+
1. **Automatic RSA Key Generation**:
21+
- Both service and client automatically generate RSA key pairs internally
22+
- No manual key management required
23+
- Service automatically handles public key requests from clients
24+
25+
2. **Transparent Encryption**:
26+
- Simply set `encryption_scheme='RSA'` when sending messages
27+
- INTERSECT SDK handles all encryption/decryption automatically
28+
- Public key exchange happens automatically in the background
29+
30+
## Running the Example
31+
32+
1. Start the service:
33+
```bash
34+
python -m examples.5_hello_world_rsa.hello_service
35+
```
36+
37+
2. In another terminal, run the client:
38+
```bash
39+
python -m examples.5_hello_world_rsa.hello_client
40+
```
41+
42+
## Expected Output
43+
44+
Client output:
45+
```
46+
Hello, hello_client!
47+
```
48+
49+
## How It Works
50+
51+
1. Client starts and generates its RSA key pair
52+
2. Client sends encrypted message to service (encryption_scheme='RSA')
53+
3. Client first fetches service's public key via `intersect_sdk.get_public_key`
54+
4. Client encrypts the request using service's public key
55+
5. Service receives encrypted request and decrypts it using its private key
56+
6. Service processes the request and generates a response
57+
7. Service fetches client's public key (if not cached)
58+
8. Service encrypts response using client's public key
59+
9. Client receives encrypted response and decrypts it using its private key
60+
10. Client prints the decrypted response
61+
62+
## Security Notes
63+
64+
- Each service and client automatically generates its own unique RSA key pair on startup
65+
- Private keys are managed internally by the SDK and never transmitted
66+
- Public keys are exchanged automatically via the INTERSECT messaging system
67+
- RSA encryption uses 2048-bit keys for strong security
68+
- No manual key management is required - the SDK handles everything automatically
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Init file for hello world RSA encryption example."""
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""
2+
Hello World Client with RSA Encryption
3+
4+
This example demonstrates how to use RSA encryption when sending messages to INTERSECT services.
5+
The client will encrypt requests using the service's public key and decrypt responses using its own private key.
6+
"""
7+
8+
import time
9+
10+
from intersect_sdk import (
11+
HierarchyConfig,
12+
IntersectClient,
13+
IntersectServiceConfig,
14+
)
15+
from intersect_sdk.shared_callback_definitions import IntersectDirectMessageParams
16+
17+
"""
18+
step one: create IntersectServiceConfig
19+
20+
Note: The client automatically generates an RSA key pair internally.
21+
You don't need to manually create or manage encryption keys.
22+
"""
23+
config = IntersectServiceConfig(
24+
brokers=[
25+
{
26+
'username': 'intersect_username',
27+
'password': 'intersect_password',
28+
'port': 1883,
29+
'protocol': 'mqtt5.0',
30+
}
31+
],
32+
hierarchy=HierarchyConfig(
33+
organization='hello-organization',
34+
facility='hello-facility',
35+
system='hello-system',
36+
subsystem='hello-subsystem-client',
37+
service='hello-client',
38+
),
39+
)
40+
41+
"""
42+
step two: set up callback and then instantiate client
43+
"""
44+
45+
46+
def user_callback(source: str, operation: str, has_error: bool, payload: dict) -> None:
47+
if has_error:
48+
print(f'Error from {source}: {payload}')
49+
else:
50+
print(payload)
51+
52+
53+
intersect_client = IntersectClient(
54+
config.hierarchy,
55+
config=config,
56+
)
57+
58+
"""
59+
step three: start up client with user callback
60+
"""
61+
intersect_client.startup(user_callback=user_callback)
62+
63+
"""
64+
step four: send message with RSA encryption
65+
Note the encryption_scheme='RSA' parameter
66+
"""
67+
message_params = IntersectDirectMessageParams(
68+
destination='hello-organization.hello-facility.hello-system.hello-subsystem.hello-service',
69+
operation='HelloExample.say_hello_to_name',
70+
payload='hello_client',
71+
encryption_scheme='RSA', # Enable RSA encryption
72+
)
73+
74+
intersect_client.send_message(message_params)
75+
time.sleep(3)
76+
77+
"""
78+
step five: shut down client
79+
"""
80+
intersect_client.shutdown()
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
"""
2+
Hello World Service with RSA Encryption
3+
4+
This example demonstrates how to use RSA encryption with INTERSECT services.
5+
The service will encrypt responses to clients using the client's public key.
6+
"""
7+
8+
import logging
9+
10+
from intersect_sdk import (
11+
HierarchyConfig,
12+
IntersectBaseCapabilityImplementation,
13+
IntersectService,
14+
IntersectServiceConfig,
15+
default_intersect_lifecycle_loop,
16+
intersect_message,
17+
intersect_status,
18+
)
19+
20+
logging.basicConfig(level=logging.INFO)
21+
logger = logging.getLogger(__name__)
22+
23+
24+
class HelloServiceCapabilityImplementation(IntersectBaseCapabilityImplementation):
25+
"""Capability implementation with RSA encryption support."""
26+
27+
intersect_sdk_capability_name = 'HelloExample'
28+
29+
@intersect_status()
30+
def status(self) -> str:
31+
"""Basic status function which returns a hard-coded string."""
32+
return 'Up'
33+
34+
@intersect_message()
35+
def say_hello_to_name(self, name: str) -> str:
36+
"""Takes in a string parameter and says 'Hello' to the parameter!"""
37+
return f'Hello, {name}!'
38+
39+
40+
if __name__ == '__main__':
41+
"""
42+
Create a service with RSA encryption enabled.
43+
44+
The service automatically generates an RSA key pair internally.
45+
When clients request RSA encryption, the service will:
46+
1. Fetch the client's public key
47+
2. Encrypt the response using the client's public key
48+
3. The client decrypts using its private key
49+
"""
50+
51+
from_config_file = {
52+
'brokers': [
53+
{
54+
'username': 'intersect_username',
55+
'password': 'intersect_password',
56+
'port': 1883,
57+
'protocol': 'mqtt5.0',
58+
}
59+
],
60+
'hierarchy': {
61+
'organization': 'hello-organization',
62+
'facility': 'hello-facility',
63+
'system': 'hello-system',
64+
'subsystem': 'hello-subsystem',
65+
'service': 'hello-service',
66+
},
67+
}
68+
69+
config = IntersectServiceConfig(**from_config_file)
70+
71+
"""
72+
step two: create instances of your capabilities, which you will inject into the service
73+
"""
74+
capabilities = [HelloServiceCapabilityImplementation()]
75+
76+
"""
77+
step three: create the service using your capability and configuration
78+
"""
79+
intersect_service = IntersectService(capabilities, config)
80+
81+
"""
82+
step four: utilize the default_intersect_lifecycle_loop function to manage startup,
83+
the user lifecycle loop, and shutdown automatically. You must pass it the service
84+
and a function containing your optional lifecycle code to execute.
85+
"""
86+
default_intersect_lifecycle_loop(intersect_service)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"$schema": "https://raw.githubusercontent.com/INTERSECT-SDK/python-sdk/main/src/intersect_sdk/_internal/schema.schema.json",
3+
"intersect_sdk_version": "0.8.0",
4+
"name": "hello-organization.hello-facility.hello-system.hello-subsystem.hello-service",
5+
"capabilities": [
6+
{
7+
"name": "HelloExample",
8+
"functions": [
9+
{
10+
"name": "say_hello_to_name",
11+
"parameters": {
12+
"type": "string"
13+
},
14+
"returns": {
15+
"type": "string"
16+
}
17+
}
18+
],
19+
"encryption_schemes": [
20+
"NONE",
21+
"RSA"
22+
]
23+
}
24+
]
25+
}

tests/e2e/test_examples.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,8 @@ def test_example_4_service_to_service_events():
150150
'From event "internal_service_event", received message "not_feeling_creative" from "example-organization.example-facility.example-system.example-subsystem.internal-service"\n'
151151
'From event "internal_service_event", received message "not_feeling_creative" from "example-organization.example-facility.example-system.example-subsystem.internal-service"\n'
152152
)
153+
154+
155+
def test_example_5_hello_world_rsa():
156+
"""Test hello world example with RSA encryption."""
157+
assert run_example_test('5_hello_world_rsa') == 'Hello, hello_client!\n'

0 commit comments

Comments
 (0)