I’m trying to automate the creation of Wireguard profiles to connect to various Proton VPN servers. As far as I can tell, when you generate one online through account.proton.me:
- The client generates a private key in-browser.
- Client POSTs the corresponding public key, along with the chosen server and some other parameters, to
/api/vpn/v1/certificate. - Server registers the given public key and returns the parameters that should be used to construct the config file.
- Client combines returned parameters with the private key to create the final config file.
I am attempting to replicate this process with a key generated using wg:
wg genkey | tee privkey.key | wg pubkey > pubkey.key
However when sending this pubkey to the server (leaving everything else exactly as captured from a working in-browser request), it responds with:
{
"Code": 2001,
"Error": "Unable to read the key, please provide a valid EC key",
"Details": {}
}
Replacing my custom pubkey with a pre-existing pubkey from a config generated through the Web UI instead returns ClientPublicKey fingerprint conflict, please regenerate a new key, so I don’t think I’m messing up the request format.
My questions are:
- Is there a better/more official way to do this? I couldn’t find anything searching.
- Why does this not work? Surely
wgcreates valid EC keys? Does Proton have some additional constraints on valid keys for some reason?
I don’t have much (or really any) experience with WireGuard, so perhaps I’m missing something obvious? Any help would be appreciated.
I recently ran into this problem as well and decided to do some web inspection. Turns out Proton’s web UI uses Ed25519 instead of standard X25519.
Code and more detailed explanations are found here: https://github.com/neovimium/protonvpn-keygen
That’s fantastic, thank you! It works perfectly.

