Before working with HTTPS, I recommend reading Android Training: Security with SSL. Many companies have adopted full-site HTTPS, but not all implementations are correct. This post records some issues I encountered.
Prerequisite knowledge:
- Symmetric encryption
- Asymmetric encryption
| Extension | Description |
|---|
.DER | Binary certificate, usually uses .cer or .crt extension |
.PEM | Base64-encoded X.509v3 certificate, starts with ---BEGIN... |
.CRT / .CER | Essentially the same; .CRT is more aligned with Microsoft standards |
.key | Public/private key file processed with PKCS #8 |
Generating Self-Signed Certificates with OpenSSL#
Generate Private Key#
1
2
3
4
5
| $ openssl genrsa -out key.pem 1024
Generating RSA private key, 1024 bit long modulus
....................++++++
.....................++++++
e is 65537 (0x10001)
|
Create Certificate Signing Request (CSR)#
1
2
3
4
5
6
7
8
9
10
| $ openssl req -new -key key.pem -out request.pem
You are about to be asked to enter information that will be incorporated
into your certificate request.
-----
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:Beijing
Locality Name (eg, city) []:Beijing
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Hxq
Common Name (e.g. server FQDN or YOUR name) []:hao
Email Address []:haoxiqiang@live.com
|
The Common Name must match the server hostname, as required by the SSL-RFC specification.
Generate Self-Signed Certificate#
1
| $ openssl x509 -req -days 30 -in request.pem -signkey key.pem -out certificate.pem
|
Fetch Certificate from an Existing Server#
1
| echo | openssl s_client -connect hostname:443 2>&1 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > hostname.pem
|
Using Certificates in Android#
Android typically only recognizes BKS (Bouncy Castle KeyStore) certificates, so conversion is required.
Convert PEM to BKS#
1
2
3
4
5
6
7
8
| keytool -importcert -v \
-trustcacerts \
-alias 0 \
-file <(openssl x509 -in hostname.pem) \
-keystore $CERTSTORE -storetype BKS \
-providerclass org.bouncycastle.jce.provider.BouncyCastleProvider \
-providerpath bcprov-jdk16-1.46.jar \
-storepass password
|
Related tools:
Loading a BKS Certificate in Code#
1
2
3
4
5
6
7
| InputStream inputStream = context.getResources().openRawResource(res);
KeyStore keyStore = KeyStore.getInstance("BKS");
keyStore.load(inputStream, password.toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
|
If you prefer not to use resource files, you can also load text-form certificates via String -> InputStream.
1
2
3
4
5
6
7
8
9
10
11
12
| # PEM to DER
$ openssl x509 -outform der -in certificate.pem -out certificate.der
# PEM to P7B
$ openssl crl2pkcs7 -nocrl -certfile certificate.cer -out certificate.p7b -certfile CAcert.cer
# PEM to PFX
$ openssl pkcs12 -export -out certificate.pfx -inkey privateKey.key -in certificate.crt -certfile CAcert.crt
# DER to PEM
$ openssl x509 -inform der -in certificate.cer -out certificate.pem
# P7B to PEM
$ openssl pkcs7 -print_certs -in certificate.p7b -out certificate.cer
# PFX to PEM
$ openssl pkcs12 -in certificate.pfx -out certificate.cer -nodes
|
1
2
3
| openssl x509 -in cert.pem -text -noout
openssl x509 -in cert.cer -text -noout
openssl x509 -in cert.crt -text -noout
|
Common Issues#
SSLPeerUnverifiedException: Hostname not verified#
This exception occurs when the Common Name in a self-signed certificate doesn’t match the server domain. You can work around it with a custom HostnameVerifier, but never set verify() to always return true:
1
2
3
4
5
6
7
8
| HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
HostnameVerifier hv =
HttpsURLConnection.getDefaultHostnameVerifier();
return hv.verify("example.com", session);
}
};
|
Reference: Hostname Not Verified Issue
References#