Search by

Secure Exchanges SDK for PHP - Secure messaging, file exchange, and electronic signatures

Maintainers

Package info

github.com/Secure-exchanges/php-sdk

pkg:composer/secure-exchanges/sdk

Transparency log

Statistics

Installs: 3

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

2026.08.26 2026-08-27 18:01 UTC

This package is auto-updated.

Last update: 2026-08-27 19:43:55 UTC


README

English | Français

PHP SDK for the Secure Exchanges API: end-to-end encrypted messaging, secure file transfer, electronic signature, and PKI certification of PDF documents.

With this SDK, from your PHP application you can:

  • send end-to-end encrypted messages and files, with or without a password, with SMS-based 2FA verification;
  • have PDF documents signed electronically (signature zones, initials, dates, text fields, and more);
  • certify PDFs with a digital certificate (PKI) before sending them for signature;
  • read and delete messages, and create secure reply envelopes;
  • view activity logs (sends, opens, signatures).

Table of contents

  1. Security
  2. Installation
  3. Requirements
  4. Credentials
  5. Configuration
  6. SDK architecture
  7. Usage guide
  8. API reference
  9. Support
  10. License

Security

  • End-to-end encryption: the content (message body, files) is encrypted inside your application before it leaves your server, using keys generated locally and never transmitted to the Secure Exchanges server: the server only ever sees encrypted data.
  • Post-quantum key exchange: ML-KEM-1024 (FIPS 203), resistant to quantum-computer attacks, signed with ML-DSA-87 (FIPS 204).
  • AES-256 encryption of the content and of all exchanges with the server.
  • Interception protection: any man-in-the-middle (MITM) attack attempt causes the connection to fail.
  • Nothing to manage: the SDK automatically applies the entire protocol on every call: you never have to handle any cryptographic primitives yourself.

Installation

composer require secure-exchanges/sdk

Dependencies

Dependencies are managed by Composer (see composer.json):

  • paragonie/pqcrypto_compat (0.3.2) : post-quantum cryptography (ML-KEM / ML-DSA)
  • phpseclib/phpseclib : cryptography (RSA for some auxiliary functions, utilities)
  • setasign/fpdi, setasign/fpdf : PDF manipulation
  • smalot/pdfparser : PDF reading
  • chrome-php/chrome : PKI certification (headless browser)
  • giggsey/libphonenumber-for-php : phone number validation

Requirements

To use the SDK, you need:

  • PHP 8.2 or newer (composer install fails on PHP 8.1)
  • PHP extensions: openssl, json, mbstring, curl
  • ext-sockets : required for PKI signing (used by chrome-php)
  • Google Chrome or Chromium installed on the machine, required only for PDF certification (PKI); the binary path can be provided via the SE_CHROME_PATH environment variable
  • A serial number (serial) : the UUID of your license
  • An API user ID (api_user) : the UUID of the API user
  • An API password (api_password) : the UUID of the API password

Recommended in production: the native ext-pqcrypto extension (github.com/paragonie/ext-pqcrypto), significantly faster for post-quantum cryptography than the pure-PHP implementation installed by Composer. No code changes are needed: the SDK automatically switches to the extension as soon as it is loaded. Otherwise, enabling ext-opcache (with JIT) noticeably speeds up the pure-PHP implementation.

Credentials

Where to find your credentials

The 3 required credentials (serial, api_user, api_password) are UUIDs (GUIDs) that you obtain from your Secure Exchanges administration portal when creating your API license.

Credential Description
serial Serial number of your license
api_user API user ID
api_password API user password

Configuration

Selecting the environment

SettingsHelper selects the environment and returns its endpoints, nothing else. The SDK stores no credentials and never reads them from the process environment: the serial, API user and API password are yours, and are passed explicitly to every call.

use SecureExchanges\SDK\Client\SecureExchangesClient;
use SecureExchanges\SDK\Client\SEMSClient;
use SecureExchanges\SDK\Helpers\SettingsHelper;

// ['apiEndpoint' => ..., 'semsEndpoint' => ..., 'environment' => 'production']
$endpoints = SettingsHelper::configureProduction();

$client = new SecureExchangesClient($endpoints['apiEndpoint']);
$sems   = new SEMSClient($endpoints['semsEndpoint']);

// loadMyCredentials() is your own function: it returns the three UUIDs of your
// licence. See "Protecting your credentials" below for where to keep them.
[$serial, $apiUser, $apiPassword] = loadMyCredentials();
Method Environment Endpoints
configureProduction() production www.secure-exchanges.com
configurePreview() preview (test) preview.secure-exchanges.com

Preview and production credentials are different: a production serial will not work in preview, and vice versa.

Individual endpoints can still be overridden afterwards with setCustomFileHandler(), setCustomFileUploadHandler() and setCustomSemsEndpoint().

Protecting your credentials

These three UUIDs are the equivalent of a password on your Secure Exchanges account: whoever holds them can send, read and delete messages in your name, and the usage is billed to you. How they are stored is entirely your decision, and your responsibility.

  • Never commit them. Keep them out of the repository, and check the history too: git log -S <serial> finds a secret committed once and removed later. A secret that reached a remote is compromised: rotate it, don't just delete the line.
  • Never hard-code them in source. They end up in stack traces, in bug reports, in the packages you ship, and in every fork of the code.
  • Prefer a secret manager. Encrypted at rest, access audited, rotation possible without a redeploy.
  • If you keep them in a file, encrypt it or lock it down. Outside the document root, owned by the PHP-FPM user, permissions 0400, and excluded from your backups or encrypted inside them.
  • Never put them in an FPM pool's env[]. They then become visible in phpinfo(), in $_ENV, and in most error dumps, for the whole lifetime of every worker. The same goes for putenv(): a value set that way still shows up in phpinfo().
  • Keep them out of your logs. Exclude them explicitly from your error handler, from your crash reporter and from request dumps, which capture the environment and superglobals by default. Never var_dump() a configuration array in production.
  • Rotate them from the administration portal the moment you suspect an exposure: a laptop lost, a log shipped to a third party, a developer leaving.

SDK architecture

