Show pageOld revisionsBacklinksBack to top This page is read only. You can view the source, but not change it. Ask your administrator if you think this is wrong. <nav type="pills" justified="false"> * [[:user_manuals|Back to Documentation]] * [[:technical:pp-android|Android and Hotspot 2.0/Passpoint]] </nav> ----- ====== Android and Hotspot 2.0/Passpoint ====== ===== Introduction ===== * This page will discuss the provisioning of Passpoint credentials to Android devices. * Information from the following URL was used as a reference: https://source.android.com/docs/core/connect/wifi-passpoint * 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. <WRAP center round info 100%> * 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**. </WRAP> -------- ===== Simple CakePHP Controller ===== * The following file demonstrates how we can use PHP to serve the Passpoint configuration file. <file php 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>RADIUSdesk-HS2.0 07-Jul-25</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; } } </file> ==== 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. <code php> $response = $response->withHeader('Content-Transfer-Encoding', 'base64'); $response = $response->withType('application/x-wifi-config'); </code> * 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. <code php> protected $ca = <<<EOD -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- EOD; </code> * It will be also base64 encoded before it is combined with the other elements <code php> $ca_64 = base64_encode($this->ca); </code> === Embedded MgmtTree XML bloc === * The MgmtTree XML bloc is defined here: <code php> $home_sp = <<<EOD <MgmtTree xmlns="syncml:dmddf1.2"> <VerDTD>1.2</VerDTD> <Node> ..... </Node> </MgmtTree> EOD; </code> * It will be also base64 encoded before it is combined with the other elements <code php> $home_sp_64 = base64_encode($home_sp); </code> === 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: <code php> $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; </code> * Here we serve it: <code php> $response = $response->withStringBody(base64_encode($home_sp_ca)); return $response; </code> * 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**. * There is also an optional third part called **Extension** which we also cover later. * 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. * There is one item however that "bleeds" from the **HomeSP** section to the **Credential** section and it can cause the authentication to fail. We will also cover this in detail on this page and what to do to fix things. ------------ ==== 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. <color #ff7f27>(This is the part that you have to be careful with)</color> * 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 === * This Realm is used in EAP authentication and is RADIUS related. * This realm has nothing to do with the NAI Realm (or Domain) in Hotspot 2.0. (HomeSP Section) * It might be the same value of the NAI Realm but it is not a requirement. * When the authentication request to RADIUS starts, an anonymous identity is used. * This is also referred to as the **Outer Identity**. * The convention Android uses is to formulate a username anonymouns@<value of realm>. * In this case it will be anonymous@mesh-manager.com. * The EAP protocol uses this recommended convention in order to determine the destination of RADIUS proxy requests. === Password === * The value of the password is also base64 encoded. * <color #00a2e8>Remember that encoded does not equal to encrypted.</color> * It is thus very easy to get the cleartext value of the password: <code bash> echo "dGVzdGluZzEyMw==" | base64 -d </code> * The get the base64 value of the password in turn you can use echo **with the -n switch**. <code bash> echo -n "testing123" | base64 </code> === 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. === Other Credential Options === * There are other credential options which is good to take note of although they beyond the scope of this discussion. * **SIM Authentication**. This will typically require collaboration with a mobile company to validate the incoming RADIUS request against their database for authentication. * **DigitalCertificate Authentication**. This require PKI infrastructure and the management of the client certificates. ------------- ===== Extension - Certificate FQDN Check (Domain Suffix Match) ===== * In WPA Supplicant we have the following option: domain_suffix_match * If this is specified then wpa_supplicant will make sure that when the client authenticates to RADIUS that the domain name of the certificate used with EAP matches one of the specified values. * If not it will reject the authentication. * This is to protect against an Evil Twin scenario. * With the Android Hotspot 2.0 setup it will take the value of FQDN under the HomeSP section as the value value for domain_suffix_match. * This is not always the case in real life. Sometimes the certificate RADIUS used has another domain/FQDN. * If you want to specify a different domain there is an **Extension** section. <code xml> <Node> <NodeName>Extension</NodeName> <Node> <NodeName>Android</NodeName> <Node> <NodeName>AAAServerTrustedNames</NodeName> <Node> <NodeName>FQDN</NodeName> <Value>radiusdesk.com;openwrt.org</Value> </Node> </Node> </Node> </Node> </code> * This section has to be on the same level as **HomeSP** and **Credential**. technical/pp-android.txt Last modified: 2025/07/07 20:17by system