ES9+ — profile download
Three calls, then a notification. The LPA drives all four; the eUICC signs the parts only it can sign; the SM-DP+ never talks to the card directly.
Source: diagrams/smdp-plus/es9plus-download.puml. Paths and
status codes from Es9PlusController.java; field names from
Es9Requests.java and Es9Responses.java.
The four calls
All four are POST, all take and return application/json, and none of them
carries an Authorization header — see who may call
what.
1 · initiateAuthentication
The LPA has just asked the eUICC for a challenge. It presents that challenge along with the address it thinks it is talking to; the SM-DP+ refuses if that address is not its own.
{ "euiccChallenge": "REDACTED (16 bytes, base64)", "euiccInfo1": "REDACTED (DER EUICCInfo1, base64)", "smdpAddress": "smdpplus.rsplab.click"}{ "header": { "functionExecutionStatus": { "status": "Executed-Success" } }, "transactionId": "REDACTED (16 hex characters)", "serverSigned1": "REDACTED (DER, base64)", "serverSignature1": "REDACTED (ECDSA signature, base64)", "euiccCiPKIdToBeUsed": "REDACTED (SubjectKeyIdentifier of the chosen CI)", "serverCertificate": "CERT.DPauth.ECDSA (DER, base64)"}euiccCiPKIdToBeUsed is the SM-DP+ choosing which certificate issuer the eUICC
should verify against, out of the set the eUICC advertised in euiccInfo1. Get
this wrong and the card rejects the server it was about to trust.
2 · authenticateClient
The eUICC has verified the server and signed its own half. This is the call
where the profile stops being anonymous: the SM-DP+ matches the matchingId
carried inside the eUICC’s response to a released order, and binds that specific
profile to that specific EID.
{ "transactionId": "REDACTED (16 hex characters)", "authenticateServerResponse": "REDACTED (DER, base64)"}{ "header": { "functionExecutionStatus": { "status": "Executed-Success" } }, "transactionId": "REDACTED (16 hex characters)", "profileMetadata": "REDACTED (DER StoreMetadataRequest, base64)", "smdpSigned2": "REDACTED (DER, base64)", "smdpSignature2": "REDACTED (ECDSA signature, base64)", "smdpCertificate": "CERT.DPpb.ECDSA (DER, base64)"}Note the certificate changes between call 1 and call 2 — CERT.DPauth proves
who the server is, CERT.DPpb proves it is entitled to bind a profile. Two
roles, two keys, deliberately not interchangeable.
3 · getBoundProfilePackage
{ "transactionId": "REDACTED (16 hex characters)", "prepareDownloadResponse": "REDACTED (DER, base64)"}{ "header": { "functionExecutionStatus": { "status": "Executed-Success" } }, "transactionId": "REDACTED (16 hex characters)", "boundProfilePackage": "REDACTED (DER BoundProfilePackage, base64)"}The Bound Profile Package is protected under SCP03t with session keys derived from the exchange above. It is bound to one eUICC: copied to another card it is inert.
4 · handleNotification
{ "pendingNotification": "REDACTED (DER PendingNotification, base64)"}HTTP/1.1 204 No Content204 with an empty body, not 200. SGP.22 section 6.3 requires it, and
Es9PlusController returns ResponseEntity.noContent().build() accordingly. An
LPA that insists on a JSON body here will treat a correct server as broken.
Two more, not shown above
cancelSession (transactionId, cancelSessionResponse) returns 200 with a
plain success header. handleDeviceChange (transactionId,
deviceChangeRequest, SGP.22 5.6.6) lives on a separate controller and moves a
profile to a new device.