The SDK is organized into three main modules (namespace SecureExchanges\SDK, PSR-4 from src/):

  • Client/ contains the HTTP layer: SecureExchangesClient (main API), SEMSClient (email delivery service), both built on BaseClient.
  • Helpers/ contains the business logic: MessageHelper (send/read/delete), HandShakeHelper (post-quantum handshake), CryptoHelper (AES/RSA/SHA primitives), SignHelper and ZoneBuilder (signature zones), PkiFileHelper (PDF certification), FileHelper (encrypted upload/download), SettingsHelper (configuration), plus validation helpers (EmailHelper, PhoneHelper), logging (LogsHelper), and licensing (LicenceHelper).
  • Models/ contains the typed objects: responses (Answer/), entities (Entity/), enumerations (Enum/), transport objects (Transport/), JSON models (JsonModels/), arguments (Args/). The Callback/ folder contains the notification (callback) objects.

Usage guide

All the examples below assume the following setup:

use SecureExchanges\SDK\Client\SecureExchangesClient;
use SecureExchanges\SDK\Client\SEMSClient;
use SecureExchanges\SDK\Helpers\SettingsHelper;
use SecureExchanges\SDK\Helpers\MessageHelper;
use SecureExchanges\SDK\Models\Entity\RecipientInfo;

$endpoints = SettingsHelper::configureProduction();

$client = new SecureExchangesClient($endpoints['apiEndpoint']);
$sems   = new SEMSClient($endpoints['semsEndpoint']);

// Your own function: it returns the three UUIDs of your licence.
[$serial, $apiUser, $apiPassword] = loadMyCredentials();

Sending a simple message

$answer = MessageHelper::multiRecipientMessage(
    apiClient:   $client,
    serial:      $serial,
    apiUser:     $apiUser,
    apiPassword: $apiPassword,
    recipients:  [new RecipientInfo(email: 'recipient@example.com')],
    message:     'Hello, here is your secure message.',
    subject:     'Confidential message',
    cultureId:   'en-US',
    semsClient:  $sems
);

if ($answer->status === 200 && $answer->recipientsAnswer) {
    echo 'URL: ' . $answer->recipientsAnswer[0]->answer->url;
}

Sending with a password and SMS 2FA

The password and the delivery channel are two independent, combinable options. The channel is controlled by the SendMethodEnum enumeration:

Value Behavior
SendMethodEnum::ONLY_EMAIL (default) Link sent by email
SendMethodEnum::SMS_ONLY Link sent by SMS only
SendMethodEnum::SMS_WITH_EMAIL_CODE Link by SMS, opening code by email
SendMethodEnum::EMAIL_WITH_SMS_CODE Link by email, opening code by SMS (2FA)
use SecureExchanges\SDK\Models\Enum\SendMethodEnum;

$answer = MessageHelper::multiRecipientMessage(
    apiClient:   $client,
    serial:      $serial,
    apiUser:     $apiUser,
    apiPassword: $apiPassword,
    recipients:  [new RecipientInfo(email: 'user@example.com', phone: '+15145551234')],
    message:     'Confidential document attached.',
    subject:     'Protected document',
    password:    'SecretPassword123',
    sendMethod:  SendMethodEnum::EMAIL_WITH_SMS_CODE,
    cultureId:   'en-US',
    maximumOpenTime:   3,     // maximum number of opens
    minutesExpiration: 1440,  // expiration in minutes (1440 = 24 h)
    semsClient:  $sems
);

Sending with attachments

// From disk
$answer = MessageHelper::multiRecipientMessage(
    apiClient:   $client,
    serial:      $serial,
    apiUser:     $apiUser,
    apiPassword: $apiPassword,
    recipients:  [new RecipientInfo(email: 'recipient@example.com')],
    message:     'See attached files.',
    subject:     'Secure files',
    filesPath:   ['/path/to/document.pdf', '/path/to/image.png'],
    semsClient:  $sems
);

// From memory
$pdfBytes = file_get_contents('/path/to/report.pdf');
$csvBytes = file_get_contents('/path/to/data.csv');

$answer = MessageHelper::multiRecipientMessage(
    apiClient:   $client,
    serial:      $serial,
    apiUser:     $apiUser,
    apiPassword: $apiPassword,
    recipients:  [new RecipientInfo(email: 'recipient@example.com')],
    message:     'Report and data attached.',
    subject:     'Monthly report',
    filesList:   [
        ['binary' => $pdfBytes, 'file_name' => 'report.pdf'],
        ['binary' => $csvBytes, 'file_name' => 'data.csv'],
    ],
    semsClient:  $sems
);

Both parameters (filesPath and filesList) can be combined in the same send.

Sending with PKI certification

PKI certification applies a digital certificate to the PDF before it is sent for signature. It requires Chrome/Chromium (headless browser via chrome-php) and the ext-sockets extension. Signature zones are defined with ZoneBuilder, which reads the actual dimensions of the PDF pages. The flow:

use SecureExchanges\SDK\Helpers\CryptoHelper;
use SecureExchanges\SDK\Helpers\ZoneBuilder;
use SecureExchanges\SDK\Helpers\SignHelper;
use SecureExchanges\SDK\Helpers\PkiFileHelper;
use SecureExchanges\SDK\Models\Args\CertifyPdfArgs;
use SecureExchanges\SDK\Models\Args\FileArgs;
use SecureExchanges\SDK\Models\Entity\SignZoneDefinition;
use SecureExchanges\SDK\Models\Transport\FileZoneDefinition;
use SecureExchanges\SDK\Models\Transport\RecipientZoneDefinition;

// 1. Read the PDF and compute its SHA512
$pdfPath  = '/path/to/contract.pdf';
$pdfBytes = file_get_contents($pdfPath);
$sha512   = CryptoHelper::getSha512HashOfBytes($pdfBytes);

// 2. Define the signature zones with ZoneBuilder
$zoneBuilder = new ZoneBuilder($pdfPath, isPki: true);
$zones = [
    // Signature zone on page 1
    $zoneBuilder->createFieldSignZone(
        page: 1, fieldName: 'signature1',
        x: 60, y: 400, width: 200, height: 50
    ),
    // Date zone filled in automatically at signing time
    $zoneBuilder->createFieldDateSignZone(
        page: 1, fieldName: 'date1',
        x: 60, y: 500, width: 100, height: 25, fontSize: 12
    ),
];
$zoneBuilder->close();

