使用AndroidKeyStore和Apache服务器实现HTTPS双向认证

打印服务器证书信息:

openssl s_client -connect www.baidu.com:443

AndroidKeyStore创建RSA的Keypair,并使用它进行签名和验证:

https://github.com/amitaymolko/react-native-rsa-native/blob/f106025e3d7724a25834e79b99561fdbd07dc389/android/src/main/java/com/RNRSA/RSA.java

查看PEM格式的证书信息:

openssl x509 -in certificate.pem -text -noout

查看DER格式的证书信息:

openssl x509 -in certificate.der -inform der -text -noout

创建双向认证的证书链:

1.产生公私钥对

openssl genrsa > root.key
openssl genrsa > server.key
openssl genrsa > client.key

2.生成根证书

openssl req -x509 -new -key root.key >root.crt

3.生成二级证书请求文件

openssl req -new -key server.key -out server.csr
openssl req -new -key client.key -out client.csr

4.为二级证书签名

openssl ca -in server.csr -cert root.crt -keyfile root.key -out ./server.crt
openssl ca -in client.csr -cert root.crt -keyfile root.key -out client.crt

此时会出错,解决方法:

mkdir -p ./demoCA/newcerts
touch ./demoCA/index.txt
touch ./demoCA/serial
echo '01' > ./demoCA/serial
cd ~
openssl rand -writerand .rnd

5.将客户端证书和密钥打包成pfx格式(可以导入浏览器)

openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.pfx

6.将客户端密钥导出为PKCS8格式(可以导入AndroidKeyStore)

openssl pkcs8 -topk8 -inform PEM -in client.key -outform PEM -nocrypt > client.pem

Apache服务器端配置双向认证:

1.修改/usr/local/apache2/conf/httpd.conf

LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule ssl_module modules/mod_ssl.so
Include conf/extra/httpd-ssl.conf

2.修改/usr/local/apache2/conf/extra/httpd-ssl.conf

ServerName 192.168.169.227:443
SSLCertificateFile "/usr/local/apache2/conf/server.crt"
SSLCertificateKeyFile "/usr/local/apache2/conf/server.key"
SSLCACertificateFile "/usr/local/apache2/conf/root.crt"
SSLVerifyClient require
SSLVerifyDepth 10

Android客户端实现双向认证:

public static void httpsDevPrivateKey() {
    new Thread(new Runnable(){
        @Override
        public void run() {
            URL url = null;
            try {
                // 证书格式设置为X.509,从浏览器下载的证书就是该格式,可以直接导入。
                CertificateFactory cf = CertificateFactory.getInstance("X.509");

                // 加载https://192.168.169.227的服务器证书
                InputStream caInput = new BufferedInputStream(new FileInputStream("/mnt/sdcard/227/server.crt"));
                Certificate ca;
                ca = cf.generateCertificate(caInput);
                System.out.println("服务器证书信息:" + ((X509Certificate) ca).getSubjectDN());
                caInput.close();

                // 加载客户端证书,证书格式为X.509。
                InputStream clientCaInput = new BufferedInputStream(new FileInputStream("/mnt/sdcard/client/client.crt"));
                Certificate clientCa;
                clientCa = cf.generateCertificate(clientCaInput);
                System.out.println("客户端证书信息:" + ((X509Certificate) clientCa).getSubjectDN());
                clientCaInput.close();

                // 加载客户端私钥,格式为PKCS8。
                // 注意:需要去掉-----BEGIN RSA PRIVATE KEY-----和-----END RSA PRIVATE KEY-----。
                InputStream clientKeyInput = new BufferedInputStream(new FileInputStream("/mnt/sdcard/client/client.pem"));
                byte[] clientKey = new byte[clientKeyInput.available()];
                clientKeyInput.read(clientKey);
                clientKeyInput.close();

                // 解析BASE64编码中的数据。
                clientKey = Base64.decode(clientKey, Base64.DEFAULT);

                // 解析PKCS8格式的私钥。
                KeyFactory keyFactory = KeyFactory.getInstance("RSA");
                PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(clientKey);
                PrivateKey clientPrivateKey = keyFactory.generatePrivate(keySpec);

                // 获取"AndroidKeyStore"。
                KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);
                keyStore.load(null, null);

                // 导入服务器的证书到AndroidKeyStore。
                keyStore.setCertificateEntry("test_ca", ca);

                // 导入客户端私钥和证书到AndroidKeyStore。
                keyStore.setKeyEntry("test_client", clientPrivateKey, null, new Certificate[]{clientCa});

                // 使用AndroidKeyStore初始化TrustyManagerFactory。
                String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
                TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
                tmf.init(keyStore);

                // 使用AndroidKeyStore初始化KeyManagerFactory。
                String kmfAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
                KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmfAlgorithm);
                kmf.init(keyStore, null);

                // 用AndroidKeyStore初始化过的TrustManagerFactory和KeyManagerFactory初始化SSLContext。
                SSLContext context = SSLContext.getInstance("TLS");
                context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

                url = new URL("https://192.168.169.227");
                HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();

                // 因为SSL只支持域名的认证,不支持IP的认证,所以这里我们手动认证。
                urlConnection.setHostnameVerifier(new HostnameVerifier()
                {
                    @Override
                    public boolean verify(String hostname, SSLSession session)
                    {
                        if(hostname.equals("192.168.169.227")) {
                            return true;
                        } else {
                            return false;
                        }
                    }
                });

                // 将https的连接绑定到AndroidKeyStore上。
                urlConnection.setSSLSocketFactory(context.getSocketFactory());

                // HTTPS双向认证的方式网站的数据。
                InputStream in = urlConnection.getInputStream();
                copyInputStreamToOutputStream(in, System.out);
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (CertificateException e) {
                e.printStackTrace();
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (KeyStoreException e) {
                e.printStackTrace();
            } catch (KeyManagementException e) {
                e.printStackTrace();
            } catch (UnrecoverableKeyException e) {
                e.printStackTrace();
            } catch (InvalidKeySpecException e) {
                e.printStackTrace();
            }
        }
    }).start();
}

