This is an old revision of the document!
Android and Hotspot 2.0/Passpoint
Introduction
- This page will discuss the provisioning of Passpoint credentials to Android devices.
- The information on this page https://source.android.com/docs/core/connect/wifi-passpoint was used as a reference.
- Our approach, however will be a bit more hands on by looking at a PHP script that is used to provision a Passpoint profile.
Provision a Passpoint configuration file
- A Passpoint configuration file has to be provisioned through a web server that has a valid SSL certificate over HTTPS.
- If you look at the contents of the Passpoint configuration file it probably does not make much sense.
- It will be one long line of characters.
- This is because the file has to be served as base64 encoded content.
- Base64 encoding is a binary-to-text encoding scheme that represents binary data in an ASCII string format.
- It's primarily used to transmit binary data over channels that are designed to handle text-based data, such as email or HTTP.
- Essentially, it converts binary data into a string of printable characters (A-Z, a-z, 0-9, +, /) that are safe to transmit
- This is the general definition of base64 encoding.
- In our case, however, we are NOT transmitting binary data but rather other base64 encoded elements.
Simple CakePHP Controller
- The following file demonstrates how we can use PHP to serve the Passpoint configuration file.
- PasspointController.php
<?php namespace App\Controller; use App\Controller\AppController; use Cake\Http\Response; class PasspointController extends AppController{ protected $ca = <<<EOD -----BEGIN CERTIFICATE----- MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe 3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== -----END CERTIFICATE----- EOD; public function initialize():void{ parent::initialize(); $this->Authentication->allowUnauthenticated([ 'androidProfile']); } public function androidProfile(){ $response = $this->response; $response = $response->withHeader('Content-Transfer-Encoding', 'base64'); $response = $response->withType('application/x-wifi-config'); $home_sp = <<<EOD <MgmtTree xmlns="syncml:dmddf1.2"> <VerDTD>1.2</VerDTD> <Node> <NodeName>PerProviderSubscription</NodeName> <RTProperties> <Type> <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName> </Type> </RTProperties> <Node> <NodeName>i001</NodeName> <Node> <NodeName>HomeSP</NodeName> <Node> <NodeName>FriendlyName</NodeName> <Value>WiFi-K9.5Jun24</Value> </Node> <Node> <NodeName>FQDN</NodeName> <Value>mesh-manager.com</Value> </Node> </Node> <Node> <NodeName>Credential</NodeName> <Node> <NodeName>Realm</NodeName> <Value>mesh-manager.com</Value> </Node> <Node> <NodeName>UsernamePassword</NodeName> <Node> <NodeName>Username</NodeName> <Value>ppsk_demo@ppsk_demo</Value> </Node> <Node> <NodeName>Password</NodeName> <Value>dGVzdGluZzEyMw==</Value> </Node> <Node> <NodeName>EAPMethod</NodeName> <Node> <NodeName>EAPType</NodeName> <Value>21</Value> </Node> <Node> <NodeName>InnerMethod</NodeName> <Value>MS-CHAP-V2</Value> </Node> </Node> </Node> </Node> </Node> </Node> </MgmtTree> EOD; $home_sp_64 = base64_encode($home_sp); $ca_64 = base64_encode($this->ca); $home_sp_ca = <<<EOD MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="XXXXboundary" Content-Transfer-Encoding: base64 --XXXXboundary Content-Type: application/x-x509-ca-cert Content-Transfer-Encoding: base64 $ca_64 --XXXXboundary Content-Type: application/x-passpoint-profile Content-Transfer-Encoding: base64 $home_sp_64 --XXXXboundary-- EOD; $response = $response->withStringBody(base64_encode($home_sp_ca)); return $response; } }
Looking at the PHP File and what it does
Sets HTTP headers
- This is so that an Android device knows the content it is about to receive can potentially be a Passpoint profile.
$response = $response->withHeader('Content-Transfer-Encoding', 'base64'); $response = $response->withType('application/x-wifi-config');
- There are two items embedded.
- The one is the CA Certificate
- The other is a MgmtTree XML block which is the actual Passpoint profile definition
Embedded CA Certificate
- The CA have to be included.
protected $ca = <<<EOD -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- EOD;
- It will be also base64 encrypted before it is combined with the other elements
$ca_64 = base64_encode($this->ca);
Embedded MgmtTree XML bloc
- The MgmtTree XML bloc is defined here:
$home_sp = <<<EOD <MgmtTree xmlns="syncml:dmddf1.2"> <VerDTD>1.2</VerDTD> <Node> ..... </Node> </MgmtTree> EOD;
- It will be also base64 encrypted before it is combined with the other elements
$home_sp_64 = base64_encode($home_sp);
Combining and sending
- These two base64 encrypted strings is then in turn combined and base64 encrypted one last time before it is served by the web server.
- Here we combine it:
$home_sp_ca = <<<EOD MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="XXXXboundary" Content-Transfer-Encoding: base64 --XXXXboundary Content-Type: application/x-x509-ca-cert Content-Transfer-Encoding: base64 $ca_64 --XXXXboundary Content-Type: application/x-passpoint-profile Content-Transfer-Encoding: base64 $home_sp_64 --XXXXboundary-- EOD;
- Here we serve it:
$response = $response->withStringBody(base64_encode($home_sp_ca)); return $response;
- Before we discuss the MgmtTree XML in more detail here is a short overview of the main points:
- Content has to be served from a web server with FQDN and HTTPS.
- Content has to be served with specified header settings.
- Content has to be base64 encoded.
- Content contains.
- Base64 encoded CA Certificate.
- Base64 encoded MgmtTree XML.
- These two are combined and base64 encoded as per step3.
MgmtTree XML
- The MgmtTree XML can be divided in two parts and when yo do this it becomes much simpler to understand.
- The one part is the HomeSP.
- The other part is the Credentials.
- The HomeSP part is used by the Android to discover Hotspot 2.0 / Passpoint WiFi Access Points to connect to.
- When such an Access Point is found, the Credentials part is used to try and authenticate the user.
- The HomeSP for all practical intend replaces the step where you would typically select or specify an SSID to connect to.