// 3. Bind the zones to the recipient (file referenced by its SHA512)
$recipientZones = [
    new RecipientZoneDefinition(
        email: 'signer@example.com',
        phone: null,
        zonesDefByFile: [
            new FileZoneDefinition(
                uniqueName: $sha512,
                zonesDef: new SignZoneDefinition(mode: 'define', zones: $zones),
                doNotEncryptPDF: false,
                doNotAppendCertificateToFile: false,
                isPKIFile: true
            )
        ]
    )
];

// 4. Get the certification token
$tokenResponse = SignHelper::getCertificationToken(
    apiClient:    $client,
    serial:       $serial,
    apiUser:      $apiUser,
    apiPassword:  $apiPassword,
    originalHash: $sha512,
    cultureId:    'en-US',
    tokenType:    'CertifyDocument'
);
$certData = $tokenResponse['certification_data'];

// 5. Certify the PDF (headless browser)
$certifyArgs = new CertifyPdfArgs(
    file:                     new FileArgs($pdfBytes, 'contract.pdf'),
    recipients:               ['signer@example.com'],
    certifyData:              $certData,
    detectExistingFields:     true,
    recipientZoneDefinitions: $recipientZones
);

$pkiHelper    = new PkiFileHelper(isPreview: false, culture: 'en-US');
$certifiedPdf = $pkiHelper->certifyPkiPdf($certifyArgs);
$pkiHelper->close();

// 6. Send the certified PDF
$answer = MessageHelper::multiRecipientMessage(
    apiClient:     $client,
    serial:        $serial,
    apiUser:       $apiUser,
    apiPassword:   $apiPassword,
    recipients:    [new RecipientInfo(email: 'signer@example.com')],
    message:       'Please sign this certified document.',
    subject:       'PKI document to sign',
    cultureId:     'en-US',
    certifiedPdfs: [$certifiedPdf],
    semsClient:    $sems
);

A complete, runnable example, including signature options (SignOptions: restricting the signing mode, requesting a file in return) and callback parameters (contextual keys), can be found in example_pki_with_signature_options.php at the root of phpSDK/.

Retrieving a message

// The link the recipient received
$link = 'https://www.secure-exchanges.com/message.aspx?msgid=xxx&sems=yyy&cpart=zzz';
$msgParams = MessageHelper::getSecureExchangesMessageFromLink($link);

// Decrypt the message
$response = MessageHelper::getMessage(
    apiClient:   $client,
    serial:      $serial,
    apiUser:     $apiUser,
    apiPassword: $apiPassword,
    msgParams:   $msgParams,
    password:    null,        // password if the message is protected
    digitCode:   null,        // SMS code if 2FA is enabled
    semsClient:  $sems
);

if ($response->status === 200) {
    echo "Subject: {$response->subject}\n";
    echo "Message: {$response->message}\n";
    // $response->filesMetaData, $response->remainingOpenTime, $response->expireOn, ...
}

Note: each read consumes one of the message's allowed opens (maximumOpenTime).

Deleting a message

$success = MessageHelper::deleteMessageByTrackingId(
    apiClient:   $client,
    serial:      $serial,
    apiUser:     $apiUser,
    apiPassword: $apiPassword,
    trackingId:  'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
    cultureId:   'en-US'
);

Reply envelopes

Envelopes let a recipient send documents to you securely:

// Simple envelope (single recipient)
$envelope = MessageHelper::getEnveloppe(
    apiClient:            $client,
    serial:               $serial,
    apiUser:              $apiUser,
    apiPassword:          $apiPassword,
    subject:              'Send me your documents',
    destination:          'recipient@example.com',
    cultureId:            'en-US',
    replyExpirationHours: 48,
    maximumReplyOpenTime: 3,
    replyToApi:           true,
    authorizedExtensions: ['.pdf', '.docx']
);

if ($envelope->url) {
    echo "Envelope URL: {$envelope->url}";
}

// Multiple envelopes (one per recipient; a phone number on the
// recipient automatically enables 2FA)
$envelopes = MessageHelper::getEnvelopes(
    apiClient:   $client,
    serial:      $serial,
    apiUser:     $apiUser,
    apiPassword: $apiPassword,
    subject:     'Required documents',
    recipients:  [
        ['Email' => 'user1@example.com'],
        ['Email' => 'user2@example.com', 'Phone' => '+15145551234'],
    ]
);

API reference

MessageHelper

Static class for sending and receiving messages (src/Helpers/MessageHelper.php).

Method Description
multiRecipientMessage(...) Sends a message to one or more recipients
getMessage($apiClient, $serial, $apiUser, $apiPassword, $msgParams, $password = null, $digitCode = null, $semsClient = null) Retrieves and decrypts a message
getSecureExchangesMessageFromLink($link) Parses a Secure Exchanges link into parameters for getMessage
deleteMessageByTrackingId($apiClient, $serial, $apiUser, $apiPassword, $trackingId, $cultureId = 'en-US') Deletes a message
getEnveloppe(...) Creates a reply envelope (single recipient)
getEnvelopes(...) Creates multiple envelopes
sendTrace($apiClient, $traceContent) Sends a support trace

multiRecipientMessage parameters

Parameter Type Default Description
apiClient SecureExchangesClient required API client
serial string required Serial number (UUID)
apiUser string required API user ID (UUID)
apiPassword string required API password (UUID)
recipients RecipientInfo[] required Recipients
message string required Message content (HTML accepted)
subject string required Subject
password ?string null Protection password
filesList ?array null In-memory files (['binary' => ..., 'file_name' => ...])
filesPath ?array null Paths of files on disk
sendMethod SendMethodEnum ONLY_EMAIL Delivery channel (see table above)
getBackHtml bool true true = you send the email yourself, false = Secure Exchanges sends it
showSubject bool true Show the subject in the notification
getNotify bool true Notify when the message is opened
cultureId string 'en-US' Language ('fr-CA', 'en-US', …)
maximumOpenTime int 1 Maximum number of opens (1-99)
minutesExpiration int 10080 Expiration in minutes (default: 7 days)
createMessageCopy bool false Copy for the sender
name string 'Secure Exchanges' Sender name
callbackParameters ?string null Callback parameters (JSON)
replyOptions ?array null Recipient reply options (see below)
ownerDontNeedToSign bool true The sender does not sign
semsClient ?SEMSClient null SEMS client (created automatically if omitted)
certifiedPdfs ?array null PKI-certified PDFs (CertifiedPdfContainer)
signOptions ?SignOptions null Signature options (restriction, return upload)

replyOptions is an array (PascalCase keys, matching the API) that controls what the recipient can do in reply:

