/* * TeleStax, Open Source Cloud Communications * Copyright 2011-2015, Telestax Inc and individual contributors * by the @authors tag. * * This program is free software: you can redistribute it and/or modify * under the terms of the GNU Affero General Public License as * published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/> * * For questions related to commercial use licensing, please contact sales@telestax.com. * */ package org.restcomm.android.sdk.SignalingClient.JainSipClient; import android.content.Context; import org.spongycastle.asn1.x509.BasicConstraints; import org.spongycastle.asn1.x509.ExtendedKeyUsage; import org.spongycastle.asn1.x509.GeneralName; import org.spongycastle.asn1.x509.GeneralNames; import org.spongycastle.asn1.x509.KeyPurposeId; import org.spongycastle.asn1.x509.KeyUsage; import org.spongycastle.asn1.x509.X509Extensions; import org.spongycastle.jce.provider.BouncyCastleProvider; import org.spongycastle.x509.X509V3CertificateGenerator; import java.io.File; import java.io.FileOutputStream; import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.NoSuchProviderException; import java.security.SecureRandom; import java.security.Security; import java.security.SignatureException; import java.security.cert.X509Certificate; import java.security.spec.ECGenParameterSpec; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.Properties; import javax.security.auth.x500.X500Principal; public class JainSipSecurityHelper { private static final String TAG = "JainSipSecurityHelper"; static { // IMPORTANT: make the SpongyCastle implementation take preference Security.insertProviderAt(new BouncyCastleProvider(), 1); } /* * Create custom keystore, generate key and certificate and add them to it for use in encryption, etc, and in the end * also add to the keystore all the trusted certificates from the System Wide Android CA Store, so that we properly accept * legit server certificates * * @param context Android context * @param filename Filename to use for storing the keystore * @return HashMap containing keystore full path (key 'keystore-path') and keystore password (key 'keystore-password') */ public static HashMap<String, String> generateKeystore(Context context, String filename) { HashMap<String, String> parameters = new HashMap<String, String>(); try { SecureRandom random = new SecureRandom(); // this yields 26 base32 characters parameters.put("keystore-password", new BigInteger(130, random).toString(32)); // Create custom BKS store KeyStore ks = KeyStore.getInstance("BKS"); ks.load(null); // Generate key pair using Elliptic Curve algorithm and Bouncy Castle provider KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC"); kpg.initialize(new ECGenParameterSpec("secp256r1")); KeyPair kp = kpg.generateKeyPair(); // Generate actual X509v3 certificate X509Certificate cert = generateCertificate(kp); X509Certificate[] certs = new X509Certificate[1]; certs[0] = cert; // Add all the above in the keystore ks.setKeyEntry("restcomm-android-sdk", kp.getPrivate(), parameters.get("keystore-password").toCharArray(), certs); // Copy all trusted CA certs from System Wide keystore to our custom keystore, so that JAIN sip can properly // trust servers it talks to KeyStore CAks = KeyStore.getInstance("AndroidCAStore"); CAks.load(null); Enumeration<String> aliases = CAks.aliases(); while (aliases.hasMoreElements()) { String alias = aliases.nextElement(); ks.setCertificateEntry(alias, CAks.getCertificate(alias)); } // Save keystore in filesystem and retrieve path so that JAIN SIP can access it File keystoreFile = new File(context.getFilesDir(), filename); FileOutputStream outputStream = new FileOutputStream(keystoreFile); ks.store(outputStream, parameters.get("keystore-password").toCharArray()); outputStream.close(); parameters.put("keystore-path", keystoreFile.getAbsolutePath()); } catch (Exception e) { e.printStackTrace(); } return parameters; } /* // Sets up TLS keystore and return a full path to it, usable by JAIN private String setupTls(Context context) { String filename = "restcomm.keystore"; // Check if keystore file exists in internal storage and if not copy it from Assets folder. This // whole thing is needed because JAIN SIP needs a path to work with, and I haven't found a way // to reference assets using a path, so :( File keystoreFile = new File(context.getFilesDir(), filename); if (!keystoreFile.exists()) { BufferedReader reader = null; DataInputStream inputStream = null; try { //reader = new BufferedReader(new InputStreamReader(context.getAssets().open(filename))); inputStream = new DataInputStream(context.getAssets().open(filename)); FileOutputStream outputStream = new FileOutputStream(keystoreFile); // do reading, usually loop until end of file reading String line; while (inputStream.available() > 0) { byte b = inputStream.readByte(); outputStream.write(b); } outputStream.close(); } catch (IOException e) { e.printStackTrace(); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } return keystoreFile.getAbsolutePath(); } */ // generate X509 V3 Certificate public static X509Certificate generateCertificate(KeyPair pair) throws InvalidKeyException, NoSuchProviderException, SignatureException { // generate the certificate X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis())); certGen.setIssuerDN(new X500Principal("CN=Restcomm Android SDK")); certGen.setNotBefore(new Date(System.currentTimeMillis() - 50000)); // TODO: using 1 day for now, need to increase that certGen.setNotAfter(new Date(System.currentTimeMillis() + 86400000)); certGen.setSubjectDN(new X500Principal("CN=Restcomm Android SDK")); certGen.setPublicKey(pair.getPublic()); certGen.setSignatureAlgorithm("SHA1withECDSA"); certGen.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(false)); certGen.addExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment)); certGen.addExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage(KeyPurposeId.id_kp_serverAuth)); certGen.addExtension(X509Extensions.SubjectAlternativeName, false, new GeneralNames(new GeneralName(GeneralName.rfc822Name, "android-sdk@cloud.restcomm.com"))); // provider is Bouncy Castle return certGen.generateX509Certificate(pair.getPrivate(), "BC"); } public static void setProperties(Properties properties, String keystorePath, String keystorePassword, Boolean disableCertVerification) { properties.setProperty("javax.net.ssl.keyStore", keystorePath); properties.setProperty("javax.net.ssl.trustStore", keystorePath); properties.setProperty("javax.net.ssl.keyStorePassword", keystorePassword ); properties.setProperty("javax.net.ssl.keyStoreType", "bks" ); properties.setProperty("android.gov.nist.javax.sip.ENABLED_CIPHER_SUITES", "TLS_RSA_WITH_AES_128_CBC_SHA SSL_RSA_WITH_3DES_EDE_CBC_SHA" ); if (disableCertVerification != null && disableCertVerification) { properties.setProperty("android.gov.nist.javax.sip.TLS_CLIENT_AUTH_TYPE", "DisabledAll"); } } }