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 encoded 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 encoded before it is combined with the other elements
$home_sp_64 = base64_encode($home_sp);
Combining and sending
- These two base64 encoded strings is then in turn combined and base64 eencoded 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 you do this it becomes much simpler to understand.
- The one part is the HomeSP.
- The other part is the Credential.
- 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 Credential 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.
HomeSP
- HomeSP have to contain the following nodes:
- FriendlyName Displayed by the Android installer and also when connected to the Hotspot 2.0 / Passpoint Access Point.
- FQDN This translates to the value of Domain and or NAI Realm in Hostapd / OpenWrt.
- HomeSP can contain the following node:
- RoamingConsortiumOI This is the RCOI and one can specify multiple values separated by commas.
Credential
- The credential section are mostly straight forward.
- There are however some items to highlight.
Realm
- The realm has nothing to do with the NAI Realm (or Domain) in Hotspot 2.0.
- It might be the same value of the NAI Realm but it is not a requirement.
- This has to do with Authentication security, protecting you from an Evil Twin setup.
- The value for realm has to match the domain of the CA on the RADIUS/EAP setup.
- If this does not match, the user will not be able to connect successfully.
Password
- The value of the password is also base64 encoded.
- Remember that encoded does not equal to encrypted.
- It is thus very easy to get the cleartext value of the password:
echo "dGVzdGluZzEyMw==" | base64 -d
- The get the base64 value of the password in turn you can use echo with the -n switch.
echo -n "testing123" | base64
EAPType
- EAPType will in most cases be 21.
- This is the number that is assigned for EAP/TTLS and part of the UsernamePassword detail.
- With this you also have to specify the InnerMethod to use when you are authenticating and can be one of the following.
- PAP
- CHAP
- MS-CHAP
- or MS-CHAP-V2
- The popular ones are MS-CHAP-V2 and PAP.
- When the RADIUS server uses Active Directory, it is the best to choose MS-CHAP-V2 in order to avoid compatibility issues.