# 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 the `baresip:` URI - Path used by the app: `/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": "", "ciphertext": "", "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.