use SecureExchanges\SDK\Models\Enum\ReplyFileUploadMode;

$replyOptions = [
    'UploadMode' => ReplyFileUploadMode::UPLOAD_MANDATORY->value,  // NO_UPLOAD, UPLOAD_OPTIONAL, UPLOAD_MANDATORY
    'AuthorizedExtensions' => ['.jpg', '.png', '.pdf'],
    'Messages' => [
        ['CultureID' => 'fr-CA', 'Message' => 'Veuillez joindre votre document.'],
        ['CultureID' => 'en-CA', 'Message' => 'Please attach your document.'],
    ],
    'NoEditor' => false,   // disable the reply text editor
    'NoReply'  => false,   // disallow any reply
];

Returns: MultiRecipientAnswer (status, data, recipientsAnswer[], each element containing a SendMessageAnswer with url, trackingId, htmlMsg, …).

SignHelper

Signature zone and certification management (src/Helpers/SignHelper.php).

Method Description
getCertificationToken($apiClient, $serial, $apiUser, $apiPassword, $originalHash, $cultureId, $tokenType = null, $metadata = null) Obtains a PKI certification token
addFileZoneDefToRecipientIndex(...) Converts templates into zone definitions
convertRecipientIndexToList($recipientIndex) Converts the index into a list for multiRecipientMessage
concatRecipientZoneDefinitionLists($list1, $list2) Merges two zone definition lists
validateAllRecipientsHaveZones(...) Checks that all recipients have zones
validateDocumentSignatureIntegrity(...) Validates the integrity of a signed document
applyPKI($manifest, $certificationData) Applies the certification data to a manifest

PkiFileHelper

PKI certification of PDFs via headless browser (src/Helpers/PkiFileHelper.php, based on chrome-php).

$helper = new PkiFileHelper(isPreview: false, culture: 'en-US');
$certified = $helper->certifyPkiPdf($args);   // CertifyPdfArgs -> CertifiedPdfContainer
$helper->close();

The Chrome/Chromium binary is detected automatically, or forced via the SE_CHROME_PATH environment variable.

CertifyPdfArgs: file (FileArgs: binary + name), recipients (emails), certifyData (certification token), detectExistingFields (default true), recipientZoneDefinitions, defineRecipients (optional callback).

CertifiedPdfContainer (return value): fileBytes (certified PDF), fileName, signFilesRequired (including the new sha512), recipientZoneDefinitions (zones updated with the new hash).

ZoneBuilder

Builds signature zones from a PDF (src/Helpers/ZoneBuilder.php).

$builder = new ZoneBuilder($filePath, isPki: true);
$zone = $builder->createFieldSignZone(page: 1, fieldName: 'sig1', x: 50, y: 600, width: 200, height: 80);
$builder->close();

Zone types: signature, initials, text, number, date, signing date, checkbox, radio buttons, text area, option list, dropdown list. Utilities: getPageCount(), getPageDimensions($page).

SettingsHelper

SDK configuration (src/Helpers/SettingsHelper.php).

Method Description
configureProduction() / configurePreview() Selects the environment and returns its endpoints
getEnvironment() Name of the configured environment
getVerifySsl() Whether TLS certificates are verified
setCustomFileHandler($url) / setCustomFileUploadHandler($url) / setCustomSemsEndpoint($url) Override an individual endpoint
getSemsEndpoint() / getFileUploadHandler() / getFileDownloadHandler() Effective endpoints
reset() Resets to default values

Other helpers

Class Purpose
LogsHelper getLog(...) (by messageId or trackingId), getLogs($apiClient, $serial, $apiUser, $apiPassword, $fromDate, $toDate, $userLogSerial = null)
LicenceHelper getSerialInfo(...), isLicenceValid(...) : license information and validity
CryptoHelper Primitives: getSha512HashOfBytes, getSha512OfFile, AES (encryptBinary/decryptBinaryFromBytes, …), RSA, .NET GUID (guidToBytesLe)
EmailHelper / PhoneHelper Validation of email addresses and phone numbers (E.164, SMS capability)

Data models

// Recipient: a phone number enables the SMS/2FA features
new RecipientInfo(email: 'user@example.com', phone: '+15145551234');

GetMessageResponse (reading): status, subject, message, filesMetaData, remainingOpenTime, expireOn, createdDate, callbackParameters, containsSignsFiles, …

GetEnveloppeResponse (envelope): url, statusCode, repId, recipient.

Support

For any question or issue, contact supportdev@secure-exchanges.info or visit secure-exchanges.com. Product documentation: help.secure-exchanges.com.

License

Licensed under the Apache License, Version 2.0. Full text at https://www.apache.org/licenses/LICENSE-2.0. The copyright notice is in the NOTICE file.

Using the SDK also requires a Secure Exchanges licence (serial, API user and API password) obtained from your administration portal. The Apache licence covers this source code, not access to the service.

Secure Exchanges PHP SDK (version française)

English | Français

SDK PHP pour l'API Secure Exchanges : messagerie chiffrée de bout en bout, transfert sécurisé de fichiers, signature électronique et certification PKI de documents PDF.

Avec ce SDK vous pouvez, depuis votre application PHP :

  • envoyer des messages et des fichiers chiffrés de bout en bout, avec ou sans mot de passe, avec vérification 2FA par SMS ;
  • faire signer électroniquement des documents PDF (zones de signature, initiales, dates, champs texte…) ;
  • certifier des PDF avec un certificat numérique (PKI) avant l'envoi pour signature ;
  • lire et supprimer des messages, créer des enveloppes de réponse sécurisées ;
  • consulter les journaux d'activité (envois, ouvertures, signatures).

Table des matières

  1. Sécurité
  2. Installation
  3. Prérequis
  4. Identifiants
  5. Configuration
  6. Architecture du SDK
  7. Guide d'utilisation
  8. Référence API
  9. Support
  10. Licence

Sécurité

  • Chiffrement de bout en bout : le contenu (corps du message, fichiers) est chiffré dans votre application avant de quitter votre serveur, avec des clés générées localement et jamais transmises au serveur Secure Exchanges : celui-ci ne voit passer que des données chiffrées.
  • Échange de clés post-quantique : ML-KEM-1024 (FIPS 203), résistant aux attaques par ordinateur quantique, signé en ML-DSA-87 (FIPS 204).
  • Chiffrement AES-256 du contenu et des échanges avec le serveur.
  • Protection anti-interception : toute tentative d'attaque de type homme du milieu (MITM) fait échouer la connexion.
  • Rien à gérer : le SDK applique automatiquement l'intégralité du protocole à chaque appel : vous n'avez aucune primitive cryptographique à manipuler.

