Motivation: certificate pinning is the simplest way to protect self-signed SSL certificate against MITM attack.
I tried ssl_verify_fun.erl but it ends up letting everything pass, because the verify_fun callback always calls with bad_cert
. Read the doc on verify_fun, you will see why I exclude extension and perform verification for all other cases.
You can plug the 2 functions below into your own module and use it in ssl options like so {:verify_fun, {&YourModule.verify_fun/3, {:sha, "SHA1_FINGERPRINT_ALL_CAPS"}}}
.
def verify_fun(_, {:extension, _}, state) do
{:unknown, state}
end
def verify_fun(cert, _, state) do
case state do
{:sha, match} -> verify_cert_fingerprint(cert, match)
_ ->
{:fail, :fingerprint_no_match}
end
end
def verify_cert_fingerprint(cert, match) do
cert_binary = :public_key.pkix_encode(:OTPCertificate, cert, :otp)
hash = :crypto.hash(:sha, cert_binary) |> Base.encode16
case hash == match do
true -> {:valid, hash}
false -> {:fail, :fingerprint_no_match}
end
end
How to use it with httpotion:
opts = [
ssl: [
{:verify_fun,
{&YourModule.verify_fun/3, {:sha, "SHA1_FINGERPRINT_ALL_CAPS"}}
},
{:verify, :verify_none}
]
]
HTTPoison.post "http://localhost", "body", [], opts
And to get SHA1 fingerprint:
openssl x509 -in cert.pem -sha1 -noout -fingerprint | sed s/://g