系统输入cmd:
windows 解决方法:
python -m ensurepip
升级pip版本:
pip install --upgrade pip
linux解决方法:
$ python -m ensurepip $ sudo easy_install pip $ python -m pip install --upgrade pip
系统输入cmd:
windows 解决方法:
python -m ensurepip
升级pip版本:
pip install --upgrade pip
linux解决方法:
$ python -m ensurepip $ sudo easy_install pip $ python -m pip install --upgrade pip
1)种子链接:https://itorrents.org/torrent/3D8B16242B56A3AAFB8DA7B5FC83EF993EBCF35B.torrent?title=[limetorrents.info]Microsoft.leaked.source.code.archive_2020-09-24(你不会想用手敲吧?)也可 本地下载
看了下大小是42.93GB(应该是对的~)
Android API level 14+提供了VPNService服务框架,在不root的情况下,开发者可以创建自己的VPN服务应用。
目前主要有三种,分别为OpenVPN for Android(ICS OpenVPN),OpenVPN Connect,OpenVPN Settings。
OpenVPN for Android和OpenVPN Connect使用官方VPNService API (Android 4.0+),不需要root权限,而OpenVPN Settings需要root权限。
OpenVPN for Android是一个开源的客户端,由Arne Schwabe开发,他定位于更高级的用户,提供更多的设置,它可以在app内导入配置文件。
OpenVPN Connect是一个不开源的客户端,它由OpenVPN Technologies , Inc. 开发,它定位于普通用户,它基于OpenVPN协议的C++实现。
OpenVPN Settings是最老的客户端,需要root权限,它并不使用VPNService API。
Ubuntu16,Android Studio,NDK,SDK
首先在Ubuntu下安装Android studio,安装完毕后,使用sdk manager工具安装sdk。
关于NDK,可以使用sdk manager来下载,不过在编译源码的过程中出现了错误,经过尝试发现sdk manager下载的是14版本的ndk,而14版本的ndk编译OpenVPN for Android会出现错误,建议使用12版本的ndk。
安装完Android studio,解压完ndk后,需要配置环境变量。
~/android-studio/bin Android studio安装目录 ~/Android/android-ndk-r12b ndk解压目录
进入用户目录(cd /home/bleach),打开配置文件(vim .bashrc),添加如下内容:
export PATH=$PATH:~/android-studio/bin:~/Android/android-ndk-r12b
打开搜索引擎,输入ics openvpn,进行搜索,然后进入ics openvpn的github主页,选择master分支里的一个已经添加标签的版本,本文选择的是0.6.64版本的源码。
进入Ubuntu命令行,创建一个源码目录,然后使用git clone命令将源码下载到本地。
$ mkdir ics-openvpn-v0.6.64 $ cd ics-openvpn-v0.6.64 $ git clone https://github.com/schwabe/ics-openvpn.git
step1:进入ics-openvpn-v0.6.64/ics-openvpn目录,修改.gitmodules文件。
$ cd ics-openvpn-v0.6.64/ics-openvpn $ ls –a $ vim .gitmodules
将.gitmodules文件中的url更改掉。
[submodule "main/openvpn"] path = main/openvpn url = https://github.com/schwabe/openvpn.git [submodule "main/openssl"] path = main/openssl url = https://github.com/schwabe/platform_external_openssl.git [submodule "main/breakpad"] path = main/breakpad url = https://github.com/schwabe/breakpad.git
step2:执行指令
$ git submodule sync
step3:然后执行指令
$ git submodule init
step4:然后执行指令
$ git submodule update
upate指令会消耗一定的时间。
step5:最后进入ics-openvpn-v0.6.64/ics-openvpn/main目录,执行./misc/build-native.sh,等待编译完成,可能需要修改build-native.sh的执行权限。
$ cd main $ chmod +x ./mics/build-native.sh $ ./misc/build-native.sh
未配置ndk环境变量,请参考2.2。
很有可能是ndk版本的问题,请使用12版本的ndk尝试编译。
不要在github上下载zip包的源码,请使用git clone指令将源码下载到本地。
将源码导入到Android studio中,目前OpenVPN for Android不支持导入到eclipse中,请使用Android studio开发环境。
打开Android studio,将源码导入,Android studio会自动更新gradle信息,更新完毕后,编译工程,就会生成对应apk包。
最近实现Socks5 proxy与HTTP proxy中遇到了SSLSocket隧道的问题,当然,最终问题经过自动证书校验安装管理器实现了证书的管理,也解决了SSLSocket,但是目前的问题是浏览器对Socks5和HTTP proxy还有很多不足,目前实现的两个代理工具只能在程序中使用。当然,我们今天的主要话题如下:
Java 实现TLS/SSL证书的自动安装校验,主要经过ssl/tls握手,密钥交换,证书校验机制实现。我们这里模拟浏览器,实现自动校验和证书的检测。
主要实现如下功能:
1.自动检测,校验根证书,校验过期时间,校验签名的证书是否有效,校验证书和域名是否匹配
2.实现证书的自动存储,自动安装,加载
3.实现普通Socket自动升级为SSLSocket
首先,我们先添加2个配置类
package com.ssl.rx.http; import java.security.KeyStore; public class ConnectionConfiguration { /** 证书文件路径 */ private String truststorePath; /** 证书类型 */ private String truststoreType; /** 证书文件密码 */ private String truststorePassword; /** 是否验证证书链的签名有效性 */ private boolean verifyChainEnabled = true; /** 是否校验根证书,注意,自签名证书没有根证书 */ private boolean verifyRootCAEnabled = true; /** 是否允许通过自签名证书 */ private boolean selfSignedCertificateEnabled = false; /** 是否检查证书的有效期 */ private boolean expiredCertificatesCheckEnabled = true; /** 检查域名的匹配情况 */ private boolean notMatchingDomainCheckEnabled = true; private String server; private int port; public ConnectionConfiguration() { truststorePassword = "WlZSak5GcFVUbTlsVjJSNg=="; truststorePath = "socket_tls_clientTrust.cert"; truststoreType = "jks"; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String getServer() { return server; } public void setServer(String server) { this.server = server; } public boolean isExpiredCertificatesCheckEnabled() { return expiredCertificatesCheckEnabled; } public void setSelfSignedCertificateEnabled(boolean selfSignedCertificateEnabled) { this.selfSignedCertificateEnabled = selfSignedCertificateEnabled; } public void setExpiredCertificatesCheckEnabled(boolean expiredCertificatesCheckEnabled) { this.expiredCertificatesCheckEnabled = expiredCertificatesCheckEnabled; } public boolean isSelfSignedCertificateEnabled() { return selfSignedCertificateEnabled; } public boolean isNotMatchingDomainCheckEnabled() { return notMatchingDomainCheckEnabled; } public boolean isVerifyRootCAEnabled() { return verifyRootCAEnabled; } public void setVerifyRootCAEnabled(boolean verifyRootCAEnabled) { this.verifyRootCAEnabled = verifyRootCAEnabled; } public void setVerifyChainEnabled(boolean verifyChainEnabled) { this.verifyChainEnabled = verifyChainEnabled; } public boolean isVerifyChainEnabled() { return verifyChainEnabled; } public String getTruststoreType() { return truststoreType; } public void setTruststoreType(String truststoreType) { this.truststoreType = truststoreType; } public String getTruststorePassword() { return truststorePassword; } public void setTruststorePassword(String truststorePassword) { this.truststorePassword = truststorePassword; } public String getTruststorePath() { return truststorePath; } public void setTruststorePath(String truststorePath) { this.truststorePath = truststorePath; } public void setNotMatchingDomainCheckEnabled(boolean notMatchingDomainCheckEnabled) { this.notMatchingDomainCheckEnabled = notMatchingDomainCheckEnabled; } } 然后增加一个用于存储keystore的javaBean package com.ssl.rx.http; public class KeyStoreOptions { private final String type; private final String path; private final String password; public KeyStoreOptions(String type, String path, String password) { super(); this.type = type; this.path = path; this.password = password; } public String getType() { return type; } public String getPath() { return path; } public String getPassword() { return password; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((password == null) ? 0 : password.hashCode()); result = prime * result + ((path == null) ? 0 : path.hashCode()); result = prime * result + ((type == null) ? 0 : type.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; KeyStoreOptions other = (KeyStoreOptions) obj; if (password == null) { if (other.password != null) return false; } else if (!password.equals(other.password)) return false; if (path == null) { if (other.path != null) return false; } else if (!path.equals(other.path)) return false; if (type == null) { if (other.type != null) return false; } else if (!type.equals(other.type)) return false; return true; } }
最后,我们来实现核心部分,证书管理器
package com.ssl.rx.http; public class SSLX509CertificateManager { private static final Logger logger = Logger.getLogger("SSLX509CertificateManager"); private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray(); private static Pattern cnPattern = Pattern.compile("(?i)(cn=)([^,]*)"); private static Map<KeyStoreOptions, KeyStore> stores = new HashMap<KeyStoreOptions, KeyStore>(); private static String toHexString(byte[] bytes) { StringBuilder sb = new StringBuilder(bytes.length * 3); for (int b : bytes) { b &= 0xff; sb.append(HEXDIGITS[b >> 4]); sb.append(HEXDIGITS[b & 15]); sb.append(' '); } return sb.toString(); } /** * 开始握手等一系列密钥协商 * * @param socket * @return */ public static boolean startHandshake(SSLSocket socket) { try { logger.log(Level.INFO, "-开始握手,认证服务器证书-"); socket.startHandshake(); System.out.println(); logger.log(Level.INFO, "-握手结束,结束认证服务器证书-"); } catch (SSLException e) { e.printStackTrace(); return false; } catch (IOException e) { e.printStackTrace(); return false; } return true; } public static SSLSocket createTrustCASocket(String host, int port, ConnectionConfiguration config) throws Exception { if (config == null) { config = new ConnectionConfiguration(); } KeyStore ks = getKeyStore(config); SSLContext context = SSLContext.getInstance("TLS"); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(ks); X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0]; CAX509TrustManager tm = new CAX509TrustManager(defaultTrustManager, ks, config); context.init(null, new TrustManager[] { tm }, new SecureRandom()); SSLSocketFactory factory = context.getSocketFactory(); logger.log(Level.INFO, "开始连接: " + host + ":" + port + "..."); SSLSocket socket = (SSLSocket) factory.createSocket(host, port); socket.setSoTimeout(10000); config.setServer(host); config.setPort(port); // config.setTrustKeyStore(ks); X509Certificate certificate = (X509Certificate) ks.getCertificate(host + ":" + port); if (certificate != null && isValid(certificate)) { logger.log(Level.INFO, "-证书文件存在并且有效,无需进行握手-"); return socket; } if (!startHandshake(socket)) { logger.log(Level.SEVERE, "-握手失败-"); return null; } X509Certificate[] chain = tm.chain; if (chain == null || chain.length == 0) { logger.log(Level.SEVERE, "-证书链为空,认证失败-"); return null; } if (config.isVerifyRootCAEnabled()) { boolean isValidRootCA = checkX509CertificateRootCA(ks, chain, config.isSelfSignedCertificateEnabled()); if (!isValidRootCA) { return null; } } return socket; } /** * 获取keystore,防治多次加载 * * @param config * @return * @throws KeyStoreException * @throws IOException * @throws NoSuchAlgorithmException * @throws CertificateException * @throws FileNotFoundException */ private static KeyStore getKeyStore(ConnectionConfiguration config) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, FileNotFoundException { KeyStore ks; synchronized (stores) { KeyStoreOptions options = new KeyStoreOptions(config.getTruststoreType(), config.getTruststorePath(), config.getTruststorePassword()); if (stores.containsKey(options)) { logger.log(Level.INFO, "从缓存中获取trustKeystore"); ks = stores.get(options); } else { File file = new File(config.getTruststorePath()); char[] password = config.getTruststorePassword().toCharArray(); logger.log(Level.INFO, "加载" + file + "证书文件"); ks = KeyStore.getInstance(KeyStore.getDefaultType()); if (!file.exists()) { logger.log(Level.INFO, "证书文件不存在,选择自动创建"); ks.load(null, password); } else { logger.log(Level.INFO, "证书文件存在,开始加载"); InputStream in = new FileInputStream(file); ks.load(in, password); in.close(); } stores.put(options, ks); } } return ks; } public static SSLSocket createTrustCASocket(String host, int port) throws Exception { return createTrustCASocket(host, port, null); } public static SSLSocket createTrustCASocket(Socket s, ConnectionConfiguration config) throws Exception { if (config == null) { config = new ConnectionConfiguration(); } KeyStore ks = getKeyStore(config); SSLContext context = SSLContext.getInstance("TLS"); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(ks); X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0]; CAX509TrustManager tm = new CAX509TrustManager(defaultTrustManager, ks, config); context.init(null, new TrustManager[] { tm }, new SecureRandom()); SSLSocketFactory factory = context.getSocketFactory(); String host = s.getInetAddress().getHostName(); int port = s.getPort(); logger.log(Level.INFO, "开始连接: " + host + ":" + port + "..."); SSLSocket socket = (SSLSocket) factory.createSocket(s, host, port, true); socket.setSoTimeout(10000); config.setServer(s.getInetAddress().getHostName()); config.setPort(s.getPort()); X509Certificate certificate = (X509Certificate) ks.getCertificate(host + ":" + s.getPort()); if (certificate != null && isValid(certificate)) { logger.log(Level.INFO, "-证书文件存在并且有效,无需进行握手-"); return socket; } if (!startHandshake(socket)) { return null; } X509Certificate[] chain = tm.chain; if (chain == null || chain.length == 0) { logger.log(Level.SEVERE, "-证书链为空,认证失败-"); return null; } if (config.isVerifyRootCAEnabled()) { boolean isValidRootCA = checkX509CertificateRootCA(ks, chain, config.isSelfSignedCertificateEnabled()); if (!isValidRootCA) { logger.log(Level.SEVERE, "根证书校验无效"); return null; } } return socket; } public static SSLSocket createTrustCASocket(Socket s) throws Exception { return createTrustCASocket(s, null); } public static class CAX509TrustManager implements X509TrustManager { private final X509TrustManager tm; private X509Certificate[] chain; private KeyStore keyStore; private ConnectionConfiguration config; public MessageDigest sha1 = null; public MessageDigest md5 = null; public CAX509TrustManager(X509TrustManager tm, KeyStore ks, ConnectionConfiguration config) throws NoSuchAlgorithmException { this.tm = tm; this.keyStore = ks; sha1 = MessageDigest.getInstance("SHA1"); md5 = MessageDigest.getInstance("MD5"); this.config = config; } public X509Certificate[] getAcceptedIssuers() { return tm.getAcceptedIssuers(); // 生成证书数组,用于存储新证书 } public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { tm.checkClientTrusted(chain, authType); // 检查客户端 } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { if (this.chain == null) { this.chain = getAcceptedIssuers(); } if (chain != null && chain.length > 0) { if (!checkX509CertificateValid(chain, config)) { logger.log(Level.SEVERE, "证书校验未通过"); return; } for (int i = 0; i < chain.length; i++) { X509Certificate certificate = chain[i]; if (i == 0) { saveCAToKeyStore(certificate, config.getServer() + ":" + config.getPort()); } else { saveCAToKeyStore(certificate, null); } } } } public void saveCAToKeyStore(X509Certificate certificate, String aliasKey) throws CertificateEncodingException { try { X509Certificate cert = certificate; System.out.println(" Subject " + cert.getSubjectDN()); System.out.println(" Issuer " + cert.getIssuerDN()); sha1.update(cert.getEncoded()); System.out.println(" sha1 " + toHexString(sha1.digest())); md5.update(cert.getEncoded()); System.out.println(" md5 " + toHexString(md5.digest())); String alias = keyStore.getCertificateAlias(cert); if (alias == null || alias != null && !isValid(certificate)) { if (aliasKey == null || aliasKey.length() == 0) { alias = cert.getSubjectDN().getName(); } else { alias = aliasKey; logger.log(Level.INFO, "设定指定证书别名:" + alias); } keyStore.setCertificateEntry(alias, certificate); OutputStream out = new FileOutputStream(config.getTruststorePath()); keyStore.store(out, config.getTruststorePassword().toCharArray()); out.close(); chain = Arrays.copyOf(chain, chain.length + 1); chain[chain.length - 1] = certificate; logger.fine(certificate.toString()); } } catch (KeyStoreException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (CertificateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } public static boolean isValid(X509Certificate cert) { if (cert == null) { return false; } try { cert.checkValidity(); } catch (CertificateExpiredException e) { e.printStackTrace(); return false; } catch (CertificateNotYetValidException e) { e.printStackTrace(); return false; } return true; } /** * 校验证书的有效性 * * @param chain * @param config * @return */ private static boolean checkX509CertificateValid(X509Certificate[] chain, ConnectionConfiguration config) { boolean result = true; if (config.isExpiredCertificatesCheckEnabled()) { result = result && checkX509CertificateExpired(chain); } if (config.isVerifyChainEnabled()) { result = result && checkX509CertificateChain(chain); } if (config.isNotMatchingDomainCheckEnabled()) { result = result && checkIsMatchDomain(chain, config.getServer()); } return result; } /** * 检查是否匹配域名 * * @param x509Certificates * @param server * @return */ public static boolean checkIsMatchDomain(X509Certificate[] x509Certificates, String server) { server = server.toLowerCase(); List<String> peerIdentities = getPeerIdentity(x509Certificates[0]); if (peerIdentities.size() == 1 && peerIdentities.get(0).startsWith("*.")) { String peerIdentity = peerIdentities.get(0).replace("*.", ""); if (!server.endsWith(peerIdentity)) { return false; } } else { for (int i = 0; i < peerIdentities.size(); i++) { String peerIdentity = peerIdentities.get(i).replace("*.", ""); if (server.endsWith(peerIdentity)) { return true; } } } return false; } /** * 校验根证书 * * @param trustStore * @param x509Certificates * @param isSelfSignedCertificate * 是否自签名证书 * @return */ public static boolean checkX509CertificateRootCA(KeyStore trustStore, X509Certificate[] x509Certificates, boolean isSelfSignedCertificate) { List<String> peerIdentities = getPeerIdentity(x509Certificates[0]); boolean trusted = false; try { int size = x509Certificates.length; trusted = trustStore.getCertificateAlias(x509Certificates[size - 1]) != null; if (!trusted && size == 1 && isSelfSignedCertificate) { logger.log(Level.WARNING, "-强制认可自签名证书-"); trusted = true; } } catch (KeyStoreException e) { e.printStackTrace(); } if (!trusted) { logger.log(Level.SEVERE, "-根证书签名的网站:" + peerIdentities + "不能被信任"); } return trusted; } /** * 检查证书是否过期 * * @param x509Certificates * @return */ public static boolean checkX509CertificateExpired(X509Certificate[] x509Certificates) { Date date = new Date(); for (int i = 0; i < x509Certificates.length; i++) { try { x509Certificates[i].checkValidity(date); } catch (GeneralSecurityException generalsecurityexception) { logger.log(Level.SEVERE, "-证书已经过期-"); return false; } } return true; } /** * 校验证书链的完整性 * * @param x509Certificates * @return */ public static boolean checkX509CertificateChain(X509Certificate[] x509Certificates) { Principal principalLast = null; List<String> peerIdentities = getPeerIdentity(x509Certificates[0]); for (int i = x509Certificates.length - 1; i >= 0; i--) { X509Certificate x509certificate = x509Certificates[i]; Principal principalIssuer = x509certificate.getIssuerDN(); Principal principalSubject = x509certificate.getSubjectDN(); if (principalLast != null) { if (principalIssuer.equals(principalLast)) { try { PublicKey publickey = x509Certificates[i + 1].getPublicKey(); x509Certificates[i].verify(publickey); } catch (GeneralSecurityException generalsecurityexception) { logger.log(Level.SEVERE, "-无效的证书签名-" + peerIdentities); return false; } } else { logger.log(Level.SEVERE, "-无效的证书签名-" + peerIdentities); return false; } } principalLast = principalSubject; } return true; } /** * 返回所有可用的签名方式 键值对 如CN=VeriSignMPKI-2-6 * * @param certificate * @return */ private static List<String> getSubjectAlternativeNames(X509Certificate certificate) { List<String> identities = new ArrayList<String>(); try { Collection<List<?>> altNames = certificate.getSubjectAlternativeNames(); if (altNames == null) { return Collections.emptyList(); } Iterator<List<?>> iterator = altNames.iterator(); do { if (!iterator.hasNext()) break; List<?> altName = iterator.next(); int size = altName.size(); if (size >= 2) { identities.add((String) altName.get(1)); } } while (true); } catch (CertificateParsingException e) { e.printStackTrace(); } return identities; } /** * 返回所有可用的签名方式的值 * * @param certificate * @return */ public static List<String> getPeerIdentity(X509Certificate x509Certificate) { List<String> names = getSubjectAlternativeNames(x509Certificate); if (names.isEmpty()) { String name = x509Certificate.getSubjectDN().getName(); Matcher matcher = cnPattern.matcher(name); if (matcher.find()) { name = matcher.group(2); } names = new ArrayList<String>(); names.add(name); } return names; } }
public class TestX509CertManager { public static void main(String[] args) { try { SSLSocket baiduSocket = SSLX509CertificateManager.createTrustCASocket("www.baidu.com", 443); SSLSocket taobaoSocket = SSLX509CertificateManager.createTrustCASocket("www.taobao.com", 443); SSLSocket imququSocket = SSLX509CertificateManager.createTrustCASocket("imququ.com", 443); } catch (Exception e) { e.printStackTrace(); } } }
我们这里附加一个工具类,专门来实现Server-Side与Client-Side的SSLSocket 连接,也可以用于测试我们的上述代码,只不过需要稍加改造。
package com.tianwt.rx.http; public class SSLTrustManager implements javax.net.ssl.TrustManager, javax.net.ssl.X509TrustManager ,HostnameVerifier { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new X509Certificate[]{}; } public boolean isServerTrusted( java.security.cert.X509Certificate[] certs) { return true; } public boolean isClientTrusted( java.security.cert.X509Certificate[] certs) { return true; } public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException { return; } public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException { return; } @Override public boolean verify(String urlHostName, SSLSession session) { //允许所有主机 return true; } /** * 客户端使用 */ public static HttpURLConnection connectTrustAllServer(String strUrl) throws Exception { return connectTrustAllServer(strUrl,null); } /** * 客户端使用 * * @param strUrl 要访问的地址 * @param proxy 需要经过的代理 * @return * @throws Exception */ public static HttpURLConnection connectTrustAllServer(String strUrl,Proxy proxy) throws Exception { javax.net.ssl.TrustManager[] trustCertsmanager = new javax.net.ssl.TrustManager[1]; javax.net.ssl.TrustManager tm = new SSLTrustManager(); trustCertsmanager[0] = tm; javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext .getInstance("TLS"); sc.init(null, trustCertsmanager, null); javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc .getSocketFactory()); HttpsURLConnection.setDefaultHostnameVerifier((HostnameVerifier) tm); URL url = new URL(strUrl); HttpURLConnection urlConn = null; if(proxy==null) { urlConn = (HttpURLConnection) url.openConnection(); }else{ urlConn = (HttpURLConnection) url.openConnection(proxy); } urlConn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36"); return urlConn; } /** * 用于双向认证,客户端使用 * * @param strUrl * @param proxy * @return * @throws KeyStoreException * @throws NoSuchAlgorithmException * @throws CertificateException * @throws FileNotFoundException * @throws IOException * @throws UnrecoverableKeyException * @throws KeyManagementException */ public static HttpURLConnection connectProxyTrustCA(String strUrl,Proxy proxy) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException, UnrecoverableKeyException, KeyManagementException { HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String s, SSLSession sslsession) { return true; } }); String clientKeyStoreFile = "D:/JDK8Home/tianwt/sslClientKeys"; String clientKeyStorePwd = "123456"; String catServerKeyPwd = "123456"; String serverTrustKeyStoreFile = "D:/JDK8Home/tianwt/sslClientTrust"; String serverTrustKeyStorePwd = "123456"; KeyStore serverKeyStore = KeyStore.getInstance("JKS"); serverKeyStore.load(new FileInputStream(clientKeyStoreFile), clientKeyStorePwd.toCharArray()); KeyStore serverTrustKeyStore = KeyStore.getInstance("JKS"); serverTrustKeyStore.load(new FileInputStream(serverTrustKeyStoreFile), serverTrustKeyStorePwd.toCharArray()); KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(serverKeyStore, catServerKeyPwd.toCharArray()); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(serverTrustKeyStore); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); URL url = new URL(strUrl); HttpURLConnection httpURLConnection = null; if(proxy==null) { httpURLConnection = (HttpURLConnection) url.openConnection(); }else{ httpURLConnection = (HttpURLConnection) url.openConnection(proxy); } httpURLConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36"); return httpURLConnection; } /** * 用于单向认证,客户端使用 * * server侧只需要自己的keystore文件,不需要truststore文件 * client侧不需要自己的keystore文件,只需要truststore文件(其中包含server的公钥)。 * 此外server侧需要在创建SSLServerSocket之后设定不需要客户端证书:setNeedClientAuth(false) * @param strUrl * @param proxy * @return * @throws KeyStoreException * @throws NoSuchAlgorithmException * @throws CertificateException * @throws FileNotFoundException * @throws IOException * @throws UnrecoverableKeyException * @throws KeyManagementException */ public static HttpURLConnection connectProxyTrustCA2(String strUrl,Proxy proxy) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException, UnrecoverableKeyException, KeyManagementException { HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String s, SSLSession sslsession) { return true; } }); String serverTrustKeyStoreFile = "D:/JDK8Home/tianwt/sslClientTrust"; String serverTrustKeyStorePwd = "123456"; KeyStore serverTrustKeyStore = KeyStore.getInstance("JKS"); serverTrustKeyStore.load(new FileInputStream(serverTrustKeyStoreFile), serverTrustKeyStorePwd.toCharArray()); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(serverTrustKeyStore); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), null); HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); URL url = new URL(strUrl); HttpURLConnection httpURLConnection = null; if(proxy==null) { httpURLConnection = (HttpURLConnection) url.openConnection(); }else{ httpURLConnection = (HttpURLConnection) url.openConnection(proxy); } httpURLConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36"); return httpURLConnection; } /** * 用于双向认证 * @param socketClient 是否产生socket * @return * @throws KeyStoreException * @throws NoSuchAlgorithmException * @throws CertificateException * @throws FileNotFoundException * @throws IOException * @throws UnrecoverableKeyException * @throws KeyManagementException */ public SSLSocket createTlsConnect(Socket socketClient) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException, UnrecoverableKeyException, KeyManagementException { String protocol = "TLS"; String serverKey = "D:/JDK8Home/tianwt/sslServerKeys"; String serverTrust = "D:/JDK8Home/tianwt/sslServerTrust"; String serverKeyPwd = "123456"; //私钥密码 String serverTrustPwd = "123456"; //信任证书密码 String serverKeyStorePwd = "123456"; // keystore存储密码 KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(new FileInputStream(serverKey),serverKeyPwd.toCharArray()); KeyStore tks = KeyStore.getInstance("JKS"); tks.load(new FileInputStream(serverTrust), serverTrustPwd.toCharArray()); KeyManagerFactory km = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); km.init(keyStore, serverKeyStorePwd.toCharArray()); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(tks); SSLContext sslContext = SSLContext.getInstance(protocol); sslContext.init(km.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom()); //第一项是用来做服务器验证的 SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); SSLSocket clientSSLSocket = (SSLSocket) sslSocketFactory.createSocket(socketClient,socketClient.getInetAddress().getHostAddress(),socketClient.getPort(), true); clientSSLSocket.setNeedClientAuth(false); clientSSLSocket.setUseClientMode(false); return clientSSLSocket; } /** * 用于单向认证 * server侧只需要自己的keystore文件,不需要truststore文件 * client侧不需要自己的keystore文件,只需要truststore文件(其中包含server的公钥)。 * 此外server侧需要在创建SSLServerSocket之后设定不需要客户端证书:setNeedClientAuth(false) * @param socketClient * @return * @throws KeyStoreException * @throws NoSuchAlgorithmException * @throws CertificateException * @throws FileNotFoundException * @throws IOException * @throws UnrecoverableKeyException * @throws KeyManagementException */ public static SSLSocket createTlsConnect2(Socket socketClient) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException, UnrecoverableKeyException, KeyManagementException { String protocol = "TLS"; String serverKey = "D:/JDK8Home/tianwt/sslServerKeys"; String serverKeyPwd = "123456"; //私钥密码 String serverKeyStorePwd = "123456"; // keystore存储密码 KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(new FileInputStream(serverKey),serverKeyPwd.toCharArray()); KeyManagerFactory km = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); km.init(keyStore, serverKeyStorePwd.toCharArray()); SSLContext sslContext = SSLContext.getInstance(protocol); sslContext.init(km.getKeyManagers(), null, new SecureRandom()); //第一项是用来做服务器验证的 SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); SSLSocket clientSSLSocket = (SSLSocket) sslSocketFactory.createSocket(socketClient,socketClient.getInetAddress().getHostAddress(),socketClient.getPort(), true); clientSSLSocket.setNeedClientAuth(false); clientSSLSocket.setUseClientMode(false); return clientSSLSocket; } /** * 将普通的socket转为sslsocket,客户端和服务端均可使用 * * 服务端使用的时候是把普通的socket转为sslsocket,并且作为服务器套接字(注意:指的不是ServerSocket,当然ServerSocket的本质也是普通socket) * * @param remoteHost * @param isClient * @return */ public static SSLSocket getTlsTrustAllSocket(Socket remoteHost,boolean isClient) { SSLSocket remoteSSLSocket = null; SSLContext context = SSLTrustManager.getTrustAllSSLContext(isClient); try { remoteSSLSocket = (SSLSocket) context.getSocketFactory().createSocket(remoteHost, remoteHost.getInetAddress().getHostName(),remoteHost.getPort(), true); remoteSSLSocket.setTcpNoDelay(true); remoteSSLSocket.setSoTimeout(5000); remoteSSLSocket.setNeedClientAuth(false); //这里设置为true时会强制握手 remoteSSLSocket.setUseClientMode(isClient); //注意服务器和客户的角色选择 } catch (IOException e) { e.printStackTrace(); } return remoteSSLSocket; } /** * 用于客户端,通过所有证书验证 * @param isClient 是否生成客户端SSLContext,否则生成服务端SSLContext * @return */ public static SSLContext getTrustAllSSLContext(boolean isClient) { String protocol = "TLS"; javax.net.ssl.SSLContext sc = null; try { javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1]; javax.net.ssl.TrustManager tm = new SSLTrustManager(); trustAllCerts[0] = tm; sc = javax.net.ssl.SSLContext .getInstance(protocol); if(isClient) { sc.init(null, trustAllCerts, null); //作为客户端使用 } else { String serverKeyPath = "D:/JDK8Home/tianwt/sslServerKeys"; String serverKeyPwd = "123456"; //私钥密码 String serverKeyStorePwd = "123456"; // keystore存储密码 KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(new FileInputStream(serverKeyPath),serverKeyPwd.toCharArray()); KeyManagerFactory km = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); km.init(keyStore, serverKeyStorePwd.toCharArray()); KeyManager[] keyManagers = km.getKeyManagers(); keyManagers = Arrays.copyOf(keyManagers, keyManagers.length+1); sc.init(keyManagers, null, new SecureRandom()); } } catch (KeyManagementException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnrecoverableKeyException e) { e.printStackTrace(); } catch (KeyStoreException e) { e.printStackTrace(); } catch (CertificateException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return sc; } }
最近公司项目需要,网络协议支持HTTPS,之前接触不多,所以这次想总结一下https在Android开发中的相关内容
一、HTTPS 证书
对于HTTPS和证书的概念,大家可以自行搜索百度。
证书分两种:
1、花钱向认证机构购买的证书。服务器如果使用了此类证书的话,那对于移动端来说,直接可以忽略此证书,直接用https访问。与之不同的是ios内置了很多信任的证书,所以他们不需要做任何操作
2、另一种是自己制作的证书,使用此类证书的话是不受信任的,也不需要花钱,所以需要我们在代码中将此类证书设置为信任证书
二、如何忽略证书
注意,下面的操作在线上环境强烈不建议采纳,只能是在测试环境中解决测试问题的时候才能使用。
1、服务器如果加上了证书的话,那么你们的网络请求的url将从http:xx改成https:xx,如果你直接也将http改成https的话而什么也不做的话,客户端将直接报错,如图:
在常规的`Android`开发过程中,随着业务逻辑越来越复杂,调用栈可能会越来越深,难免会遇到调用栈越界的情况,这种情况下,就需要调整线程栈的大小。
当然,主要还是增大线程栈大小,尤其是存在`jni`调用的情况下,`C++`层的栈开销有时候是非常恐怖的,比如说递归调用。
这就需要分三种情况,主线程,自定义线程池,`AsyncTask`。
主线程的线程栈是没有办法进行修改的,这个没办法处理。
针对线程池的情况,需要在创建线程的时候,调用构造函数
public Thread(@RecentlyNullable ThreadGroup group, @RecentlyNullable Runnable target, @RecentlyNonNull String name, long stackSize)
通过设置`stackSize`参数来解决问题。
参考代码如下:
import android.support.annotation.NonNull; import android.util.Log; import java.util.concurrent.ThreadFactory; /** * A ThreadFactory implementation which create new threads for the thread pool. */ public class SimpleThreadFactory implements ThreadFactory { private static final String TAG = "SimpleThreadFactory"; private final static ThreadGroup group = new ThreadGroup("SimpleThreadFactoryGroup"); // 工作线程堆栈大小调整为2MB private final static int workerStackSize = 2 * 1024 * 1024; @Override public Thread newThread(@NonNull final Runnable runnable) { final Thread thread = new Thread(group, runnable, "PoolWorkerThread", workerStackSize); // A exception handler is created to log the exception from threads thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(@NonNull Thread thread, @NonNull Throwable ex) { Log.e(TAG, thread.getName() + " encountered an error: " + ex.getMessage()); } }); return thread; } }
import android.support.annotation.AnyThread; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * A Singleton thread pool */ public class ThreadPool { private static final String TAG = "ThreadPool"; private static final int KEEP_ALIVE_TIME = 1; private static volatile ThreadPool sInstance = null; private static int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors(); private final ExecutorService mExecutor; private final BlockingQueue<Runnable> mTaskQueue; // Made constructor private to avoid the class being initiated from outside private ThreadPool() { // initialize a queue for the thread pool. New tasks will be added to this queue mTaskQueue = new LinkedBlockingQueue<>(); Log.d(TAG, "Available cores: " + NUMBER_OF_CORES); mExecutor = new ThreadPoolExecutor(NUMBER_OF_CORES, NUMBER_OF_CORES * 2, KEEP_ALIVE_TIME, TimeUnit.SECONDS, mTaskQueue, new SimpleThreadFactory()); } @NonNull @AnyThread public static ThreadPool getInstance() { if (null == sInstance) { synchronized (ThreadPool.class) { if (null == sInstance) { sInstance = new ThreadPool(); } } } return sInstance; } private boolean isThreadPoolAlive() { return (null != mExecutor) && !mExecutor.isTerminated() && !mExecutor.isShutdown(); } @Nullable @AnyThread public <T> Future<T> submitCallable(@NonNull final Callable<T> c) { synchronized (this) { if (isThreadPoolAlive()) { return mExecutor.submit(c); } } return null; } @Nullable @AnyThread public Future<?> submitRunnable(@NonNull final Runnable r) { synchronized (this) { if (isThreadPoolAlive()) { return mExecutor.submit(r); } } return null; } /* Remove all tasks in the queue and stop all running threads */ @AnyThread public void shutdownNow() { synchronized (this) { mTaskQueue.clear(); if ((!mExecutor.isShutdown()) && (!mExecutor.isTerminated())) { mExecutor.shutdownNow(); } } } }
针对`AsyncTask`的情况,一般是通过调用
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params)
指定线程池来运行,在特定的线程池中调整线程栈的大小。
参考代码如下:
import android.os.AsyncTask; import android.support.annotation.AnyThread; import android.support.annotation.NonNull; import android.util.Log; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public abstract class AsyncTaskEx<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> { private static final String TAG = "AsyncTaskEx"; private static final int KEEP_ALIVE_TIME = 1; private static volatile ThreadPool sInstance = null; private static int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors(); private final ExecutorService mExecutor; private final BlockingQueue<Runnable> mTaskQueue; public AsyncTaskEx() { // initialize a queue for the thread pool. New tasks will be added to this queue mTaskQueue = new LinkedBlockingQueue<>(); Log.d(TAG, "Available cores: " + NUMBER_OF_CORES); mExecutor = new ThreadPoolExecutor(NUMBER_OF_CORES, NUMBER_OF_CORES * 2, KEEP_ALIVE_TIME, TimeUnit.SECONDS, mTaskQueue, new SimpleThreadFactory()); } public AsyncTask<Params, Progress, Result> executeAsync(@NonNull final Params... params) { return super.executeOnExecutor(mExecutor, params); } /* Remove all tasks in the queue and stop all running threads */ @AnyThread public void shutdownNow() { synchronized (this) { mTaskQueue.clear(); if ((!mExecutor.isShutdown()) && (!mExecutor.isTerminated())) { mExecutor.shutdownNow(); } } } }