How can I download the certificate of a wireless AP using 802.1X?

As far as I understand, wireless access points using WPA-Enterprise (i.e. WPA plus 802.1X) can send a public key certificate to a client during connection setup. The client can verify this certificate to make sure it is not connecting to a rogue AP (similar to the certificate validation in HTTPS).

Questions:

  • Did I understand this correctly?
  • If yes, is there a way to download the AP certificate? Ideally, I'd like a solution that works under Linux.

I'd like to download a self-signed certificate, to use it for verifying subsequent connection attempts. This would be simpler than asking the operator of the AP for a file.


Yes, the most common WPA-Enterprise configurations use either PEAP or TTLS, both implementing TLS over EAP over 802.1X.

Usually the certificate is already published somewhere by the network operators for exactly this purpose. It's not something the user should have to ask for.

Sadly, wpa_supplicant doesn't have an option to dump the certificates even in debug mode. (I'll update this if I find a better way.) You can still monitor the actual EAPOL authentication process, though. First, install Wireshark.

While disconnected, bring the interface up manually and start a capture on it:

$ sudo ip link set wlan0 up
$ wireshark -ki wlan0 &

Start wpa_supplicant and soon you'll see the TLS handshake:

The server will send its certificates immediately after ServerHello. Select the first such packet, then dig into:

802.1X
└─Extensible Authentication Protocol
  └─Secure Sockets Layer
    └─Handshake Protocol: Certificatte
      └─Certificates

Right-click the first instance of "Certificate (stuff)" and choose "Export selected packet bytes". Wireshark will save it as a file, in binary DER format. Repeat this for all other certificates. The topmost one (RADIUS server's) has information that you can configure in altsubject_match; the last one (root CA) should be given to wpa_supplicant as ca_cert.

Now you have a few *.crt or *.der files in binary DER format. Convert them to PEM "text" format:

openssl x509 -inform DER < mycert.der > mycert.pem

(If your wpa_supplicant is using OpenSSL as the TLS handler, you must give it the "root CA" certificate; giving it the server's certificate won't work.

Note that it's also possible that the last certificate seen in Wireshark won't be of a root CA, but only issued by one of the root CAs in your /etc/ssl/certs directory... If that's the case, be sure to set domain_suffix_match as well – otherwise, using public CAs would be insecure (802.1X unfortunately does not know what "hostname" to verify against, the way e.g. HTTPS would.)


I wanted to do something similar (PEAP with MSCHAPv2) so posted a question on the hostap mailing list. The author replied with:

wpa_supplicant sends out CTRL-EVENT-EAP-PEER-CERT events as control interface events during EAP authentication that uses TLS. Those message include a SHA256 hash of the certificate and full hexdump of the raw DER encoded certificate. The entries with depth=0 are for the server certificate and the higher depth values are for the CAs provided by the server.

As an example, you would see something like this in wpa_cli when connected to the wpa_supplicant control interface:

CTRL-EVENT-EAP-PEER-CERT depth=0 subject='/C=FI/O=w1.fi/CN=server.w1.fi' hash=5891bd91eaf977684e70d4376d1514621d18f09ab2020bea1ad293d59a6e8944
CTRL-EVENT-EAP-PEER-CERT depth=0 subject='/C=FI/O=w1.fi/CN=server.w1.fi' cert=308203ac30820294a003020102020900d8d3e3a6cbe3cd69300d06092a864886f70d01010b05003041310b30090603550406130246493110300e06035504070c0754757573756c61310e300c060355040a0c0577312e66693110300e06035504030c07526f6f74204341301e170d3231303530333137303235335a170d3232303530333137303235335a3034310b3009060355040613024649310e300c060355040a0c0577312e66693115301306035504030c0c7365727665722e77312e666930820122300d06092a864886f70d01010105000382010f003082010a0282010100fd0e5ecd192c6a4183d92c0e36cd09b08fb2c98cc8d203386e54bc43eecef859b1e93f6dd45ee3c573193d0bbc29d7d06e750035fcf765b1b60fcb0649cfa94e3dc2644b18240a70b86e54dc903f6c18fdb688ab37dc8b4bb5e1ffedbb4505a3f0aa512bf179067f2fcd5dfce68aaf1bb97bca4375011d8e2dd9111386fb3c4b60a42949e3d22020cb5c0d9cd29c302111bcb374df64b0b767ee6adfaf3d1802b7dce55475b56c40c4b08a1bc6fdab1eb9efd183090d55ba17884ed0bdcf73367c284bf6aa48d4e8fb25d81dafc96873ac46aecbeb793657adc37002156df0a54373ab7204b11660740e4ac18f52281a957eabd004f6fa11da1bd199a44f9eed0203010001a381b33081b030090603551d1304023000301d0603551d0e0416041471267a1f7281972411aac075fabf31106949d0e7301f0603551d23041830168014a4fdb9391b81b3aaeb881dd481a9b51170cca7e1303506082b0601050507010104293027302506082b060105050730018619687474703a2f2f7365727665722e77312e66693a383838382f30170603551d110410300e820c7365727665722e77312e666930130603551d25040c300a06082b06010505070301300d06092a864886f70d01010b05000382010100b1d96f63a139815510cd05c1cc147d330a9aefc034dc77765b41922015a3c601af1f057cbb374a1d1f005e4a176b7a6a6ca4fbc7e41ee2387f25d1459beb6895f91bba9f40b95dc76ca0466b05acf4384d640b5de07b3031b8a6dad0a53e817b6a1ab54f2d4af200681368b8836b79f9b263a7df52de8e129d8773ec4b4738a29829a8c88b8eb12b47ddebcf6add2102005e7d8d4c19aa7d1bf49ba6a8f8f3a79d66e8540cdc7fe9afa24c888b87542833c55387b041e42e337baac02982c2bd541029f92da499d1e7c7570766ccd02e745d98280afe8a323c623d307c750c1631cecbe7411e4f3c921a3e80b11378b553b26a449fc13b92cf080e083210271b

If you want to trust only the specific certificate for future connections, you can use that hash value in the network profile in the following style:

ca_cert="hash://server/sha256/5891bd91eaf977684e70d4376d1514621d18f09ab2020bea1ad293d59a6e8944"

This is the way that can be used to implement trust-on-first-use type of an policy which would sound like the use case you are describing here. A bit more complete mechanism is defined in the Wi-Fi Alliance WPA3 specification (search for "TOD" in that document).

This worked for me, but I should note that I didn't need to use wpa_cli as verifying the hash was enough for me. This was printed out by wpa_supplicant by default for me so it was easy to copy/paste from there.

Note: if you want the whole certificate then you'll need to set the ctrl_interface in your config file and have wpa_cli connected to see it. Something like this should do:

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel