Creating an NDEF WiFi record using application/vnd.wfa.wsc in Android

こ雲淡風輕ζ 提交于 2019-11-30 17:39:27

The answer lies in the Wi-Fi Alliance "Wi-Fi Simple Configuration Technical Specification v2.0.5" (available for download here). Android makes use of this standard format for configuring WiFi networks, I wrongly assumed it was proprietary.

Firstly, I created an NFC helper class (aptly named NFCHelper.java) which has all the byte constants needed to construct the record. Then, I created a hacky method for creating one of the two records required. The spec is actually fairly useless here, what I did was examined a number of payloads of tags that had been successfully configured via the Android OS. Finally, you need to have a mechanism to prepend a "Handover Select Record (NFC WKT Hs)" (see page 90 of WiFi spec). I believe this record "tells" Android to register the network in the following token.

How to create the handover record:

ndefRecords = new NdefRecord[2];
byte[] version = new byte[] { (0x1 << 4) | (0x2)};
ndefRecords[0] = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_HANDOVER_REQUEST, new byte[0], version);
// and then obviously add the record you create with the method below.

Method for creating the configuration token:

private NdefRecord createWifiRecord(String[] data) {
    String ssid = data[0];
    String password = data[1];
    String auth = data[2];
    String crypt = data[3];
    byte[] authByte = getAuthBytes(auth);
    byte[] cryptByte = getCryptBytes(crypt);
    byte[] ssidByte = ssid.getBytes();
    byte[] passwordByte = password.getBytes();
    byte[] ssidLength = {(byte)((int)Math.floor(ssid.length()/256)), (byte)(ssid.length()%256)};
    byte[] passwordLength = {(byte)((int)Math.floor(password.length()/256)), (byte)(password.length()%256)};
    byte[] cred = {0x00, 0x36};
    byte[] idx = {0x00, 0x01, 0x01};
    byte[] mac = {0x00, 0x06};
    byte[] keypad = {0x00, 0x0B};

    byte[] payload = concat(NFCHelper.CREDENTIAL, cred,
            NFCHelper.NETWORK_IDX, idx,
            NFCHelper.NETWORK_NAME, ssidLength, ssidByte,
            NFCHelper.AUTH_TYPE, NFCHelper.AUTH_WPA_PERSONAL, authByte,
            NFCHelper.CRYPT_TYPE, NFCHelper.CRYPT_WEP, NFCHelper.CRYPT_AES_TKIP,
            NFCHelper.NETWORK_KEY, passwordLength, passwordByte);
           // NFCHelper.MAC_ADDRESS, mac);
    return NdefRecord.createMime(NFC_TOKEN_MIME_TYPE, payload);
} 

License and gist here. You can find an implementation of the concat method anywhere on the net, or just write your own.

Note: this is a fairly hacky implementation (as you may notice). I am including the AES and AES/TKIP bytes as I found in testing it worked for a variety of networks using different encryption/auth methods under Android 5.* Please feel free to change the function prototype, the String array just worked nicely with what I was doing.

Using the two records created in the first snippet above, you should then pass that into an NdefMessage and write it to your tag.

One day soon I'm going to do a write up and a far better/robust soln with graphics and stuff too, so I'll update this answer then.

The call of doStringCommand("WPS_NFC_CONFIG_TOKEN WPS " + netId) in the end is handled by the wpa_supplicant module. This feature is described here. I think the actual implementation of this can be found in wps_supplicant.c.

What you are actually trying to do isn't something Android specific actually. It's defined in the "WiFi Simple Configuration Technical Specification", which you can download by filling this form. The relevant part should be 10.1.2 Configuration Token.

NfcUtils.java has a working implementation for this! There are a few FIXMEs and TODOs, but in total it works and should give you a pretty good idea of what you need to do.

In case you want to parse such NdefRecords yourself and do something with the SSID and key, NfcWifiProtectedSetup.java shows how to do that.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!