3.9 KiB
Provisioning server
Baresip Studio can provision accounts from a server-encrypted bundle using a per-device RSA key pair generated in Android Keystore.
Endpoint contract
- Base URL: whatever you pass as
endpoint=in thebaresip:URI - Path used by the app:
<endpoint>/bundle - Method: POST
- Request Content-Type: application/json
- Response Content-Type: application/json
Enrollment request body { "extension": "101", "public_key": "" }
Provisioning bundle response body { "encrypted_key": "", "iv": "<base64 12-byte GCM nonce>", "ciphertext": "", "tag": "<base64 16-byte GCM tag>" }
The plaintext AES-GCM payload after decryption should be a JSON object like: { "connect_string": "sip:101@pbx.example.com", "transport": "tls", "sip_verify_server": "yes", "username": "101", "password": "secret", "display_name": "Extension 101", "account_name": "Desk Phone", "outbound1": "sip:pbx.example.com", "outbound2": "", "register": true, "reg_int": 900, "check_origin": true, "media_enc": "", "media_nat": "", "stun_server": "", "stun_user": "", "stun_pass": "", "rtcp_mux": false, "rel100": false, "dtmf_mode": 2, "answer_mode": 0, "auto_redirect": false, "vm_uri": "", "country_code": "", "tel_provider": "", "numeric_keypad": false, "default_account": true, "custom_params": "", "client_cert": "", "client_key": "", "ca_certs": "" }
Python server example
- Requirements: python >= 3.10
- pip install fastapi uvicorn pydantic
from fastapi import FastAPI, HTTPException from pydantic import BaseModel from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.ciphers.aead import AESGCM import base64 import json
app = FastAPI()
class EnrollRequest(BaseModel): extension: str public_key: str
class BundleResponse(BaseModel): encrypted_key: str iv: str ciphertext: str tag: str
SERVER_RSA_PRIVATE_KEY_PEM = """-----BEGIN RSA PRIVATE KEY----- ... -----END RSA PRIVATE KEY-----"""
def server_private_key(): return serialization.load_pem_private_key( SERVER_RSA_PRIVATE_KEY_PEM.encode(), password=None )
@app.post("/bundle", response_model=BundleResponse) def bundle(req: EnrollRequest): pub = serialization.load_der_public_key(base64.b64decode(req.public_key)) aes_key = AESGCM.generate_key(bit_length=256) aesgcm = AESGCM(aes_key) iv = b"012345678901" # replace with os.urandom(12) in production payload = { "connect_string": f"sip:{req.extension}@pbx.example.com", "transport": "tls", "sip_verify_server": "yes", "username": req.extension, "password": "secret", "client_cert": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----", "client_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----", "ca_certs": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----", } plaintext = json.dumps(payload).encode("utf-8") encrypted = aesgcm.encrypt(iv, plaintext, None) encrypted_key = server_private_key().encrypt( aes_key, padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None), ) ciphertext = encrypted[:-16] tag = encrypted[-16:] return BundleResponse( encrypted_key=base64.b64encode(encrypted_key).decode(), iv=base64.b64encode(iv).decode(), ciphertext=base64.b64encode(ciphertext).decode(), tag=base64.b64encode(tag).decode(), )
Example baresip URI baresip://provision?endpoint=https://pbx.example.com&extension=101
Security notes
- Always serve over HTTPS.
- Keep SERVER_RSA_PRIVATE_KEY_PEM secret and offline from the app.
- Treat the bundle URL as a secret; short expiry and single-use are recommended.