I am developing an android application which can connect with multiple node server. This connection needs to be secure so i need certificates. But i cant pay to certificates. As my researches, i will create certificates for each server and sign them with my own root certificate(I also need that). Then i will pin root certificate into my android application. So i can connect multiple server from one android app. But i dont know to create this certificates and how to pin it into android application.
A CA can generate a certificate bound to an IP, but it is not usual. I agree in this case it is more appropriate to use self-generated certificates. You need
1) Create the CA certificate and SSL certificate
Extracted from here You will need openssl
Create the CA certificate
openssl genrsa -out rootCA.key 2048
openssl genrsa -des3 -out rootCA.key 2048
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem
This will start an interactive script which will ask you for various bits of information. You will get rootCA.pem
Create one certificate for each device
openssl genrsa -out device.key 2048
openssl req -new -key device.key -out device.csr
openssl x509 -req -in device.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out device.crt -days 500 -sha256
You’ll be asked various questions (Country, State/Province, etc.) in the second step insert in 'common name' the IP or name of your device. It is important to match the real name because browser or android device will validate it
2) Configure your nodejs server to use https
I have no enough knowledge of node.js to provide you a good explanation or a link, so use the official documentation. Maybe some reader could edit this and provide a link
3) add the public key and the chain of the certificate to the truststore of the android application.
Extracted here from
You will need
1) Get the public part of your CA certificate
2) Create a BKS keystore and import the certificate (only the root will be needed)
3) Use the keystore in your app. Create a Custom Apache HTTP client which uses your keystore to configure de SSL connection
The details are in the link, that is in the community wiki.
For Android Volley. Using Android Volley With Self-Signed SSL Certificate
Related
With OpenSSL I created a certificate using the following.
openssl req -x509 -newkey rsa:4096 -keyout myKey.pem -out cert.pem -days 365 -nodes
=> Hitting enter on all prompts
openssl pkcs12 -export -out keyStore.p12 -inkey myKey.pem -in cert.pem -name "alias" -passout pass:123
=>Transferring keyStore.p12 and cert.pem to my android device (S10+)
=>Checked in cert.pem in settings
Then I tried to check In keyStore.p12: android settings > Biometrics and Security > Other Security Settings > Install from device storage > VPN and app user certificate > typed in the password “123”
Android returns “Invalid Password” but I know the password is correct (double checked it on my windows machine)
I've already tried to leave the password blank, restarted my phone and computer, generated a new certificate and verified the password multiple times etc.
I´m really at a loss here. thanks for any help in advance!
Found the answer on accident.
I installed "Win64 OpenSSL v1.1.1n" and created the certificate wth that version instead of "Win64 OpenSSL v3.0.2", which resolved the problem
Try adding the option -legacy to openssl pkcs12.
I'm using OpenSsl for windows.
I want to get the certificate hash associated with Fiddler's root certificate that I downloaded from:
http://ipv4.fiddler:8888/
So I'm executing from the command-line:
openssl x509 -hash -noout -in FiddlerRoot.cer
But I'm getting this error instead of getting the 8 characters long alphanumeric hash:
unable to load certificate
15176:error:0906D06C:PEM routines:PEM_read_bio:no start line:./crypto/pem/pem_lib.c:647:Expecting: TRUSTED CERTIFICATE
The message seems related to PEM (Privacy-Enhaced Mail), but Fiddler's cert doesn't have PEM and I don't need it since I just want the cert to "Ensure the identity of a remote computer".
Maybe I got something wrong and the hash is only for other purposes?
The command does work with other certificates that I extracted from Android at /system/etc/security/cacerts/00673b5b.0. which returns 00673b5b.
I opened both certs in notepad to compare them and notice that the cert from Android is readable format and has a PEM section, while FiddlerRoot.cer is binary and no PEM, so it may have something to do.
Notice that the certificate hash is not the same as the file's CRC32 or other file hashes, but something related to SSL (I already tested if the file's hash matched with the SSL hash but it didn't).
The reason I need the cert hash is because I think Android requires certificate files to be named with the hash and the ".0" extension: [CertHash].0
and installing the cert in Android in the "user" store won't work for me, it has to be in the "system" store.
Can someone provide a way/command to get FiddlerRoot.cer hash?
Or am I mistaken in Android's naming requirement for installing FiddlerRoot.cer in the "system" store?
Is there any other way to install FiddlerRoot.cer in the "system" store without calculating the cert hash?
Notice that from Android's user interface, installing a cert goes to the "user" store, not to "system".
PEM is not just for emails, it is the file format (see wikipedia).
The FiddlerRoot.cer must be converted to .pem format before calculating the hash:
openssl x509 -inform der -in FiddlerRoot.cer -outform PEM -out FiddlerRoot.pem
And then you can finally get the hash with the command:
openssl x509 -hash -noout -in FiddlerRoot.pem
I'm working on an app that connects two Android devices via Wifi so they can exchange files/data using TCP. Since Android Oreo (API level 26) there's finally an official API for this: WifiManager.startLocalOnlyHotspot(). This creates a Wifi hotspot/network without internet access. The documentation says:
Applications should also be aware that this network will be shared with other applications. Applications are responsible for protecting their data on this network (e.g., TLS).
I have no experience in using TLS when it comes to connecting two devices via TCP, so I searched around and found some approaches mentioning self-signed certificates. I'm not sure wether this is a good practice; I can't get it working anyway. Any help is appreciated!
What I did so far:
I created a self-signed certificate using OpenSSL as described in this answer:
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 10
I created a new keystore with the latest (March 2018) Bouncy Castle provider .jar file and added cert.pem to it. The following snippet is derived from this great blog article, more specifically from the sample app it features.
ALIAS=`openssl x509 -inform PEM -subject_hash -noout -in cert.pem`
keytool -import -v -trustcacerts \
-alias $ALIAS \
-file cert.pem \
-keystore keystore_output_file \
-storetype BKS \
-providerclass org.bouncycastle.jce.provider.BouncyCastleProvider \
-providerpath bcprov-jdk15on-159.jar \
-storepass my_keystore_password
I've added keystore_output_file to the src/res/raw/ folder of my app and initialized a SSLContext. Then my app creates a SSLServerSocket when acting as server or a SSLSocket when acting as client. Originally I found this approach here.
// Exception handling omitted
KeyStore keyStore = KeyStore.getInstance("BKS");
InputStream certStore = context.getResources().openRawResource(R.raw.keystore_output_file);
keyStore.load(certStore, "my_keystore_password".toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, "my_key_password".toCharArray());
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());
...continuing as server:
SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();
SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(port);
SSLSocket sslClientSocket = (SSLSocket) sslServerSocket.accept();
...continuing as client:
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(ipAddress, port);
sslSocket.startHandshake();
The Problem:
The connection fails. There are different error messages depending on what version of Android the server device is running, but both are mentioning problems with ciphers. The Android versions of my testing devices are 4.3 and 7.1.1. The stacktraces are:
Server: Android 7.1.1
javax.net.ssl.SSLHandshakeException: Handshake failed
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:429)
at com.android.org.conscrypt.OpenSSLSocketImpl.waitForHandshake(OpenSSLSocketImpl.java:682)
at com.android.org.conscrypt.OpenSSLSocketImpl.getInputStream(OpenSSLSocketImpl.java:644)
at com.candor.tlstcptest.ServerWorkerThread.run(ServerWorkerThread.java:46)
Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x8b0f8a40: Failure in SSL library, usually a protocol error
error:100000b8:SSL routines:OPENSSL_internal:NO_SHARED_CIPHER (external/boringssl/src/ssl/s3_srvr.c:1059 0x99b4286a:0x00000000)
at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:357)
Server: Android 4.3
javax.net.ssl.SSLException: Could not find any key store entries to support the enabled cipher suites.
at org.apache.harmony.xnet.provider.jsse.OpenSSLServerSocketImpl.checkEnabledCipherSuites(OpenSSLServerSocketImpl.java:232)
at org.apache.harmony.xnet.provider.jsse.OpenSSLServerSocketImpl.accept(OpenSSLServerSocketImpl.java:177)
at com.candor.tlstcptest.ServerThread.run(ServerThread.java:71)
Now I'm stuck, I don't even know how to start to resolve this problem and I haven't yet found helpful information online. I wonder if there is really no offical documentation on this topic... As I said, any help is appreciated! Thanks
The problem here is that you've created a keystore that only contains the certificate, not its private key. (This is what keytool -import ... does.)
One way to create a keystore that has a private key entry (with its corresponding certificate) would be to create a PKCS#12 store from OpenSSL and then convert it into BKS via keytool (more or less the same principle as here).
openssl pkcs12 -export -in cert.pem -inkey key.pem -out store.p12
Then, convert it into BKS using keytool -importkeystore (not just -import). I haven't tried the exact command, but this should be something like this:
keytool -importkeystore \
-srckeystore store.p12 -srcstoretype PKCS12 \
-destkeystore store.jks -deststoretype BKS \
-providerclass org.bouncycastle.jce.provider.BouncyCastleProvider \
-providerpath bcprov-jdk15on-159.jar
(Check the keytool documentation for the exact options you may also need.)
This should result in a store.jks keystore that also contains the private key. That keystore should only be used on the server side, as the keystore, not as the truststore on the client side." (The one you already had can be used as a truststore, on the client side.)
A couple of side notes:
Another thing to check with SSL/TLS is the identity in the certificate (not just trusting that the certificate is genuine and issued by a party you know). This is not always checked by default in Java (depending on the options you use).
For this particular type of application (essentially ad-hoc connections), you may want to generate the certificate/private key pair on the server upon installation, show its fingerprint to the user, then have a looser trust manager on the client that displays the fingerprint it has to validate it interactively (and possibly remember it).
If you do indeed manage to validate an individual certificate for the remote device explicitly, you might be able to do it securely without checking the name in the certificate (as long as the client can check it's connecting to a device with that exact certificate).
I'm not familiar enough with startLocalOnlyHotspot, but I suspect many of those details will depend on the IP address used by the hotspot. I'd imagine it also embeds some form of DHCP server to give the client an IP address, but I'm not sure how the client can get the IP address of the hotspot device itself (there might be some mDNS integration, for example).
Is this even possible? I have a key-pair that I already made with GPG but I just can't find a way to sign it with that key. I don't really want to make a new key with keytool or whatever just for this; I'd rather use the key I have now. Anybody know how I could do this? Thanks in advance.
I very much doubt that GPG generates keys that could be used by jarsigner. It might be possible to write a converter to do this, but it would be far less work to just bite the bullet and generate a new key. The command to do this is simply
keytool -genkey -alias mynickname -validity 20000 -keystore ~/.android/my-keystore
(p.s. make a backup of the key and make very sure you don't forget either the keystore password or the key password. There are far too many sad stories of people who've put apps on the market and then forgotten or lost the password.)
I just wanna manage the OpenPGP keys only too. So here is my way.
openpgp2ssh
First install it from monkeysphere.
sudo apt install monkeysphere
Note: openpgp2ssh works only if the secret key is not password-protected and RSA keys. So it might be necessary to remove the protection.
Now, export the PGP key and hand it over to openpgp2ssh:
gpg --list-keys # show your keys with keyid.
gpg --export-secret-subkeys your#email | openpgp2ssh $SubKeyId > id_rsa
openssl rsa -in id_rsa -outform pem > key.pem
openssl req -new -key key.pem -out request.pem
openssl x509 -req -days 9999 -in request.pem -signkey key.pem -out certificate.pem
openssl pkcs8 -topk8 -outform DER -in key.pem -inform PEM -out key.pk8 -nocrypt
You do need to sign with jarsigner. But jarsigner is actually a little more flexible than you'd think. If you already have a signing key you want to use then you can export it out of gpg and import it into a java keystore then sign that way. If you want to attempt to do that you can try keytool but it's far easier to get Keytool Explorer because keytool has a lot of options.
Jarsigner is actually pretty flexible. This Document describes a process by which you can create your own providers. I'm surprised there isn't already one that uses the GPG keystores already. There is a way to do it with a Yubikey which is the only reason I am aware of all of this - my signing keys are safely locked away in my yubikey where even I can't get them (yes I have a secure backup somewhere).
I'm getting an SSLPeerUnverifiedException: "No Peer Certificate" when connecting to a web service from my Android app which is hosted on a server with an SSL certificate by Thawte CA.
Please bear in mind that I'm in way over my head when it comes to the server side of things, but a bunch of solutions I've seen for this on SO involve blatantly trusting any certificate. Most of the solutions are from 2010-early 2011.
I have two questions, specifically:
How / where do I check if Thawte CA is a trusted CA for Android
How do I solve this issue?
Thanks!
For anyone looking for an answer: After loads of time spent scouring SO and the internet, I learned that there could be two possible causes:
Improper installation of an intermediate certificate (which wasn't the case here)
Incorrect ordering of the certificate chain (this was the case here).
The answer that really helped me out was by SO user bdc on this thread: Apache HttpClient on Android producing CertPathValidatorException (IssuerName != SubjectName).
In short, he suggested to check the chain ordering by running the openssl s_client -connect server.domain.com:443. Running this command on Mac Terminal with the domain name of the server where the API was hosted showed that the chain ordering was incorrect.
Once the ordering was fixed on the server side, voila! Everything works A-OK!
Totally agree with #Sid here. Please don't add any spurious klugey code on android to bypass SSL exceptions. Totally defeats the purpose of SSL.
For anyone having issues connecting over https to a tomcat server from android:
Make sure you chain with the root and intermediate certificates of your CA.
I didnt generate the private key used create the CSR for GoDaddy, our CA, so I had to convert the key and certs to pkcs12 before importing into a keystore. Note the -chain option. Its important.
openssl pkcs12 -export -out mykey.pks -inkey private_key.key -in domain.crt -CAfile ca_intermed_root_bundle.crt -chain -name alias_name -passout stdin
(enter password through stdin)
Now, import mykey.pks into a java keystore
keytool -importkeystore -deststorepass changeit -destkeypass changeit -destkeystore mystore.keystore -srckeystore mykey.pks -srcstoretype PKCS12 -alias alias_name
This keystore can now be used in the tomcat 8443 connector:
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
keystoreFile="/path../mystore.keystore"
keystorePass="changeit" keyAlias="alias_name"
/>
I was having repeated SSLPeerUnverifiedExceptions when connecting from android and this totally fixed it.
Finally, please verify with http://www.sslshopper.com/ssl-checker.html or any other tool to check if the certificates are chained correctly.