This is an old revision of the document!



Android and Hotspot 2.0/Passpoint

  • 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.

  • The way to get the Passpoint configuration file is to download it from a web server that has HTTPS working and a valid SSL certificate.
  • 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 enclosed elements.

  • 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;
    }
}

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 in then in turn combined and base64 encrypted one last time before it is served by the web server.
  • Here we combine them:
$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;
  • technical/pp-android.1751774145.txt.gz
  • Last modified: 2025/07/06 05:55
  • by system