private static void copyInputStreamToOutputStream(InputStream in, PrintStream out) throws IOException {
    byte[] buffer = new byte[4096];
    int readCount = 0;
    while ((readCount = in.read(buffer)) > 0) {
        out.write(buffer, 0, readCount);
    }
}

 

 

 

发表在 Android | 标签为 , , , , , | 留下评论

设置Linux开机启动项

  1. 写好脚本,比如xxx.sh,脚本中需要加入下列开头注释:
  2. 将脚本cp到/etc/init.d中,更改权限为755
  3. 添加自启动 update-rc.d xxx.sh defaults
  4. 移除自启动 update-rc.d -f xxx.sh remove
  5. 执行sudo find /etc/ -name “xxx.sh”,可以在/etc/rc2.d,/etc/rc3.d,/etc/rc4.d,/etc/rc5.d里面找到xxx.sh启动项的配置。

注意:

  • 脚本开头一定要有:

BEGIN INIT INFO
# Provides: update-ip
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: Update IP address of the Domain Name
### END INIT INFO

不然无法加入启动项且没有错误提示。

  • 脚本运行时不能阻塞,否则重启后无法正常开机。

 

 

发表在 未分类 | 留下评论

RAR文件忘记密码

git clone https://github.com/ziman/rarcrack.git
sudo apt-get install libxml2-dev libxslt-dev
cd rarcrack
make
./rarcrack ./test.rar

 

发表在 效率工具 | 标签为 | 留下评论

doxygen学习笔记

生成默认的layout文件:

doxygen -l

设置不显示头文件的源码:
VERBATIM_HEADERS = NO

设置输入的目录:
INPUT = ./tee

设置图片的目录:
IMAGE_PATH = ./tee/img

设置Layout文件目录:
LAYOUT_FILE = xxx_layout.xml

设置C语言优化:
OPTIMIZE_OUTPUT_FOR_C = YES

设置图标:
PROJECT_LOGO = ./common/xxx_icon.png

在markdown中使用doxygen命令:
@addtogroup xxx Framework API

在markdown中做页内跳转:
<a href=”#1″>111</a>
#### <a name=”1″>xxx Architecture</a> ####

缩进:
>
>>

原点号:
*

让Latex支持中文:
sudo apt-get install latex-cjk-all

2) 用文本编辑器打开docxygen生成的latex目录中的refman.tex。找到“\begin{document}”这一行,将其修改为——

\usepackage{CJKutf8}
\begin{document}
\begin{CJK}{UTF8}{gbsn}

然后再找到“\end{document}”这一行,将其修改为——

\end{CJK}
\end{document}

用命令:
sed -i -e ‘s,begin{document},usepackage{CJKutf8}\n\\begin{document}\n\\begin{CJK}{UTF8}{gbsn},’ refman.tex
sed -i -e ‘s,end{document},end{CJK}\n\\end{document},’ refman.tex

让Latex显示markdown中的图片:
@image xxx_arch.eps “People image” width=\textwidth

发表在 效率工具 | 标签为 | 留下评论

OPTEE学习笔记

sudo apt-get install android-tools-adb android-tools-fastboot autoconf \
automake bc bison build-essential cscope curl device-tree-compiler flex \
ftp-upload gdisk iasl libattr1-dev libc6:i386 libcap-dev libfdt-dev \
libftdi-dev libglib2.0-dev libhidapi-dev libncurses5-dev \
libpixman-1-dev libssl-dev libstdc++6:i386 libtool libz1:i386 make \
mtools netcat python-crypto python-serial python-wand unzip uuid-dev \
xdg-utils xterm xz-utils zlib1g-dev
cd build
make
make run
Fetching project linaro-swg/linux.git
remote: Counting objects: 6245264, done. 
remote: Compressing objects: 100% (1132/1132), done. 
error: index-pack died of signal 9245264), 2.01 GiB | 9.59 MiB/s 
fatal: index-pack failed
^Caborted by user
git config –global pack.windowMemory 10m
git config –global pack.packSizeLimit 20m
发表在 操作系统 | 标签为 | 留下评论