Installation

composer require secure-exchanges/sdk

Dépendances

Les dépendances sont gérées par Composer (voir composer.json) :

  • paragonie/pqcrypto_compat (0.3.2) : cryptographie post-quantique (ML-KEM / ML-DSA)
  • phpseclib/phpseclib : cryptographie (RSA pour certaines fonctions annexes, utilitaires)
  • setasign/fpdi, setasign/fpdf : manipulation de PDF
  • smalot/pdfparser : lecture de PDF
  • chrome-php/chrome : certification PKI (navigateur headless)
  • giggsey/libphonenumber-for-php : validation des numéros de téléphone

Prérequis

Pour utiliser le SDK, vous devez disposer de :

  • PHP 8.2 ou plus récent (composer install échoue sous PHP 8.1)
  • Extensions PHP : openssl, json, mbstring, curl
  • ext-sockets : requise pour la signature PKI (utilisée par chrome-php)
  • Google Chrome ou Chromium installé sur la machine, requis uniquement pour la certification PDF (PKI) ; le chemin du binaire peut être fourni via la variable d'environnement SE_CHROME_PATH
  • Un numéro de série (serial) : UUID de votre licence
  • Un identifiant API (api_user) : UUID de l'utilisateur API
  • Un mot de passe API (api_password) : UUID du mot de passe API

Recommandé en production : l'extension native ext-pqcrypto (github.com/paragonie/ext-pqcrypto), nettement plus rapide pour la cryptographie post-quantique que l'implémentation PHP pur installée par Composer. Aucun changement de code n'est nécessaire : le SDK bascule automatiquement sur l'extension dès qu'elle est chargée. À défaut, activer ext-opcache (avec JIT) accélère sensiblement l'implémentation PHP pur.

Identifiants

Où trouver vos identifiants

Les 3 identifiants nécessaires (serial, api_user, api_password) sont des UUID (GUIDs) que vous obtenez dans votre portail d'administration Secure Exchanges lors de la création de votre licence API.

Identifiant Description
serial Numéro de série de votre licence
api_user Identifiant de l'utilisateur API
api_password Mot de passe de l'utilisateur API

Configuration

Sélection de l'environnement

SettingsHelper sélectionne l'environnement et retourne ses endpoints, rien d'autre. Le SDK ne stocke aucun identifiant et ne va jamais les chercher dans l'environnement du processus : le serial, l'utilisateur et le mot de passe API vous appartiennent, et sont passés explicitement à chaque appel.

use SecureExchanges\SDK\Client\SecureExchangesClient;
use SecureExchanges\SDK\Client\SEMSClient;
use SecureExchanges\SDK\Helpers\SettingsHelper;

// ['apiEndpoint' => ..., 'semsEndpoint' => ..., 'environment' => 'production']
$endpoints = SettingsHelper::configureProduction();

$client = new SecureExchangesClient($endpoints['apiEndpoint']);
$sems   = new SEMSClient($endpoints['semsEndpoint']);

// chargerMesIdentifiants() est votre propre fonction : elle retourne les trois UUID
// de votre licence. Voir « Protéger vos identifiants » ci-dessous pour savoir
// où les conserver.
[$serial, $apiUser, $apiPassword] = chargerMesIdentifiants();
Méthode Environnement Endpoints
configureProduction() production www.secure-exchanges.com
configurePreview() preview (test) preview.secure-exchanges.com

Vos identifiants preview et production sont différents : un serial de production ne fonctionnera pas en preview, et inversement.

Les endpoints individuels restent surchargeables ensuite avec setCustomFileHandler(), setCustomFileUploadHandler() et setCustomSemsEndpoint().

Protéger vos identifiants

Ces trois UUID valent un mot de passe sur votre compte Secure Exchanges : quiconque les détient peut envoyer, lire et supprimer des messages en votre nom, et la consommation vous est facturée. Leur stockage est entièrement votre décision, et votre responsabilité.

  • Ne les committez jamais. Gardez-les hors du dépôt, et vérifiez aussi l'historique : git log -S <serial> retrouve un secret committé une fois puis retiré. Un secret parti sur un remote est compromis : il faut le régénérer, pas seulement supprimer la ligne.
  • Ne les écrivez jamais en dur dans le code. Ils se retrouvent dans les traces d'exception, dans les rapports de bogue, dans les paquets que vous distribuez et dans chaque fork du code.
  • Privilégiez un gestionnaire de secrets. Chiffrés au repos, accès journalisé, rotation possible sans redéploiement.
  • Si vous les gardez dans un fichier, chiffrez-le ou verrouillez-le. Hors de la racine web, appartenant à l'utilisateur PHP-FPM, permissions 0400, et exclu de vos sauvegardes ou chiffré à l'intérieur.
  • Ne les mettez jamais dans le env[] d'un pool FPM. Ils deviennent alors visibles dans phpinfo(), dans $_ENV et dans la plupart des vidages d'erreur, pour toute la durée de vie de chaque worker. Idem pour putenv() : une valeur posée ainsi apparaît quand même dans phpinfo().
  • Tenez-les hors de vos journaux. Excluez-les explicitement de votre gestionnaire d'erreurs, de votre rapporteur d'incidents et des vidages de requête, qui capturent l'environnement et les superglobales par défaut. Ne faites jamais un var_dump() d'un tableau de configuration en production.
  • Régénérez-les depuis le portail d'administration dès que vous soupçonnez une exposition : un portable perdu, un journal transmis à un tiers, le départ d'un développeur.

Architecture du SDK

Le SDK est organisé en trois grands modules (namespace SecureExchanges\SDK, PSR-4 depuis src/) :

  • Client/ contient la couche HTTP : SecureExchangesClient (API principale), SEMSClient (service d'envoi d'emails), tous deux bâtis sur BaseClient.
  • Helpers/ contient la logique métier : MessageHelper (envoi/lecture/suppression), HandShakeHelper (handshake post-quantique), CryptoHelper (primitives AES/RSA/SHA), SignHelper et ZoneBuilder (zones de signature), PkiFileHelper (certification PDF), FileHelper (upload/download chiffrés), SettingsHelper (configuration), plus des helpers de validation (EmailHelper, PhoneHelper), de logs (LogsHelper) et de licence (LicenceHelper).
  • Models/ contient les objets typés : réponses (Answer/), entités (Entity/), énumérations (Enum/), objets de transport (Transport/), modèles JSON (JsonModels/), arguments (Args/). Le dossier Callback/ contient les objets de notification (callbacks).

