package de.blinkt.openvpn;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.preference.PreferenceManager;
import android.security.KeyChain;
import android.security.KeyChainException;
import android.util.Base64;
import android.widget.Toast;
import de.blinkt.openvpn.core.NativeUtils;
import de.blinkt.openvpn.core.VpnStatus;
import de.blinkt.openvpn.core.OpenVpnService;
import de.blinkt.openvpn.core.X509Utils;
import org.spongycastle.util.io.pem.PemObject;
import org.spongycastle.util.io.pem.PemWriter;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Locale;
import java.util.UUID;
import java.util.Vector;
public class VpnProfile implements Serializable {
// Note that this class cannot be moved to core where it belongs since
// the profile loading depends on it being here
// The Serializable documentation mentions that class name change are possible
// but the how is unclear
//
transient static final long MAX_EMBED_FILE_SIZE = 2048*1024; // 2048kB
// Don't change this, not all parts of the program use this constant
public static final String EXTRA_PROFILEUUID = "de.blinkt.openvpn.profileUUID";
public static final String INLINE_TAG = "[[INLINE]]";
public static final String MINIVPN = "miniopenvpn";
private static final long serialVersionUID = 7085688938959334563L;
private static final String OVPNCONFIGFILE = "android.conf";
public static final int MAXLOGLEVEL = 4;
public static String DEFAULT_DNS1 = "8.8.8.8";
public static String DEFAULT_DNS2 = "8.8.4.4";
public transient String mTransientPW = null;
public transient String mTransientPCKS12PW = null;
public static final int TYPE_CERTIFICATES = 0;
public static final int TYPE_PKCS12 = 1;
public static final int TYPE_KEYSTORE = 2;
public static final int TYPE_USERPASS = 3;
public static final int TYPE_STATICKEYS = 4;
public static final int TYPE_USERPASS_CERTIFICATES = 5;
public static final int TYPE_USERPASS_PKCS12 = 6;
public static final int TYPE_USERPASS_KEYSTORE = 7;
public static final int X509_VERIFY_TLSREMOTE = 0;
public static final int X509_VERIFY_TLSREMOTE_COMPAT_NOREMAPPING = 1;
public static final int X509_VERIFY_TLSREMOTE_DN = 2;
public static final int X509_VERIFY_TLSREMOTE_RDN = 3;
public static final int X509_VERIFY_TLSREMOTE_RDN_PREFIX = 4;
// variable named wrong and should haven beeen transient
// but needs to keep wrong name to guarante loading of old
// profiles
public transient boolean profileDleted = false;
public int mAuthenticationType = TYPE_KEYSTORE;
public String mName;
public String mAlias;
public String mClientCertFilename;
public String mTLSAuthDirection = "";
public String mTLSAuthFilename;
public String mClientKeyFilename;
public String mCaFilename;
public boolean mUseLzo = true;
public String mServerPort = "1194";
public boolean mUseUdp = true;
public String mPKCS12Filename;
public String mPKCS12Password;
public boolean mUseTLSAuth = false;
public String mServerName = "openvpn.blinkt.de";
public String mDNS1 = DEFAULT_DNS1;
public String mDNS2 = DEFAULT_DNS2;
public String mIPv4Address;
public String mIPv6Address;
public boolean mOverrideDNS = false;
public String mSearchDomain = "blinkt.de";
public boolean mUseDefaultRoute = true;
public boolean mUsePull = true;
public String mCustomRoutes;
public boolean mCheckRemoteCN = false;
public boolean mExpectTLSCert = true;
public String mRemoteCN = "";
public String mPassword = "";
public String mUsername = "";
public boolean mRoutenopull = false;
public boolean mUseRandomHostname = false;
public boolean mUseFloat = false;
public boolean mUseCustomConfig = false;
public String mCustomConfigOptions = "";
public String mVerb = "1"; //ignored
public String mCipher = "";
public boolean mNobind = false;
public boolean mUseDefaultRoutev6 = true;
public String mCustomRoutesv6 = "";
public String mKeyPassword = "";
public boolean mPersistTun = false;
public String mConnectRetryMax = "5";
public String mConnectRetry = "5";
public boolean mUserEditable = true;
public String mAuth = "";
public int mX509AuthType = X509_VERIFY_TLSREMOTE_RDN;
private transient PrivateKey mPrivateKey;
// Public attributes, since I got mad with getter/setter
// set members to default values
private UUID mUuid;
public VpnProfile(String name) {
mUuid = UUID.randomUUID();
mName = name;
}
public static String openVpnEscape(String unescaped) {
if (unescaped == null)
return null;
String escapedString = unescaped.replace("\\", "\\\\");
escapedString = escapedString.replace("\"", "\\\"");
escapedString = escapedString.replace("\n", "\\n");
if (escapedString.equals(unescaped) && !escapedString.contains(" ") && !escapedString.contains("#"))
return unescaped;
else
return '"' + escapedString + '"';
}
public void clearDefaults() {
mServerName = "unknown";
mUsePull = false;
mUseLzo = false;
mUseDefaultRoute = false;
mUseDefaultRoutev6 = false;
mExpectTLSCert = false;
mPersistTun = false;
}
public UUID getUUID() {
return mUuid;
}
public String getName() {
if (mName==null)
return "No profile name";
return mName;
}
public String getConfigFile(Context context, boolean configForOvpn3) {
File cacheDir = context.getCacheDir();
String cfg = "";
// Enable managment interface
cfg += "# Enables connection to GUI\n";
cfg += "management ";
cfg += cacheDir.getAbsolutePath() + "/" + "mgmtsocket";
cfg += " unix\n";
cfg += "management-client\n";
// Not needed, see updated man page in 2.3
//cfg += "management-signal\n";
cfg += "management-query-passwords\n";
cfg += "management-hold\n\n";
cfg += getVersionEnvString(context);
cfg += "parsable-output\n";
boolean useTLSClient = (mAuthenticationType != TYPE_STATICKEYS);
if (useTLSClient && mUsePull)
cfg += "client\n";
else if (mUsePull)
cfg += "pull\n";
else if (useTLSClient)
cfg += "tls-client\n";
//cfg += "verb " + mVerb + "\n";
cfg += "verb " + MAXLOGLEVEL + "\n";
if (mConnectRetryMax == null) {
mConnectRetryMax = "5";
}
if (!mConnectRetryMax.equals("-1"))
cfg += "connect-retry-max " + mConnectRetryMax + "\n";
if (mConnectRetry == null)
mConnectRetry = "5";
cfg += "connect-retry " + mConnectRetry + "\n";
cfg += "resolv-retry 60\n";
// We cannot use anything else than tun
cfg += "dev tun\n";
// Server Address
cfg += "remote ";
cfg += mServerName;
cfg += " ";
cfg += mServerPort;
if (mUseUdp)
cfg += " udp\n";
else
cfg += " tcp-client\n";
switch (mAuthenticationType) {
case VpnProfile.TYPE_USERPASS_CERTIFICATES:
cfg += "auth-user-pass\n";
case VpnProfile.TYPE_CERTIFICATES:
// Ca
cfg += insertFileData("ca", mCaFilename);
// Client Cert + Key
cfg += insertFileData("key", mClientKeyFilename);
cfg += insertFileData("cert", mClientCertFilename);
break;
case VpnProfile.TYPE_USERPASS_PKCS12:
cfg += "auth-user-pass\n";
case VpnProfile.TYPE_PKCS12:
cfg += insertFileData("pkcs12", mPKCS12Filename);
break;
case VpnProfile.TYPE_USERPASS_KEYSTORE:
cfg += "auth-user-pass\n";
case VpnProfile.TYPE_KEYSTORE:
if (!configForOvpn3) {
String[] ks = getKeyStoreCertificates(context);
cfg += "### From Keystore ####\n";
if (ks != null) {
cfg += "\n" + ks[0] + "\n\n";
if (ks[1] != null)
cfg += "\n" + ks[1] + "\n\n";
cfg += "\n" + ks[2] + "\n\n";
cfg += "management-external-key\n";
} else {
cfg += context.getString(R.string.keychain_access) + "\n";
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN)
if (!mAlias.matches("^[a-zA-Z0-9]$"))
cfg += context.getString(R.string.jelly_keystore_alphanumeric_bug) + "\n";
}
}
break;
case VpnProfile.TYPE_USERPASS:
cfg += "auth-user-pass\n";
cfg += insertFileData("ca", mCaFilename);
}
if (mUseLzo) {
cfg += "comp-lzo\n";
}
if (mUseTLSAuth) {
if (mAuthenticationType == TYPE_STATICKEYS)
cfg += insertFileData("secret", mTLSAuthFilename);
else
cfg += insertFileData("tls-auth", mTLSAuthFilename);
if (nonNull(mTLSAuthDirection)) {
cfg += "key-direction ";
cfg += mTLSAuthDirection;
cfg += "\n";
}
}
if (!mUsePull) {
if (nonNull(mIPv4Address))
cfg += "ifconfig " + cidrToIPAndNetmask(mIPv4Address) + "\n";
if (nonNull(mIPv6Address))
cfg += "ifconfig-ipv6 " + mIPv6Address + "\n";
}
if (mUsePull && mRoutenopull)
cfg += "route-nopull\n";
String routes = "";
int numroutes = 0;
if (mUseDefaultRoute)
routes += "route 0.0.0.0 0.0.0.0\n";
else
for (String route : getCustomRoutes()) {
routes += "route " + route + "\n";
numroutes++;
}
if (mUseDefaultRoutev6)
cfg += "route-ipv6 ::/0\n";
else
for (String route : getCustomRoutesv6()) {
routes += "route-ipv6 " + route + "\n";
numroutes++;
}
// Round number to next 100
if (numroutes > 90) {
numroutes = ((numroutes / 100) + 1) * 100;
cfg += "# Alot of routes are set, increase max-routes\n";
cfg += "max-routes " + numroutes + "\n";
}
cfg += routes;
if (mOverrideDNS || !mUsePull) {
if (nonNull(mDNS1))
cfg += "dhcp-option DNS " + mDNS1 + "\n";
if (nonNull(mDNS2))
cfg += "dhcp-option DNS " + mDNS2 + "\n";
if (nonNull(mSearchDomain))
cfg += "dhcp-option DOMAIN " + mSearchDomain + "\n";
}
if (mNobind)
cfg += "nobind\n";
// Authentication
if (mCheckRemoteCN) {
if (mRemoteCN == null || mRemoteCN.equals(""))
cfg += "verify-x509-name " + mServerName + " name\n";
else
switch (mX509AuthType) {
// 2.2 style x509 checks
case X509_VERIFY_TLSREMOTE_COMPAT_NOREMAPPING:
cfg += "compat-names no-remapping\n";
case X509_VERIFY_TLSREMOTE:
cfg += "tls-remote " + openVpnEscape(mRemoteCN) + "\n";
break;
case X509_VERIFY_TLSREMOTE_RDN:
cfg += "verify-x509-name " + openVpnEscape(mRemoteCN) + " name\n";
break;
case X509_VERIFY_TLSREMOTE_RDN_PREFIX:
cfg += "verify-x509-name " + openVpnEscape(mRemoteCN) + " name-prefix\n";
break;
case X509_VERIFY_TLSREMOTE_DN:
cfg += "verify-x509-name " + openVpnEscape(mRemoteCN) + "\n";
break;
}
}
if (mExpectTLSCert)
cfg += "remote-cert-tls server\n";
if (nonNull(mCipher)) {
cfg += "cipher " + mCipher + "\n";
}
if (nonNull(mAuth)) {
cfg += "auth " + mAuth + "\n";
}
// Obscure Settings dialog
if (mUseRandomHostname)
cfg += "#my favorite options :)\nremote-random-hostname\n";
if (mUseFloat)
cfg += "float\n";
if (mPersistTun) {
cfg += "persist-tun\n";
cfg += "# persist-tun also sets persist-remote-ip to avoid DNS resolve problem\n";
cfg += "persist-remote-ip\n";
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean usesystemproxy = prefs.getBoolean("usesystemproxy", true);
if (usesystemproxy) {
cfg += "# Use system proxy setting\n";
cfg += "management-query-proxy\n";
}
if (mUseCustomConfig) {
cfg += "# Custom configuration options\n";
cfg += "# You are on your on own here :)\n";
cfg += mCustomConfigOptions;
cfg += "\n";
}
return cfg;
}
private String getVersionEnvString(Context c) {
String version = "unknown";
try {
PackageInfo packageinfo = c.getPackageManager().getPackageInfo(c.getPackageName(), 0);
version = packageinfo.versionName;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return String.format(Locale.US, "setenv IV_OPENVPN_GUI_VERSION \"%s %s\"\n", c.getPackageName(), version);
}
//! Put inline data inline and other data as normal escaped filename
private String insertFileData(String cfgentry, String filedata) {
if (filedata == null) {
// TODO: generate good error
return String.format("%s %s\n", cfgentry, "missing");
} else if (filedata.startsWith(VpnProfile.INLINE_TAG)) {
String datawoheader = filedata.substring(VpnProfile.INLINE_TAG.length());
return String.format(Locale.ENGLISH, "<%s>\n%s\n%s>\n", cfgentry, datawoheader, cfgentry);
} else {
return String.format(Locale.ENGLISH, "%s %s\n", cfgentry, openVpnEscape(filedata));
}
}
private boolean nonNull(String val) {
if (val == null || val.equals(""))
return false;
else
return true;
}
private Collection getCustomRoutes() {
Vector cidrRoutes = new Vector();
if (mCustomRoutes == null) {
// No routes set, return empty vector
return cidrRoutes;
}
for (String route : mCustomRoutes.split("[\n \t]")) {
if (!route.equals("")) {
String cidrroute = cidrToIPAndNetmask(route);
if (cidrroute == null)
return null;
cidrRoutes.add(cidrroute);
}
}
return cidrRoutes;
}
private Collection getCustomRoutesv6() {
Vector cidrRoutes = new Vector();
if (mCustomRoutesv6 == null) {
// No routes set, return empty vector
return cidrRoutes;
}
for (String route : mCustomRoutesv6.split("[\n \t]")) {
if (!route.equals("")) {
cidrRoutes.add(route);
}
}
return cidrRoutes;
}
private String cidrToIPAndNetmask(String route) {
String[] parts = route.split("/");
// No /xx, assume /32 as netmask
if (parts.length == 1)
parts = (route + "/32").split("/");
if (parts.length != 2)
return null;
int len;
try {
len = Integer.parseInt(parts[1]);
} catch (NumberFormatException ne) {
return null;
}
if (len < 0 || len > 32)
return null;
long nm = 0xffffffffl;
nm = (nm << (32 - len)) & 0xffffffffl;
String netmask = String.format(Locale.ENGLISH, "%d.%d.%d.%d", (nm & 0xff000000) >> 24, (nm & 0xff0000) >> 16, (nm & 0xff00) >> 8, nm & 0xff);
return parts[0] + " " + netmask;
}
private String[] buildOpenvpnArgv(File cacheDir) {
Vector args = new Vector();
// Add fixed paramenters
//args.add("/data/data/de.blinkt.openvpn/lib/openvpn");
args.add(cacheDir.getAbsolutePath() + "/" + VpnProfile.MINIVPN);
args.add("--config");
args.add(cacheDir.getAbsolutePath() + "/" + OVPNCONFIGFILE);
return args.toArray(new String[args.size()]);
}
public Intent prepareIntent(Context context) {
String prefix = context.getPackageName();
Intent intent = new Intent(context, OpenVpnService.class);
if (mAuthenticationType == VpnProfile.TYPE_KEYSTORE || mAuthenticationType == VpnProfile.TYPE_USERPASS_KEYSTORE) {
if (getKeyStoreCertificates(context) == null)
return null;
}
intent.putExtra(prefix + ".ARGV", buildOpenvpnArgv(context.getCacheDir()));
intent.putExtra(prefix + ".profileUUID", mUuid.toString());
ApplicationInfo info = context.getApplicationInfo();
intent.putExtra(prefix + ".nativelib", info.nativeLibraryDir);
try {
FileWriter cfg = new FileWriter(context.getCacheDir().getAbsolutePath() + "/" + OVPNCONFIGFILE);
cfg.write(getConfigFile(context, false));
cfg.flush();
cfg.close();
} catch (IOException e) {
e.printStackTrace();
}
return intent;
}
String[] getKeyStoreCertificates(Context context) {
return getKeyStoreCertificates(context, 5);
}
class NoCertReturnedException extends Exception {
public NoCertReturnedException (String msg) {
super(msg);
}
}
synchronized String[] getKeyStoreCertificates(Context context,int tries) {
PrivateKey privateKey = null;
X509Certificate[] cachain;
Exception exp=null;
try {
privateKey = KeyChain.getPrivateKey(context, mAlias);
mPrivateKey = privateKey;
String keystoreChain = null;
cachain = KeyChain.getCertificateChain(context, mAlias);
if(cachain == null)
throw new NoCertReturnedException("No certificate returned from Keystore");
if (cachain.length <= 1 && !nonNull(mCaFilename)) {
VpnStatus.logMessage(VpnStatus.LogLevel.ERROR, "", context.getString(R.string.keychain_nocacert));
} else {
StringWriter ksStringWriter = new StringWriter();
PemWriter pw = new PemWriter(ksStringWriter);
for (int i = 1; i < cachain.length; i++) {
X509Certificate cert = cachain[i];
pw.writeObject(new PemObject("CERTIFICATE", cert.getEncoded()));
}
pw.close();
keystoreChain = ksStringWriter.toString();
}
String caout = null;
if (nonNull(mCaFilename)) {
try {
Certificate cacert = X509Utils.getCertificateFromFile(mCaFilename);
StringWriter caoutWriter = new StringWriter();
PemWriter pw = new PemWriter(caoutWriter);
pw.writeObject(new PemObject("CERTIFICATE", cacert.getEncoded()));
pw.close();
caout= caoutWriter.toString();
} catch (Exception e) {
VpnStatus.logError("Could not read CA certificate" + e.getLocalizedMessage());
}
}
StringWriter certout = new StringWriter();
if (cachain.length >= 1) {
X509Certificate usercert = cachain[0];
PemWriter upw = new PemWriter(certout);
upw.writeObject(new PemObject("CERTIFICATE", usercert.getEncoded()));
upw.close();
}
String user = certout.toString();
String ca, extra;
if(caout==null) {
ca =keystoreChain;
extra=null;
} else {
ca = caout;
extra=keystoreChain;
}
return new String[]{ca, extra, user};
} catch (InterruptedException e) {
exp=e;
} catch (FileNotFoundException e) {
exp=e;
} catch (CertificateException e) {
exp=e;
} catch (IOException e) {
exp=e;
} catch (KeyChainException e) {
exp=e;
} catch (NoCertReturnedException e) {
exp =e;
} catch (AssertionError e) {
if (tries ==0)
return null;
Toast.makeText(context, String.format("Failure getting Keystore Keys (%s), retrying",e.getLocalizedMessage()),Toast.LENGTH_LONG).show();
try {
Thread.sleep(3000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
return getKeyStoreCertificates(context, tries-1);
}
if (exp != null) {
exp.printStackTrace();
VpnStatus.logError(R.string.keyChainAccessError, exp.getLocalizedMessage());
VpnStatus.logError(R.string.keychain_access);
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN) {
if (!mAlias.matches("^[a-zA-Z0-9]$")) {
VpnStatus.logError(R.string.jelly_keystore_alphanumeric_bug);
}
}
}
return null;
}
//! Return an error if somethign is wrong
public int checkProfile(Context context) {
if (mAuthenticationType == TYPE_KEYSTORE || mAuthenticationType == TYPE_USERPASS_KEYSTORE) {
if (mAlias == null)
return R.string.no_keystore_cert_selected;
}
if (!mUsePull) {
if (mIPv4Address == null || cidrToIPAndNetmask(mIPv4Address) == null)
return R.string.ipv4_format_error;
}
if (!mUseDefaultRoute && getCustomRoutes() == null)
return R.string.custom_route_format_error;
// Everything okay
return R.string.no_error_found;
}
//! Openvpn asks for a "Private Key", this should be pkcs12 key
//
public String getPasswordPrivateKey() {
if (mTransientPCKS12PW != null) {
String pwcopy = mTransientPCKS12PW;
mTransientPCKS12PW = null;
return pwcopy;
}
switch (mAuthenticationType) {
case TYPE_PKCS12:
case TYPE_USERPASS_PKCS12:
return mPKCS12Password;
case TYPE_CERTIFICATES:
case TYPE_USERPASS_CERTIFICATES:
return mKeyPassword;
case TYPE_USERPASS:
case TYPE_STATICKEYS:
default:
return null;
}
}
boolean isUserPWAuth() {
switch (mAuthenticationType) {
case TYPE_USERPASS:
case TYPE_USERPASS_CERTIFICATES:
case TYPE_USERPASS_KEYSTORE:
case TYPE_USERPASS_PKCS12:
return true;
default:
return false;
}
}
public boolean requireTLSKeyPassword() {
if (!nonNull(mClientKeyFilename))
return false;
String data = "";
if (mClientKeyFilename.startsWith(INLINE_TAG))
data = mClientKeyFilename;
else {
char[] buf = new char[2048];
FileReader fr;
try {
fr = new FileReader(mClientKeyFilename);
int len = fr.read(buf);
while (len > 0) {
data += new String(buf, 0, len);
len = fr.read(buf);
}
fr.close();
} catch (FileNotFoundException e) {
return false;
} catch (IOException e) {
return false;
}
}
if (data.contains("Proc-Type: 4,ENCRYPTED"))
return true;
else if (data.contains("-----BEGIN ENCRYPTED PRIVATE KEY-----"))
return true;
else
return false;
}
public int needUserPWInput() {
if ((mAuthenticationType == TYPE_PKCS12 || mAuthenticationType == TYPE_USERPASS_PKCS12) &&
(mPKCS12Password == null || mPKCS12Password.equals(""))) {
if (mTransientPCKS12PW == null)
return R.string.pkcs12_file_encryption_key;
}
if (mAuthenticationType == TYPE_CERTIFICATES || mAuthenticationType == TYPE_USERPASS_CERTIFICATES) {
if (requireTLSKeyPassword() && !nonNull(mKeyPassword))
if (mTransientPCKS12PW == null) {
return R.string.private_key_password;
}
}
if (isUserPWAuth() && !(nonNull(mUsername) && (nonNull(mPassword) || mTransientPW != null))) {
return R.string.password;
}
return 0;
}
public String getPasswordAuth() {
if (mTransientPW != null) {
String pwcopy = mTransientPW;
mTransientPW = null;
return pwcopy;
} else {
return mPassword;
}
}
// Used by the Array Adapter
@Override
public String toString() {
return mName;
}
public String getUUIDString() {
return mUuid.toString();
}
public PrivateKey getKeystoreKey() {
return mPrivateKey;
}
public String getSignedData(String b64data) {
PrivateKey privkey = getKeystoreKey();
Exception err;
byte[] data = Base64.decode(b64data, Base64.DEFAULT);
// The Jelly Bean *evil* Hack
// 4.2 implements the RSA/ECB/PKCS1PADDING in the OpenSSLprovider
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN) {
return processSignJellyBeans(privkey, data);
}
try {
Cipher rsasinger = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
rsasinger.init(Cipher.ENCRYPT_MODE, privkey);
byte[] signed_bytes = rsasinger.doFinal(data);
return Base64.encodeToString(signed_bytes, Base64.NO_WRAP);
} catch (NoSuchAlgorithmException e) {
err = e;
} catch (InvalidKeyException e) {
err = e;
} catch (NoSuchPaddingException e) {
err = e;
} catch (IllegalBlockSizeException e) {
err = e;
} catch (BadPaddingException e) {
err = e;
}
VpnStatus.logError(R.string.error_rsa_sign, err.getClass().toString(), err.getLocalizedMessage());
return null;
}
private String processSignJellyBeans(PrivateKey privkey, byte[] data) {
Exception err;
try {
Method getKey = privkey.getClass().getSuperclass().getDeclaredMethod("getOpenSSLKey");
getKey.setAccessible(true);
// Real object type is OpenSSLKey
Object opensslkey = getKey.invoke(privkey);
getKey.setAccessible(false);
Method getPkeyContext = opensslkey.getClass().getDeclaredMethod("getPkeyContext");
// integer pointer to EVP_pkey
getPkeyContext.setAccessible(true);
int pkey = (Integer) getPkeyContext.invoke(opensslkey);
getPkeyContext.setAccessible(false);
// 112 with TLS 1.2 (172 back with 4.3), 36 with TLS 1.0
byte[] signed_bytes = NativeUtils.rsasign(data, pkey);
return Base64.encodeToString(signed_bytes, Base64.NO_WRAP);
} catch (NoSuchMethodException e) {
err = e;
} catch (IllegalArgumentException e) {
err = e;
} catch (IllegalAccessException e) {
err = e;
} catch (InvocationTargetException e) {
err = e;
} catch (InvalidKeyException e) {
err = e;
}
VpnStatus.logError(R.string.error_rsa_sign, err.getClass().toString(), err.getLocalizedMessage());
return null;
}
}