scons学习笔记

常用的命令行选项:
  • 构建工程并输出所有的构建命令。(如果不使用Default方法设置默认目标,则构建所有目标,这点和makefile不一样)
scons
  • 构建工程不输出构建命令。
scons -Q
  • 构建名称为bar的目标。
scons bar
  • 显示目标被构建的原因。
scons –debug=explain
  • 输出依赖关系图。
scons –tree=all
  • 显示如何生成构建命令。
scons –debug=presub
  • 显示构建过程引用的库的路径。
scons –debug=findlibs
  • 显示各个目标是否需要更新。
scons –debug=prepare
scons构建过程的两个阶段:
  1. 建立依赖关系图,在此阶段不执行任何构建命令。(scons的设计思想是只存在一个依赖关系图,和makefile不同)
  2. 根据依赖关系图构建工程。
scons三种环境变量:
  1. 外部环境(External Environment)变量,执行scons命令的shell环境中的变量,可以使用os.environ(一个外部环境变量字典)来获取。
  2. 构建环境(Construction Environment)变量,构建环境在scons中表现为Environment对象,在scons中,可以很方便的创建或克隆出不同的构建环境,构建环境变量主要是一些编译过程中用到的配置选项($CPPPATH、$CPPFLAGS、$CPPDEFINES等)。可以使用Environment对象访问,例如env[‘CPPPATH’]。
  3. 执行环境(Execution Environment)变量,执行构建命令的环境中的变量,scons的执行环境和外部环境是隔离开的,从而确保不会因为外部环境的一些错误配置发生不好追踪的错误,最大限度保证正确性。(makefile中外部环境会影响执行环境)可以使用env[‘ENV’]访问,例如env[‘ENV’][‘PATH’]。
scons判断文件过期的方式:
  • Decider(‘MD5’),使用md5比较,优点是比较准确,缺点速度慢,适用于规模较小的项目。
  • Decider(‘timestamp-newer’),使用timestamp比较,优点是速度快,缺点是可能不准确,适用于规模较大的项目。
  • Decider(‘MD5-timestamp’),使用md5和timestamp比较,先使用timestamp检测出疑似过期的文件,然后使用md5进行比较,是上面两种方案的折中。
  • 自定义Decider的方式。
提高性能的方法–implicit-cache:
  • 使用该选项会使scons忽略对搜索路径的修改($CPPPATH、$LIBPATH等)。
  • 使用该选项会使scons忽略那些变更日期早于原文件的文件。
scons的两种构建脚本(SConstruct和SConscript):
根据构建过程使用一个依赖关系图的设计原则
  • SConstruct只能有一个,它是最顶层的构建脚本。
  • SConscript不能单独执行,只能由SConstruct或其他的SConscript调用执行。
使用VariantDir和SConscript设置构建目录:
  • 默认的情况下,scons会把编译的结果直接放在源码目录,也就是构建目录就是源码目录。
  • 使用VariantDir指定的构建目录只对SConscript所在目录之下的目录有效。
  • SConscript中的相对路径是相对于SConscript所在目录的,在设置VariantDir之后,相对路径自动变为相对于VariantDir,所以在SConscript中配置的源代码文件的路径最好是相对路径,这样VariantDir如果变更,不用修改SConscript。
常用scons函数和相关构造变量:
env.StaticObject()
  • 创建静态目标文件,主要用于生成静态库,它所生成的目标不能用于动态库的创建,scons内部有检查机制来确保这点。内置scanner会自动扫描依赖的头文件,但不能扫描宏定义的头文件引用,这种情况需要使用ParseDepends来添加依赖关系。
  • 相关的内置构建环境变量:
CPPDEFINES    宏定义
CPPPATH          头文件目录,scons内置的scanner会根据该变量中定义的目录自动扫描源码所依赖的头文件,所以不要将头文件目录用-I的形式手动写到CPPFLAGS中。
CPPFLAGS        编译选项
上述变量会应用于C、C++和汇编文件。
CFLAGS
只会影响C文件
CXXFLAGS
只会影响C++文件
ASFLAGS
只会影响汇编文件
CCFLAGS
会影响C和C++文件
CC              C编译器
CCCOM      C编译命令
CXX            C++编译器
CXXCOM     C++编译命令
AS               汇编器
ASCOM       汇编编译命令
env.StaticLibrary()
  • 创建静态库
  • 相关的内置构建环境变量:
ARFLAGS    打包命令选项
AR               打包程序
ARCOM       打包命令
RANLIBFLAGS
RANLIB
RANLIBCOM
env.Program()
  • 连接并创建可执行程序
  • 相关的内置构建环境变量:
LINKFLAGS   连接时用到的选项
LIBS               连接时用到的库,不要在LINKFLAGS中以-l的形式添加库,否则scons不能自动生成对库的依赖关系。
LIBPATH        库的搜索路径,不要在LINKFLAGS中以-L的形式添加库的搜索路径,否则scons不能自动生成对库的依赖关系。
LINK               连接器
LINKCOM       连接命令
发表在 操作系统 | 标签为 | 留下评论