Guide d'utilisation

Tous les exemples ci-dessous supposent la configuration suivante :

use SecureExchanges\SDK\Client\SecureExchangesClient;
use SecureExchanges\SDK\Client\SEMSClient;
use SecureExchanges\SDK\Helpers\SettingsHelper;
use SecureExchanges\SDK\Helpers\MessageHelper;
use SecureExchanges\SDK\Models\Entity\RecipientInfo;

$endpoints = SettingsHelper::configureProduction();

$client = new SecureExchangesClient($endpoints['apiEndpoint']);
$sems   = new SEMSClient($endpoints['semsEndpoint']);

// Votre propre fonction : elle retourne les trois UUID de votre licence.
[$serial, $apiUser, $apiPassword] = chargerMesIdentifiants();

Envoi d'un message simple

$answer = MessageHelper::multiRecipientMessage(
    apiClient:   $client,
    serial:      $serial,
    apiUser:     $apiUser,
    apiPassword: $apiPassword,
    recipients:  [new RecipientInfo(email: 'destinataire@example.com')],
    message:     'Bonjour, voici votre message sécurisé.',
    subject:     'Message confidentiel',
    cultureId:   'fr-CA',
    semsClient:  $sems
);

if ($answer->status === 200 && $answer->recipientsAnswer) {
    echo 'URL : ' . $answer->recipientsAnswer[0]->answer->url;
}

Envoi avec mot de passe et 2FA par SMS

Le mot de passe et le canal d'envoi sont deux options indépendantes et combinables. Le canal est contrôlé par l'énumération SendMethodEnum :

Valeur Comportement
SendMethodEnum::ONLY_EMAIL (défaut) Lien envoyé par email
SendMethodEnum::SMS_ONLY Lien envoyé par SMS uniquement
SendMethodEnum::SMS_WITH_EMAIL_CODE Lien par SMS, code d'ouverture par email
SendMethodEnum::EMAIL_WITH_SMS_CODE Lien par email, code d'ouverture par SMS (2FA)
use SecureExchanges\SDK\Models\Enum\SendMethodEnum;

$answer = MessageHelper::multiRecipientMessage(
    apiClient:   $client,
    serial:      $serial,
    apiUser:     $apiUser,
    apiPassword: $apiPassword,
    recipients:  [new RecipientInfo(email: 'user@example.com', phone: '+15145551234')],
    message:     'Document confidentiel ci-joint.',
    subject:     'Document protégé',
    password:    'MotDePasseSecret123',
    sendMethod:  SendMethodEnum::EMAIL_WITH_SMS_CODE,
    cultureId:   'fr-CA',
    maximumOpenTime:   3,     // nombre maximum d'ouvertures
    minutesExpiration: 1440,  // expiration en minutes (1440 = 24 h)
    semsClient:  $sems
);

Envoi avec fichiers joints

// Depuis le disque
$answer = MessageHelper::multiRecipientMessage(
    apiClient:   $client,
    serial:      $serial,
    apiUser:     $apiUser,
    apiPassword: $apiPassword,
    recipients:  [new RecipientInfo(email: 'destinataire@example.com')],
    message:     'Voir fichiers joints.',
    subject:     'Fichiers sécurisés',
    filesPath:   ['/chemin/vers/document.pdf', '/chemin/vers/image.png'],
    semsClient:  $sems
);

// Depuis la mémoire
$pdfBytes = file_get_contents('/chemin/vers/rapport.pdf');
$csvBytes = file_get_contents('/chemin/vers/donnees.csv');

$answer = MessageHelper::multiRecipientMessage(
    apiClient:   $client,
    serial:      $serial,
    apiUser:     $apiUser,
    apiPassword: $apiPassword,
    recipients:  [new RecipientInfo(email: 'destinataire@example.com')],
    message:     'Rapport et données en pièces jointes.',
    subject:     'Rapport mensuel',
    filesList:   [
        ['binary' => $pdfBytes, 'file_name' => 'rapport.pdf'],
        ['binary' => $csvBytes, 'file_name' => 'donnees.csv'],
    ],
    semsClient:  $sems
);

Les deux paramètres (filesPath et filesList) peuvent être combinés dans un même envoi.

Envoi avec certification PKI

La certification PKI appose un certificat numérique sur le PDF avant l'envoi pour signature. Elle nécessite Chrome/Chromium (navigateur headless via chrome-php) et l'extension ext-sockets. Les zones de signature se définissent avec ZoneBuilder, qui lit les dimensions réelles des pages du PDF. Le flux :

use SecureExchanges\SDK\Helpers\CryptoHelper;
use SecureExchanges\SDK\Helpers\ZoneBuilder;
use SecureExchanges\SDK\Helpers\SignHelper;
use SecureExchanges\SDK\Helpers\PkiFileHelper;
use SecureExchanges\SDK\Models\Args\CertifyPdfArgs;
use SecureExchanges\SDK\Models\Args\FileArgs;
use SecureExchanges\SDK\Models\Entity\SignZoneDefinition;
use SecureExchanges\SDK\Models\Transport\FileZoneDefinition;
use SecureExchanges\SDK\Models\Transport\RecipientZoneDefinition;

// 1. Lire le PDF et calculer son SHA512
$pdfPath  = '/chemin/vers/contrat.pdf';
$pdfBytes = file_get_contents($pdfPath);
$sha512   = CryptoHelper::getSha512HashOfBytes($pdfBytes);

// 2. Définir les zones de signature avec ZoneBuilder
$zoneBuilder = new ZoneBuilder($pdfPath, isPki: true);
$zones = [
    // Zone de signature en page 1
    $zoneBuilder->createFieldSignZone(
        page: 1, fieldName: 'signature1',
        x: 60, y: 400, width: 200, height: 50
    ),
    // Zone de date remplie automatiquement à la signature
    $zoneBuilder->createFieldDateSignZone(
        page: 1, fieldName: 'date1',
        x: 60, y: 500, width: 100, height: 25, fontSize: 12
    ),
];
$zoneBuilder->close();

// 3. Associer les zones au destinataire (fichier référencé par son SHA512)
$recipientZones = [
    new RecipientZoneDefinition(
        email: 'signataire@example.com',
        phone: null,
        zonesDefByFile: [
            new FileZoneDefinition(
                uniqueName: $sha512,
                zonesDef: new SignZoneDefinition(mode: 'define', zones: $zones),
                doNotEncryptPDF: false,
                doNotAppendCertificateToFile: false,
                isPKIFile: true
            )
        ]
    )
];

// 4. Obtenir le token de certification
$tokenResponse = SignHelper::getCertificationToken(
    apiClient:    $client,
    serial:       $serial,
    apiUser:      $apiUser,
    apiPassword:  $apiPassword,
    originalHash: $sha512,
    cultureId:    'fr-CA',
    tokenType:    'CertifyDocument'
);
$certData = $tokenResponse['certification_data'];

// 5. Certifier le PDF (navigateur headless)
$certifyArgs = new CertifyPdfArgs(
    file:                     new FileArgs($pdfBytes, 'contrat.pdf'),
    recipients:               ['signataire@example.com'],
    certifyData:              $certData,
    detectExistingFields:     true,
    recipientZoneDefinitions: $recipientZones
);

$pkiHelper    = new PkiFileHelper(isPreview: false, culture: 'fr-CA');
$certifiedPdf = $pkiHelper->certifyPkiPdf($certifyArgs);
$pkiHelper->close();

// 6. Envoyer le PDF certifié
$answer = MessageHelper::multiRecipientMessage(
    apiClient:     $client,
    serial:        $serial,
    apiUser:       $apiUser,
    apiPassword:   $apiPassword,
    recipients:    [new RecipientInfo(email: 'signataire@example.com')],
    message:       'Veuillez signer ce document certifié.',
    subject:       'Document PKI à signer',
    cultureId:     'fr-CA',
    certifiedPdfs: [$certifiedPdf],
    semsClient:    $sems
);

Un exemple complet et exécutable, incluant les options de signature (SignOptions : restriction du mode de signature, demande de fichier en retour) et les paramètres de callback (clés contextuelles), se trouve dans example_pki_with_signature_options.php à la racine de phpSDK/.

Récupération d'un message

// Le lien reçu par le destinataire
$link = 'https://www.secure-exchanges.com/message.aspx?msgid=xxx&sems=yyy&cpart=zzz';
$msgParams = MessageHelper::getSecureExchangesMessageFromLink($link);

// Déchiffrer le message
$response = MessageHelper::getMessage(
    apiClient:   $client,
    serial:      $serial,
    apiUser:     $apiUser,
    apiPassword: $apiPassword,
    msgParams:   $msgParams,
    password:    null,        // mot de passe si le message est protégé
    digitCode:   null,        // code SMS si 2FA
    semsClient:  $sems
);

if ($response->status === 200) {
    echo "Sujet  : {$response->subject}\n";
    echo "Message: {$response->message}\n";
    // $response->filesMetaData, $response->remainingOpenTime, $response->expireOn, ...
}

Note : chaque lecture consomme une ouverture du message (maximumOpenTime).

Suppression d'un message

$success = MessageHelper::deleteMessageByTrackingId(
    apiClient:   $client,
    serial:      $serial,
    apiUser:     $apiUser,
    apiPassword: $apiPassword,
    trackingId:  'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
    cultureId:   'fr-CA'
);

Enveloppes de réponse

Les enveloppes permettent à un destinataire de vous envoyer des documents de manière sécurisée :

// Enveloppe simple (un destinataire)
$envelope = MessageHelper::getEnveloppe(
    apiClient:            $client,
    serial:               $serial,
    apiUser:              $apiUser,
    apiPassword:          $apiPassword,
    subject:              'Envoyez-moi vos documents',
    destination:          'destinataire@example.com',
    cultureId:            'fr-CA',
    replyExpirationHours: 48,
    maximumReplyOpenTime: 3,
    replyToApi:           true,
    authorizedExtensions: ['.pdf', '.docx']
);

if ($envelope->url) {
    echo "URL de l'enveloppe : {$envelope->url}";
}

// Enveloppes multiples (une par destinataire ; un numéro de téléphone
// dans le destinataire active automatiquement le 2FA)
$envelopes = MessageHelper::getEnvelopes(
    apiClient:   $client,
    serial:      $serial,
    apiUser:     $apiUser,
    apiPassword: $apiPassword,
    subject:     'Documents requis',
    recipients:  [
        ['Email' => 'user1@example.com'],
        ['Email' => 'user2@example.com', 'Phone' => '+15145551234'],
    ]
);

Référence API

MessageHelper

Classe statique pour l'envoi et la réception de messages (src/Helpers/MessageHelper.php).

Méthode Description
multiRecipientMessage(...) Envoie un message à un ou plusieurs destinataires
getMessage($apiClient, $serial, $apiUser, $apiPassword, $msgParams, $password = null, $digitCode = null, $semsClient = null) Récupère et déchiffre un message
getSecureExchangesMessageFromLink($link) Parse un lien Secure Exchanges en paramètres pour getMessage
deleteMessageByTrackingId($apiClient, $serial, $apiUser, $apiPassword, $trackingId, $cultureId = 'en-US') Supprime un message
getEnveloppe(...) Crée une enveloppe de réponse (un destinataire)
getEnvelopes(...) Crée des enveloppes multiples
sendTrace($apiClient, $traceContent) Envoie une trace de support

Paramètres de multiRecipientMessage

Paramètre Type Défaut Description
apiClient SecureExchangesClient requis Client API
serial string requis Numéro de série (UUID)
apiUser string requis Identifiant API (UUID)
apiPassword string requis Mot de passe API (UUID)
recipients RecipientInfo[] requis Destinataires
message string requis Contenu du message (HTML accepté)
subject string requis Sujet
password ?string null Mot de passe de protection
filesList ?array null Fichiers en mémoire (['binary' => ..., 'file_name' => ...])
filesPath ?array null Chemins de fichiers sur le disque
sendMethod SendMethodEnum ONLY_EMAIL Canal d'envoi (voir tableau plus haut)
getBackHtml bool true true = vous envoyez l'email vous-même, false = Secure Exchanges l'envoie
showSubject bool true Afficher le sujet dans la notification
getNotify bool true Notifier à l'ouverture
cultureId string 'en-US' Langue ('fr-CA', 'en-US', …)
maximumOpenTime int 1 Nombre maximum d'ouvertures (1-99)
minutesExpiration int 10080 Expiration en minutes (défaut : 7 jours)
createMessageCopy bool false Copie pour l'expéditeur
name string 'Secure Exchanges' Nom de l'expéditeur
callbackParameters ?string null Paramètres de callback (JSON)
replyOptions ?array null Options de réponse du destinataire (voir plus bas)
ownerDontNeedToSign bool true L'expéditeur ne signe pas
semsClient ?SEMSClient null Client SEMS (créé automatiquement si omis)
certifiedPdfs ?array null PDF certifiés PKI (CertifiedPdfContainer)
signOptions ?SignOptions null Options de signature (restriction, upload en retour)

replyOptions est un tableau (clés PascalCase, comme l'API) qui contrôle ce que le destinataire peut faire en réponse :

use SecureExchanges\SDK\Models\Enum\ReplyFileUploadMode;

$replyOptions = [
    'UploadMode' => ReplyFileUploadMode::UPLOAD_MANDATORY->value,  // NO_UPLOAD, UPLOAD_OPTIONAL, UPLOAD_MANDATORY
    'AuthorizedExtensions' => ['.jpg', '.png', '.pdf'],
    'Messages' => [
        ['CultureID' => 'fr-CA', 'Message' => 'Veuillez joindre votre document.'],
        ['CultureID' => 'en-CA', 'Message' => 'Please attach your document.'],
    ],
    'NoEditor' => false,   // désactiver l'éditeur de texte de réponse
    'NoReply'  => false,   // interdire toute réponse
];

Retour : MultiRecipientAnswer (status, data, recipientsAnswer[], chaque élément contenant un SendMessageAnswer avec url, trackingId, htmlMsg, …).

SignHelper

Gestion des zones de signature et de la certification (src/Helpers/SignHelper.php).

Méthode Description
getCertificationToken($apiClient, $serial, $apiUser, $apiPassword, $originalHash, $cultureId, $tokenType = null, $metadata = null) Obtient un token de certification PKI
addFileZoneDefToRecipientIndex(...) Convertit des templates en définitions de zones
convertRecipientIndexToList($recipientIndex) Convertit l'index en liste pour multiRecipientMessage
concatRecipientZoneDefinitionLists($list1, $list2) Fusionne deux listes de définitions de zones
validateAllRecipientsHaveZones(...) Vérifie que tous les destinataires ont des zones
validateDocumentSignatureIntegrity(...) Valide l'intégrité d'un document signé
applyPKI($manifest, $certificationData) Applique les données de certification à un manifeste

PkiFileHelper

Certification PKI de PDF via navigateur headless (src/Helpers/PkiFileHelper.php, basé sur chrome-php).

$helper = new PkiFileHelper(isPreview: false, culture: 'fr-CA');
$certified = $helper->certifyPkiPdf($args);   // CertifyPdfArgs -> CertifiedPdfContainer
$helper->close();

Le binaire Chrome/Chromium est détecté automatiquement, ou imposé via la variable d'environnement SE_CHROME_PATH.

CertifyPdfArgs : file (FileArgs : binaire + nom), recipients (emails), certifyData (token de certification), detectExistingFields (défaut true), recipientZoneDefinitions, defineRecipients (callback optionnel).

CertifiedPdfContainer (retour) : fileBytes (PDF certifié), fileName, signFilesRequired (dont le nouveau sha512), recipientZoneDefinitions (zones mises à jour avec le nouveau hash).

ZoneBuilder

Construction de zones de signature à partir d'un PDF (src/Helpers/ZoneBuilder.php).

$builder = new ZoneBuilder($filePath, isPki: true);
$zone = $builder->createFieldSignZone(page: 1, fieldName: 'sig1', x: 50, y: 600, width: 200, height: 80);
$builder->close();

Types de zones : signature, initiales, texte, nombre, date, date de signature, case à cocher, boutons radio, zone de texte, liste d'options, liste déroulante. Utilitaires : getPageCount(), getPageDimensions($page).

SettingsHelper

Configuration du SDK (src/Helpers/SettingsHelper.php).

Méthode Description
configureProduction() / configurePreview() Sélectionne l'environnement et retourne ses endpoints
getEnvironment() Nom de l'environnement configuré
getVerifySsl() Validation des certificats TLS
setCustomFileHandler($url) / setCustomFileUploadHandler($url) / setCustomSemsEndpoint($url) Surcharge un endpoint particulier
getSemsEndpoint() / getFileUploadHandler() / getFileDownloadHandler() Endpoints effectifs
reset() Retour aux valeurs par défaut

Autres helpers

Classe Rôle
LogsHelper getLog(...) (par messageId ou trackingId), getLogs($apiClient, $serial, $apiUser, $apiPassword, $fromDate, $toDate, $userLogSerial = null)
LicenceHelper getSerialInfo(...), isLicenceValid(...) : informations et validité de licence
CryptoHelper Primitives : getSha512HashOfBytes, getSha512OfFile, AES (encryptBinary/decryptBinaryFromBytes, …), RSA, GUID .NET (guidToBytesLe)
EmailHelper / PhoneHelper Validation d'adresses email et de numéros de téléphone (E.164, aptitude SMS)

Modèles de données

// Destinataire : un téléphone active les fonctionnalités SMS/2FA
new RecipientInfo(email: 'user@example.com', phone: '+15145551234');

GetMessageResponse (lecture) : status, subject, message, filesMetaData, remainingOpenTime, expireOn, createdDate, callbackParameters, containsSignsFiles, …

GetEnveloppeResponse (enveloppe) : url, statusCode, repId, recipient.

Support

Pour toute question ou problème, contactez supportdev@secure-exchanges.info ou visitez secure-exchanges.com. Documentation produit : help.secure-exchanges.com.

Licence

Distribué sous licence Apache, version 2.0. Texte complet sur https://www.apache.org/licenses/LICENSE-2.0. Le copyright figure dans le fichier NOTICE.

L'utilisation du SDK requiert par ailleurs une licence Secure Exchanges (serial, usager et mot de passe API) obtenue depuis votre portail d'administration. La licence Apache couvre ce code source, pas l'accès au service.