CentOS 6.9 OpenVpn搭建流程

公司内网环境需要外网访问,同事需兼顾数据传输以及连接上的安全性,在外网IP网关不固定的情况下,需达到在任何外网(无连接限制)都能连接到公司内网环境,并正常访问。


方案原理

OpenVpn + Mysql + Pam插件

虚拟专用网VPN(virt ual private network)是在公共网络中建立的安全网络连接,这个网络连接和普通意义上的网络连接不同之处在于,它采用了专有的隧道协议,实现了数据的加密和完整性的检验、用户的身份认证,从而保证了信息在传输中不被偷看、篡改、复制,从网络连接的安全性角度来看,就类似于再公共网络中建立了一个专线网络一样,只补过这个专线网络是逻辑上的而不是物理的所以称为虚拟专用网。

通过OpenVpn实现隧道的建立,Mysql+Pam_Mysql插件实现用户名密码验登录,实现多用户配置登录配置。去除一对一生成认证文件的繁琐操作。


安装前准备


安装环境

Server端

  • 操作系统:CentOS 6.9 64位
  • 内核版本:2.6.32-696.el6.x86_64

模块支持

OpenVpn的安装需要tun模块的支持,命令如下:

$ ls mod | grep tun                
# 查看系统是否支持tun模块
$ modinfo tun                      
# 查看模块信息
$ /etc/init.d/iptables             
# 查看iptables是否存在

依赖包安装

$ yum install openssl-devel openssl gcc gcc-c++ cmake -y 

开启路由转发功能

$ vim /etc/profile
    net.ipv4.ip_forward = 0 
    # 修改为 
    net.ipv4.ip_forward = 1
$ sysctl -p         
# 使配置生效

安装包下载

下载服务端(tar.gz)和windows客户端(*.exe)最好是保持版本的一致性

下载地址:openvpn-2.4.6.tar.gz


服务端安装


安装LZO

LZO 是致力于解压速度的一种数据压缩算法,LZO 是 Lempel-Ziv-Oberhumer 的缩写。这个算法是无损算法,参考实现程序是线程安全的。
实现它的一个自由软件工具是lzop。最初的库是用 ANSI C 编写、并且遵从 GNU通用公共许可证发布的。现在 LZO 有用于 Perl、Python 以及 Java 的各种版本。代码版权的所有者是 Markus F. X. J. Oberhumer。

LZO 库实现了许多有下述特点的算法:
• 解压简单,速度非常快。
• 解压不需要内存。
• 压缩相当地快。
• 压缩需要 64 kB 的内存。
• 允许在压缩部分以损失压缩速度为代价提高压缩率,解压速度不会降低。
• 包括生成预先压缩数据的压缩级别,这样可以得到相当有竞争力的压缩比。
• 另外还有一个只需要 8 kB 内存的压缩级别。
• 算法是线程安全的。
• 算法是无损的。

下载地址:lzo-2.10.tar.gz

$ mkdir -p /usr/src/software
$ cd /usr/src/software
$ wget http://www.oberhumer.com/opensource/lzo/download/lzo-2.10.tar.gz
$ tar -xzvf lzo-2.10.tar.gz
$ cd /usr/src/software/lzo-2.0
$ ./configure --prefix=/usr/local/lzo   
# 指定安装目录
$ make              
# 编译
$ make check        
# 编译检查
$ make install  
# 安装 

安装OpenVpn

$ cd /usr/src/software
$ tar -xzvf openvpn-2.4.6.tar.gz
$ cd openvpn-2.4.6
$ ./configure
# 执行以上命令之后,安装报错:
#checking for lz4.h... no
#usable LZ4 library or header not found, using version in src/compat/compat-lz4.*
#checking git checkout... no
#configure: error: lzo enabled but missing  
$ yum -y install lzo-devel
#又报错:
#usable LZ4 library or header not found, using version in src/compat/compat-lz4.*
#checking git checkout... no
#checking whether the compiler acceppts -Wno-unused-function... yes
#checking whether the compiler acceppts -Wno-unused-parameter... yes
#checking whether the compiler acceppts -Wall... yes
#configure: error: libpam required but missing
$ yum -y install pam-devel
$ ./configure
——————————————————————————————————————————————————
\\以下为配置的输出内容
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking for style of include used by make... GNU
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables... 
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking whether gcc understands -c and -o together... yes
checking dependency style of gcc... gcc3
checking how to run the C preprocessor... gcc -E
............\\Openssl版本最好在安装之前进行下升级,版本至少是0.9.6
checking whether TUNSETPERSIST is declared... yes
checking for setcon in -lselinux... yes
checking for pam_start in -lpam... yes
checking for PKCS11_HELPER... no
checking for OPENSSL... yes
checking for SSL_CTX_new... yes
checking for EVP_CIPHER_CTX_set_key_length... yes
checking for ENGINE_load_builtin_engines... yes
checking for ENGINE_register_all_complete... yes
checking for ENGINE_cleanup... yes
............\\检查lzo文件,如果这边没有通过,则无法安装OpenVpn
checking for lzo1x_1_15_compress in -llzo2... yes
checking lzo/lzoutil.h usability... yes
checking lzo/lzoutil.h presence... yes
checking for lzo/lzoutil.h... yes
checking lzo/lzo1x.h usability... yes
checking lzo/lzo1x.h presence... yes
checking for lzo/lzo1x.h... yes
............\\最后一段输出
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating version.sh
config.status: creating Makefile
config.status: creating build/Makefile
config.status: creating build/msvc/Makefile
config.status: creating build/msvc/msvc-generate/Makefile
config.status: creating distro/Makefile
config.status: creating distro/rpm/Makefile
config.status: creating distro/rpm/openvpn.spec
config.status: creating distro/systemd/Makefile
config.status: creating include/Makefile
config.status: creating src/Makefile
config.status: creating src/compat/Makefile
config.status: creating src/openvpn/Makefile
config.status: creating src/openvpnserv/Makefile
config.status: creating src/plugins/Makefile
config.status: creating src/plugins/auth-pam/Makefile
config.status: creating src/plugins/down-root/Makefile
config.status: creating tests/Makefile
config.status: creating tests/unit_tests/Makefile
config.status: creating tests/unit_tests/example_test/Makefile
config.status: creating tests/unit_tests/openvpn/Makefile
config.status: creating tests/unit_tests/plugins/Makefile
config.status: creating tests/unit_tests/plugins/auth-pam/Makefile
config.status: creating vendor/Makefile
config.status: creating sample/Makefile
config.status: creating doc/Makefile
config.status: creating tests/t_client.sh
config.status: creating config.h
config.status: config.h is unchanged
config.status: creating include/openvpn-plugin.h
config.status: include/openvpn-plugin.h is unchanged
config.status: executing depfiles commands
config.status: executing libtool commands
——————————————————————————————————————————————————
$ echo $?
0
$ make                  
# 编译
$ make install      
# 最后一步,安装       
\\最后几行输出
# /bin/mkdir -p '/usr/local/share/doc/openvpn'
# /usr/bin/install -c -m 644 README README.IPv6 README.mbedtls Changes.rst COPYRIGHT.GPL COPYING '/usr/local/share/doc/openvpn'
#make[3]: Leaving directory `/usr/src/software/openvpn-2.4.6'
#make[2]: Leaving directory `/usr/src/software/openvpn-2.4.6'
#make[1]: Leaving directory `/usr/src/software/openvpn-2.4.6'

生成服务器证书


在OpenVpn官网了解到,从openvpn2.3.x之后,easy-rsa不再捆绑到安装包里了,而是分开下载,所以需要下载单独的easy-rsa,之后再配置。

下载地址: easy-rsa-3.0.5.zip

$ unzip easy-rsa-3.0.5.zip

$ mv easy-rsa-3.0.5 openvpn-2.4.6
$ /usr/src/software/openvpn-2.4.6/easy-rsa-3.0.5/easyrsa3   
# 进入目录
$ cp vars.example vars      
# 一般情况下,默认的配置文件可以满足需求,也可以根据需要修改 
$ vim vars
# 修改以下内容
———————————开始———————————
set_var EASYRSA "`pwd`"
set_var EASYRSA_OPENSSL "openssl"
set_var EASYRSA_OPENSSL "openssl"
set_var EASYRSA_REQ_COUNTRY     "CN"
set_var EASYRSA_REQ_PROVINCE    "Beijing"
set_var EASYRSA_REQ_CITY        "Chaoyang"
set_var EASYRSA_REQ_ORG         "Copyleft Certificate Co"
set_var EASYRSA_REQ_EMAIL       "liuxiangyu@91caiyu.in"
set_var EASYRSA_REQ_OU          "My Organizational Unit"
set_var EASYRSA_KEY_SIZE        2048
# In how many days should the root CA key expire?
set_var EASYRSA_CA_EXPIRE       3650
# In how many days should certificates expire?
set_var EASYRSA_CERT_EXPIRE     3650
———————————结束———————————
生成服务端证书
$ pwd
/usr/src/software/openvpn-2.4.6/easy-rsa-3.0.5/easyrsa3
$ ./easyrsa init-pki    
# 建立一个空的pki结构,生成一系列的文件和目录
# 初始化,会在当前目录创建PKI目录,用于存储一些中间变量及最终生成的证书 
    Note: using Easy-RSA configuration from: ./vars

    init-pki complete; you may now create a CA or requests.
    Your newly created PKI dir is: /usr/src/software/openvpn-2.4.6/easy-rsa-3.0.5/easyrsa3/pki

$ ./easyrsa build-ca    
# 创建根证书ca  密码 和 cn需要记住 
# 创建根证书,首先会提示设置密码,用于ca对之后生成的server和client证书签名时使用,然后会提示设置Country Name,State or Province Name,Locality Name,Organization Name,Organizational Unit Name,Common Name,Email Address,可以键入回车使用默认的,也可以手动更改 
    Note: using Easy-RSA configuration from: ./vars
    Enter New CA Key Passphrase:                                #123456
    Re-Enter New CA Key Passphrase:                             #123456      #此密码需要记住,后续还会使用
    Generating RSA private key, 2048 bit long modulus
    .................................................................................................................................................................................................+++
    ..............................................................................................................+++
    e is 65537 (0x10001)
    You are about to be asked to enter information that will be incorporated
    into your certificate request.
    What you are about to enter is what is called a Distinguished Name or a DN.
    There are quite a few fields but you can leave some blank
    For some fields there will be a default value,
    If you enter '.', the field will be left blank.
    -----
    Common Name (eg: your user, host, or server name) [Easy-RSA CA]:                #mycompany   

    CA creation complete and you may now import and sign cert requests.
    Your new CA certificate file for publishing is at:
    /usr/src/software/openvpn-2.4.6/easy-rsa-3.0.5/easyrsa3/pki/ca.crt

$ ./easyrsa gen-req server nopass   
# 创建服务端证书  common name 最好不要跟前面的cn那么一样
# 创建server端证书和private key,nopass表示不加密private key,然后会提示设置Country Name,State or Province Name,Locality Name,Organization Name,Organizational Unit Name,Common Name,Email Address,可以键入回车使用默认的,也可以手动更改 
    Note: using Easy-RSA configuration from: ./vars
    Generating a 2048 bit RSA private key
    ...............+++
    ..................................+++
    writing new private key to '/usr/src/software/openvpn-2.4.6/easy-rsa-3.0.5/easyrsa3/pki/private/server.key.Yv4YWmpFoG'
    -----
    You are about to be asked to enter information that will be incorporated
    into your certificate request.
    What you are about to enter is what is called a Distinguished Name or a DN.
    There are quite a few fields but you can leave some blank
    For some fields there will be a default value,
    If you enter '.', the field will be left blank.
    -----
    Common Name (eg: your user, host, or server name) [server]:             #original    

    Keypair and certificate request completed. Your files are:
    req: /usr/src/software/openvpn-2.4.6/easy-rsa-3.0.5/easyrsa3/pki/reqs/server.req
    key: /usr/src/software/openvpn-2.4.6/easy-rsa-3.0.5/easyrsa3/pki/private/server.key

签约服务端证书

$ ./easyrsa sign server server      
# 签约服务端证书
# 给server端证书做签名,首先是对一些信息的确认,可以输入yes,然后输入build-ca时设置的那个密码 
$ ./easyrsa gen-dh      
# 创建Diffie-Hellman
# 创建Diffie-Hellman,时间会有点长,耐心等待 
    Note: using Easy-RSA configuration from: ./vars
    Generating DH parameters, 2048 bit long safe prime, generator 2
    This is going to take a long time
    .....................+..................................................................................
    ..+.................+..................................................++*++*

    DH parameters of size 2048 created at /usr/src/software/openvpn-2.4.6/easy-rsa-3.0.5/easyrsa3/pki/dh.pem
生成客户端证书

在本文架构环境中,是不需要生成一对一的客户端证书的,此步骤只作为扩展

# 创建client端证书,需要单独把easyrsa3文件夹拷贝出来一份,删除里面的PKI目录,然后进入到此目录 
$ cd ~ 
$ mkdir client && cd client
$ cp -R /usr/src/software/easy-rsa-3.0.5 ./
$ cd easy-ras-3.0.5/easyrsa3 
$ cp vars.example vars

# 开始生成
$ ./easyrsa init-pki
# 初始化,会在当前目录创建PKI目录,用于存储一些中间变量及最终生成的证书 
$ ./easyrsa gen-req liuxy       \\用自己的名字,需要创建一个密码  和 cn name,自己用的 需要记住
    Note: using Easy-RSA configuration from: ./vars
    Generating a 2048 bit RSA private key
    .......+++
    ....................................................................+++
    writing new private key to '/root/client/easy-rsa-3.0.5/easyrsa3/pki/private/liuxy.key.hweu50ee1N'
    Enter PEM pass phrase:                                  \\445566
    Verifying - Enter PEM pass phrase:                      \\445566        #需要记住,可能连接的时候会用
    -----
    You are about to be asked to enter information that will be incorporated
    into your certificate request.
    What you are about to enter is what is called a Distinguished Name or a DN.
    There are quite a few fields but you can leave some blank
    For some fields there will be a default value,
    If you enter '.', the field will be left blank.
    -----
    Common Name (eg: your user, host, or server name) [liuxy]:                  \\niklaus

    Keypair and certificate request completed. Your files are:
    req: /root/client/easy-rsa-3.0.5/easyrsa3/pki/reqs/liuxy.req
    key: /root/client/easy-rsa-3.0.5/easyrsa3/pki/private/liuxy.key

至此,客户端的证书已经成功生成,后续步骤需要签约

客户端签约

$ cd /usr/src/software/openvpn-2.4.6
$ mv easy-rsa-3.0.5/ /etc/openvpn/
$ cp sample/sample-config-files/server.conf /etc/openvpn/
$ ./easyrsa import-req ~/client/easy-rsa-3.0.5/easyrsa3/pki/reqs/liuxy.req  liuxy       \\导入req
#               Note: using Easy-RSA configuration from: ./vars
#   The request has been successfully imported with a short name of: liuxy
#   You may now use this name to perform signing operations on this request.
$ ./easyrsa sign client liuxy                                           \\用户签约,根据提示输入服务端的ca密码
Note: using Easy-RSA configuration from: ./vars

You are about to sign the following certificate.
Please check over the details shown below for accuracy. Note that this request
has not been cryptographically verified. Please be sure it came from a trusted
source or that you have verified the request checksum with the sender.

Request subject, to be signed as a client certificate for 3650 days:

subject=
    commonName                = niklaus


Type the word 'yes' to continue, or any other input to abort.
  Confirm request details: yes
Using configuration from /etc/openvpn/easy-rsa-3.0.5/easyrsa3/openssl-easyrsa.cnf
Enter pass phrase for /etc/openvpn/easy-rsa-3.0.5/easyrsa3/pki/private/ca.key:              \\123456
Check that the request matches the signature
Signature ok
The Subjects Distinguished Name is as follows
commonName            :PRINTABLE:'niklaus'
Certificate is to be certified until Jun 26 15:03:54 2028 GMT (3650 days)

Write out database with 1 new entries
Data Base Updated

Certificate created at: /etc/openvpn/easy-rsa-3.0.5/easyrsa3/pki/issued/liuxy.crt
查看服务端生成的所有证书文件
$ tree pki 
pki
├── ca.crt
├── certs_by_serial
│   ├── 0576F004D6ECB014B5C946C05DC88173.pem
│   └── 62014DDC889192A89E19F517AE73546C.pem
├── dh.pem
├── index.txt
├── index.txt.attr
├── index.txt.attr.old
├── index.txt.old
├── issued
│   ├── liuxy.crt
│   └── server.crt
├── private
│   ├── ca.key
│   └── server.key
├── reqs
│   ├── liuxy.req
│   └── server.req
├── serial
└── serial.old

* 客户端 
$ tree pki 
pki
├── private
│   └── liuxy.key
└── reqs
    └── liuxy.req

把证书文件放到统一目录下,方便查找

服务端证书

$ cp /etc/openvpn/easy-rsa-3.0.5/easyrsa3/pki/ca.crt /etc/openvpn/
$ cp /etc/openvpn/easy-rsa-3.0.5/easyrsa3/pki/private/server.key /etc/openvpn/
$ cp /etc/openvpn/easy-rsa-3.0.5/easyrsa3/pki/issued/server.crt /etc/openvpn/
$ cp /etc/openvpn/easy-rsa-3.0.5/easyrsa3/pki/dh.pem /etc/openvpn/

客户端证书

$ mkdir /liuxy/client
$ cp /etc/openvpn/easy-rsa-3.0.5/easyrsa3/pki/ca.crt /liuxy/client/
$ cp /etc/openvpn/easy-rsa-3.0.5/easyrsa3/pki/issued/liuxy.crt /liuxy/client/
$ cp ~/client/easy-rsa-3.0.5/easyrsa3/pki/private/liuxy.key /liuxy/client/

至此,证书安装、生成、认证完成 !

注意:生成客户端证书步骤可忽略,因为在openvpn+mysql+pam插件认证的环境下,是不需要配置一对一客户端证书的。

OpenVpn配置文件

$ cd /etc/openvpn/
$ cp server.conf server.conf.original
$ vim server.conf           

** 服务端配置文件见最后,因为中间出现过好多报错

启动OpenVpn

$ /usr/local/sbin/openvpn --daemon --config /etc/openvpn/server.conf &          
# 启动
$ tail -f openvpn.log 
    #Fri Jun 29 16:21:58 2018 Could not determine IPv4/IPv6 protocol. Using AF_INET
    #Fri Jun 29 16:21:58 2018 Socket Buffers: R=[87380->87380] S=[16384->16384]
    #Fri Jun 29 16:21:58 2018 Listening for incoming TCP connection on [AF_INET][undef]:11194
    #Fri Jun 29 16:21:58 2018 TCPv4_SERVER link local (bound): [AF_INET][undef]:11194
    #Fri Jun 29 16:21:58 2018 TCPv4_SERVER link remote: [AF_UNSPEC]
    #Fri Jun 29 16:21:58 2018 MULTI: multi_init called, r=256 v=256
    #Fri Jun 29 16:21:58 2018 IFCONFIG POOL: base=10.8.0.4 size=62, ipv6=0
    #Fri Jun 29 16:21:58 2018 IFCONFIG POOL LIST
    #Fri Jun 29 16:21:58 2018 MULTI: TCP INIT maxclients=100 maxevents=104
    #Fri Jun 29 16:21:58 2018 Initialization Sequence Completed

OpenVpn能成功启动,说明已经配置成功。如果有报错信息,可根据日志查看解决。


Mysql+Pam插件安装


数据库的安装,采用的yum安装方式

$ yum -y install epel-release
$ yum install -y mysql mysql-devel mysql-server
$ yum install -y pam_krb5 pam_mysql pam pam-devel
$ yum install -y cyrus-sasl cyrus-sasl-plain cyrus-sasl-devel cyrus-sasl-lib cyrus-sasl-gssapi
$ service mysqld start
$ mysql 
> create database vpn;
> grant all privileges on vpn.* to 'vpn'@'localhost' identified by '111213';
> grant all privileges on vpn.* to 'vpn'@'%' identified by '111213';
> flush privileges;
> CREATE TABLE vpnuser ( name char(20) NOT NULL, password char(128) default NULL, active int(10) NOT NULL DEFAULT 1, PRIMARY KEY (name)  );

$ update mysql.user set password=PASSWORD('111213') where user='root';      
# 为root用户添加密码,增加安全性

Mysql数据库创建OpenVpn使用的表和用户,为保证服务的连通性,请确保Vpn连接账户的权限为可执行。

Pam插件配置测试

OpenVpn的用户密码认证方式是通过此插件来实现。我前几次安装都是认证失败。后来发现是版本的问题。
插件的认证文件是需要手动make生成的,我采用OpenVpn-2.0.9版本中plugin来生成的openvpn-auth-pam.so认证文件

下载地址:openvpn-2.0.9.tar.gz

$ pwd
/usr/src/software/openvpn-2.0.9/plugin/auth-pam
$ make 

将生成的openvpn-auth-pam.so文件复制到/etc/openvpn/目录下

配置Pam_mysql模块

手动创建/etc/pam.d/openvpn文件

$ vim /etc/pam.d/openvpn
auth sufficient pam_mysql.so user=vpn passwd=caiyu111213 host=localhost db=vpn table=vpnuser usercolumn=name passwdcolumn=password where=active=1 sqllog=0 crypt=2
account required pam_mysql.so user=vpn passwd=caiyu111213 host=localhost db=vpn table=vpnuser usercolumn=name passwdcolumn=password where=active=1 sqllog=0 crypt=2

测试认证

进行插件的测试认证之前,请确定已经配置好配置文件,并且已经重新启动服务。有时候出现问题就可能是因为服务修改配置后没有重启导致。

$ testsaslauthd -u liuxy -p 111213 -s openvpn
0: OK "Success."

出现OK说明认证成功

OpenVpn配置文件添加认证模块

认证模块配置没问题之后,需要在openvpn配置文件中添加模块调用,如下:

client-cert-not-required
plugin /etc/openvpn/openvpn-auth-pam.so openvpn

OpenVpn配置文件详细

$ cat /etc/openvpn/server.conf | grep -v "^#"
local a.b.c.d
port 11194                                                  
proto tcp
;proto udp
;dev tap
dev tun
;dev-node MyTap
ca /etc/openvpn/ca.crt
cert /etc/openvpn/server.crt
key /etc/openvpn/server.key  # This file should be kept secret
dh /etc/openvpn/dh.pem
;topology subnet
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
;server-bridge 10.8.0.4 255.255.255.0 10.8.0.50 10.8.0.100
;server-bridge
;push "route 192.168.10.0 255.255.255.0"
;push "route 192.168.20.0 255.255.255.0"
push "route 172.16.16.0 255.255.255.0"
push "route 172.16.16.10 255.255.255.255"
push "route 172.16.16.11 255.255.255.255"
push "route 172.16.16.12 255.255.255.255"
;client-config-dir ccd
;route 192.168.40.128 255.255.255.248
;client-config-dir ccd
;route 10.9.0.0 255.255.255.252
;learn-address ./script
;push "redirect-gateway def1 bypass-dhcp"
;push "dhcp-option DNS 208.67.222.222"
;push "dhcp-option DNS 208.67.220.220"
client-to-client
duplicate-cn
keepalive 10 120
;tls-auth ta.key 0 # This file is secret
;cipher AES-256-CBC
;compress lz4-v2
;push "compress lz4-v2"
comp-lzo
max-clients 100
;user nobody
;group nobody
persist-key
persist-tun
status /etc/openvpn/openvpn-status.log
log         /etc/openvpn/openvpn.log
log-append  /etc/openvpn/openvpn.log
verb 3
;mute 20
;explicit-exit-notify 1
client-cert-not-required
plugin /etc/openvpn/openvpn-auth-pam.so openvpn

windows客户端安装配置


windows客户端安装,下载安装包之后傻瓜式安装。值得一提的是,openvpn的认证文件配置以及ca证书的安装。

客户端只需要2个文件:

  • ca.crt
  • client.ovpn

ca.crt需要从服务端机器下载,保存到openvpn客户端的安装目录config/目录下
client.ovpn同样需要放到config/目录下,具体配置如下:

$ cat client.ovpn | grep -v "^#"
client
;dev tap
dev tun
;dev-node MyTap
proto tcp
;proto udp
remote xxx.xxx.xxx.xxx 11194
# 此处配置公司局域网外网IP地址或者域名地址用于连接到局域网
;remote my-server-2 1194
;remote-random
resolv-retry infinite
;user nobody
;group nobody
persist-key
persist-tun
;http-proxy-retry # retry on connection failures
;http-proxy [proxy server] [proxy port #]
;mute-replay-warnings
ca ca.crt
auth-user-pass
ns-cert-type server
;tls-auth ta.key 1
;cipher x
comp-lzo
verb 3
;mute 20

配置文件每一项的具体意义都必须清楚,才能配置正确


网络疏通以及IPtables


大部分时候,openvpn安装到这里,此时连接时仍然会无法正常连接,因为上文提到,大体环境下是外网访问内网。外网访问局域网内,要想联通,必须有外网地址+端口映射,客户端程序才能找到连接入口。

针对这种情况,可以提供两种方法使用:

  • ssh链路端口转发
  • 固定外网地址添加端口映射

我安装的环境下,路由器有固定的外网地址,所以我只需要把内网OpenVpn服务的11194端口映射到路由器外网IP地址,并指定端口就可以了。具体的实现方式,我就不细说了,每个人环境不同,操作步骤也不一样。但原理都相同

SSH链路转发方式,是通过指定外网服务器的固定IP来添加端口转发,实现端口映射。具体命令格式如下:

ssh -C -f -N -g -R 11194:内网IP:11193 root@外网IP地址 -o ServerAliveInterval=360 -o ServerAliveCountMax=5000
# 尽量不要使用root账户

通过端口映射成功连接到内网OpenVpn服务器之后,无法ping通内网其他服务器怎么办。还需要在openvpn服务器添加地址转发,从而通过客户端连接后,打通内网(局域网内)所有机器,iptables命令如下:

$ iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

各种方面都要考虑,其实比较有意思,哈哈😄

安装总结

OpenVpn的安装还是有点难度的,不过细心一点,以及掌握服务原理,很好安装。在这,总结出以下几点,希望会有帮助:

 * 安装前了解服务原理,这一点很重要
 * 安装前了解服务版本之间的迭代以及区别,选好版本已经成功了一半
 * 安装时的配置,以及配置更改后重启服务
 * 安装报错,或者各种报错,先看服务日志,日志帮助很大
 * 安装后总结,方便以后Review ^ . ^

作者:__YesIdo_
链接:https://www.jianshu.com/p/3c798c99743a
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

完整CentOS搭建OpenVPN服务环境图文教程

对于OpenVPN环境有什么用途老左就不多说,因为看到这样的信息的朋友应该都了解且有需要使用这样的教程。这篇教程是老左让@水清流同学花费几个小时整理出来的,且经过多次重装实战演练确定成功可以安装和使用的,VPS环境基于CENTOS6环境下安装,如果你也想安装不凡认真看看下面的教程,写的真比较详细。

第一步、为vps安装openvpn及所有所需软件

A:安装EPEL仓库

wget http://dl.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm

rpm -Uvh epel-release-6-8.noarch.rpm

B:安装openvpn

yum install openvpn

C:安装openvpn最新的easy-rsa,该包用来制作ca证书,服务端证书,客户端证书。最新的为easy-rsa3

wget https://github.com/OpenVPN/easy-rsa/archive/master.zip

unzip master.zip

如果你的机器不支持UNZIP命令可以参考”解决Linux VPS安装UNZIP组件解压文件“文章。

D:将解压得到的文件夹easy-rsa-master重命名为easy-rsa

mv easy-rsa-mater/ easy-rsa/

然后将的到的easy-ras文件夹复制到/etc/openvpn/目录下

cp -R easy-rsa/ /etc/openvpn/

第二步、编辑vars文件,根据自己环境配置

A:先进入/etc/openvpn/easy-rsa/easyrsa3目录

cp /etc/openvpn/easy-rsa/easyrsa3/

B:复制vars.example 为vars

cp vars.example vars

C:修改下面字段,命令:vi vars,然后修改,最后wq保存

set_var EASYRSA_REQ_COUNTRY “CN” //根据自己情况更改

set_var EASYRSA_REQ_PROVINCE “Beijing”

set_var EASYRSA_REQ_CITY “Tong”

set_var EASYRSA_REQ_ORG “qingliu Certificate”

set_var EASYRSA_REQ_EMAIL “shuiqingliu14@gmail.com”

set_var EASYRSA_REQ_OU “My OpenVPN”

第三步、创建服务端证书及key

A:进入/etc/openvpn/easy-rsa/easyrsa3/目录初始化:

./easyrsa init-pki

B:创建根证书

./easyrsa build-ca

如下:

Generating a 2048 bit RSA private key

…………………………………….+++

……+++

writing new private key to ‘/root/easy-rsa/easyrsa3/pki/private/ca.key’

Enter PEM pass phrase:

Verifying – Enter PEM pass phrase:

—–

You are about to be asked to enter information that will be incorporated

into your certificate request.

What you are about to enter is what is called a Distinguished Name or a DN.

There are quite a few fields but you can leave some blank

For some fields there will be a default value,

If you enter ‘.’, the field will be left blank.

—–

Common Name (eg: your user, host, or server name) [Easy-RSA CA]:qingliu

CA creation complete and you may now import and sign cert requests.

Your new CA certificate file for publishing is at:

/root/easy-rsa/easyrsa3/pki/ca.crt

注意:在上述部分需要输入PEM密码 PEM pass phrase,输入两次,此密码必须记住,不然以后不能为证书签名。还需要输入common name 通用名,这个你自己随便设置个独一无二的。

eg:Common Name (eg: your user, host, or server name) [Easy-RSA CA]:qingliu

我输入qingliu

C:创建服务器端证书

./easyrsa gen-req server nopass

如下:

[root@localhost easyrsa3]# ./easyrsa gen-req server nopass

Generating a 2048 bit RSA private key

……………………………………………………………………..+++

……………………+++

writing new private key to ‘/root/easy-rsa/easyrsa3/pki/private/server.key’

—–

You are about to be asked to enter information that will be incorporated

into your certificate request.

What you are about to enter is what is called a Distinguished Name or a DN.

There are quite a few fields but you can leave some blank

For some fields there will be a default value,

If you enter ‘.’, the field will be left blank.

—–

Common Name (eg: your user, host, or server name) [server]:shuiqingliu

Keypair and certificate request completed. Your files are:

req: /root/easy-rsa/easyrsa3/pki/reqs/server.req

key: /root/easy-rsa/easyrsa3/pki/private/server.key

该过程中需要输入common name,随意但是不要跟之前的根证书的一样

签约服务端证书:

./easyrsa sign server server

如下:

You are about to sign the following certificate.

Please check over the details shown below for accuracy. Note that this request

has not been cryptographically verified. Please be sure it came from a trusted

source or that you have verified the request checksum with the sender.

Request subject, to be signed as a server certificate for 3650 days:

subject=

commonName = shuiqingliu

Type the word ‘yes’ to continue, or any other input to abort.

Confirm request details: yes

Using configuration from /root/easy-rsa/easyrsa3/openssl-1.0.cnf

Enter pass phrase for /root/easy-rsa/easyrsa3/pki/private/ca.key:

Check that the request matches the signature

Signature ok

The Subject’s Distinguished Name is as follows

commonName

RINTABLE:’shuiqingliu’

Certificate is to be certified until Apr 20 06:02:10 2024 GMT (3650 days)

Write out database with 1 new entries

Data Base Updated

Certificate created at: /root/easy-rsa/easyrsa3/pki/issued/server.crt

该命令中.需要你确认生成,要输入yes,还需要你提供我们当时创建CA时候的密码。如果你忘记了密码,那你就重头开始再来一次吧。

D:创建Diffie-Hellman,确保key穿越不安全网络的命令:

./easyrsa gen-dh

如下:

Note: using Easy-RSA configuration from: ./vars

Generating DH parameters, 2048 bit long safe prime, generator 2

This is going to take a long time

……..+……………………………….+..+…………………………………………………………………………………………………………………………….

DH parameters of size 2048 created at /etc/openvpn/easy-rsa/easyrsa3/pki/dh.pem

第四步、创建客户端证书

A:进入root目录新建client文件夹,文件夹可随意命名,然后拷贝前面解压得到的easy-ras文件夹到client文件夹,进入下列目录

cd /root/

mkdir client && cd client

cp -R easy-rsa/ client/

cd client/easy-rsa/easyrsa3/

B:初始化

./easyrsa init-pki

C:创建客户端key及生成证书(记住生成是自己输入的密码)

./easyrsa gen-req qingliu //名字自己定义

D:将的到的qingliu.req导入然后签约证书

a.进入到/etc/openvpn/easy-rsa/easyrsa3/

cd /etc/openvpn/easy-rsa/easyrsa3/

b.导入req

./easyrsa import-req /root/client/easy-rsa/easyrsa3/pki/reqs/qingliu.req qingliu

c.签约证书

./easyrsa sign client qingliu

//这里生成client所以必须为client,qingliu要与之前导入名字一致

上面签约证书跟server类似,就不截图了,但是期间还是要输入CA的密码

E:这步很重要,现在说一下我们上面都生成了什么东西

服务端:(etc/openvpn/easy-rsa/文件夹)

/etc/openvpn/easy-rsa/easyrsa3/pki/ca.crt

/etc/openvpn/easy-rsa/easyrsa3/pki/reqs/server.req

/etc/openvpn/easy-rsa/easyrsa3/pki/reqs/qingliu.req

/etc/openvpn/easy-rsa/easyrsa3/pki/private/ca.key

/etc/openvpn/easy-rsa/easyrsa3/pki/private/server.key

/etc/openvpn/easy-rsa/easyrsa3/pki/issued/server.crt

/etc/openvpn/easy-rsa/easyrsa3/pki/issued/qingliu.crt

/etc/openvpn/easy-rsa/easyrsa3/pki/dh.pem

客户端:(root/client/easy-rsa文件夹)

/root/client/easy-rsa/easyrsa3/pki/private/qingliu.key

/root/client/easy-rsa/easyrsa3/pki/reqs/qingliu.req //这个文件被我们导入到了服务端文件所以那里也有

a.这一步就是拷贝这些文件放入到相应位置。将下列文件放到/etc/openvpn/ 目录执行命令:

cp /etc/openvpn/easy-rsa/easyrsa3/pki/ca.crt /etc/openvpn

cp /etc/openvpn/easy-rsa/easyrsa3/pki/private/server.key /etc/openvpn

cp /etc/openvpn/easy-rsa/easyrsa3/pki/issued/server.crt /etc/openvpn

cp /etc/openvpn/easy-rsa/easyrsa3/pki/dh.pem /etc/openvpn

这样就将上述四个文件放入到了/etc/openvpn目录下

b.这一步将下列文件放到/root/client 目录下执行命令:

cp /etc/openvpn/easy-rsa/easyrsa3/pki/ca.crt /root/client

cp /etc/openvpn/easy-rsa/easyrsa3/pki/issued/qingliu.crt /root/client

cp /root/client/easy-rsa/easyrsa3/pki/private/qingliu.key /root/client

这样就将上述三个文件复制到了/root/client目录,包括:ca.crt、qingliu.crt、qingliu.key

第五步、为服务端编写配置文件

当你安装好了openvpn时候,他会提供一个server配置的文件例子,在

/usr/share/doc/openvpn-2.3.2/sample/sample-config-files

下会有一个server.conf文件,我们将这个文件复制到/etc/openvpn

cp /usr/share/doc/openvpn-2.3.2/sample/sample-config-files/server.conf /etc/openvpn

然后修改配置vi server.conf如下:

local 192.227.161.xx(跟自己vps IP)

port 1194

proto udp

dev tun

ca /etc/openvpn/ca.crt

cert /etc/openvpn/server.crt

key /etc/openvpn/server.key # This file should be kept secret

dh /etc/openvpn/dh.pem

server 10.8.0.0 255.255.255.0

ifconfig-pool-persist ipp.txt

push “redirect-gateway def1 bypass-dhcp”

push “dhcp-option DNS 8.8.8.8″

keepalive 10 120

comp-lzo

max-clients 100

persist-key

persist-tun

status openvpn-status.log

verb 3

每个项目都会由一大堆介绍,上述修改,openvpn提供的server.conf已经全部提供,我们只需要去掉前面的注释#,然后修改我们自己的有关配置。

第六步、下载openvpn客户端,并进行配置

A:用sftp将我们在vps生成的客户端证书和key下载到客户端电脑

ca.crt qingliu.crt qingliu.key //这三个文件

B:去官网下载openvpn客户端进行安装,然后安装目录找到simple-config

D:\Program Files\OpenVPN\sample-config\client.ovpn

将client.ovpn 复制到D:\Program Files\OpenVPN\config下,当然我把客户端装在了D盘你根据自己情况选择.

将下载到的三个文件放入D:\Program Files\OpenVPN\config下然后如下:

编辑配置文件:

client

dev tun

proto udp

remote 192.227.161.xx 1194 //主要这里修改成自己vps ip

resolv-retry infinite

nobind

persist-key

persist-tun

ca ca.crt //这里需要证书

cert qingliu.crt

key qingliu.key

comp-lzo

verb 3

我们只需要以上项目每行一个。

第七步、测试排错

A:启动vps上的openvpn服务

Service openvpn start

Oh,不幸的是出现service start failed!!!

但是当你运行:

Openvpn /etc/openvpn/server.conf

又可以运行,解决办法:

删除/etc/openvpn/下的ipp.txt openvpn-status.log

然后就可以启动服务了。如果你还不能解决,那就去var/log中找message慢慢分析原因

B:启动客户端

打开openvpn gui启动,注意在启动后需要输入创建qingliu.key时候的密码!我前面让你记住过的。然后进入如下界面

当不能长时间连接的时候你需要查看出错误log,我第一链接时候出错了,tls的某个错但是我已经解决了就是注释掉客户端配置文件中的#ns-cert-type server,这样就不会出错了。我提供给你的配置选项一修复问题。

鉴于以上操作,你的openvpn已经应该可以争取运行了如下:

如果不能正常工作,请执行下面步骤:查看客户端和服务器端log———–google相应错误以及在openvpn官方论坛查找答案——solve it

如果有朋友需要其他的VPN安装可以参考教程:

完整Openvz VPS一键安装PPTP VPN教程(CentOS 5 32位)

Windows7设置PPTP登录账户教程 实现全局VPN

实战Linux VPS CentOS搭建VPN教程(一键快速安装)

结束语:

本次配置openvpn,我们需要足够的耐心可以不言弃的决心。最重要的部分在easy-rsa的证书部分,需要细心配置。网络上关于easy-rsa3的操作资料非常少几乎只有官方文档有,easy-rsa3跟easy-rsa 2.0操作完全不同,因此大多数教程只适用于2.0版本,这次教程基于easy-rsa3,我觉得3的操作更简单。当遇到问题时候积极去各大搜索引擎论坛去查找资料来完成我们的搭建,祝您折腾顺利!

以上为@水清流同学供稿,足够的保证教程的完整性,如果有朋友在安装过程中出现问题,可以联系老左,让这位同学代为安装。过程是漫长的,对于有需要的朋友来说还是必须使用到的。

作者:大福技术
链接:https://www.jianshu.com/p/4bbf946222d5
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

linux下查看和修改文件时间

一、查看文件时间及相关命令

1、stat查看文件时间

[root@web10 ~]# stat install.log
  File: “install.log”
  Size: 33386           Blocks: 80         IO Block: 4096   一般文件
Device: fd00h/64768d    Inode: 7692962     Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2012-07-13 16:02:34.000000000 +0800
Modify: 2011-11-29 16:03:06.000000000 +0800
Change: 2011-11-29 16:03:08.000000000 +0800

说明:Access访问时间。Modify修改时间。Change状态改变时间。可以stat *查看这个目录所有文件的状态。

而我们想要查看某文件的三个时间中的具体某个时间,并以年月日时分秒的格式保存。我们可以使用下面的命令:

[root@web10 ~]# stat install.log|grep -i Modify | awk -F. '{print $1}' | awk '{print $2$3}'| awk -F- '{print $1$2$3}' | awk -F: '{print $1$2$3}'
20111129160306

2、ls查看文件时间

相应的通过ls 查看时也有三个时间:

• modification time(mtime,修改时间):当该文件的“内容数据”更改时,就会更新这个时间。内容数据指的是文件的内容,而不是文件的属性。
• status time(ctime,状态时间):当该文件的”状态(status)”改变时,就会更新这个时间,举例来说,更改了权限与属性,就会更新这个时间。
• access time(atime,存取时间):当“取用文件内容”时,就会更新这个读取时间。举例来说,使用cat去读取 ~/.bashrc,就会更新atime了。

[root@web10 ~]# ls -l --time=ctime install.log
-rw-r--r-- 1 root root 33386 2011-11-29 install.log
[root@web10 ~]# ls -l --time=atime install.log
-rw-r--r-- 1 root root 33386 07-13 16:02 install.log

注意:ls参数里没有–mtime这个参数,因为我们默认通过ls -l查看到的时间就是mtime 。

二、修改文件时间

创建文件我们可以通过touch来创建。同样,我们也可以使用touch来修改文件时间。touch的相关参数如下:

-a : 仅修改access time。
-c : 仅修改时间,而不建立文件。
-d : 后面可以接日期,也可以使用 --date="日期或时间"
-m : 仅修改mtime。
-t : 后面可以接时间,格式为 [YYMMDDhhmm]

注:如果touch后面接一个已经存在的文件,则该文件的3个时间(atime/ctime/mtime)都会更新为当前时间。若该文件不存在,则会主动建立一个新的空文件。

[root@web10 ~]# touch install.log
[root@web10 ~]# stat install.log
  File: “install.log”
  Size: 33386           Blocks: 80         IO Block: 4096   一般文件
Device: fd00h/64768d    Inode: 7692962     Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2012-07-13 16:21:50.000000000 +0800
Modify: 2012-07-13 16:21:50.000000000 +0800
Change: 2012-07-13 16:21:50.000000000 +0800

同样,使用ls ,查看到的结果也一样。

[root@web10 ~]# ls -l --time=ctime install.log
-rw-r--r-- 1 root root 33386 07-13 16:21 install.log
[root@web10 ~]# ls -l --time=atime install.log
-rw-r--r-- 1 root root 33386 07-13 16:21 install.log
[root@web10 ~]# ls -l install.log
-rw-r--r-- 1 root root 33386 07-13 16:21 install.log

下面再看一个和touch不相关的例子:

[root@web10 ~]# cp /etc/profile .;ll --time=atime profile ;ll --time=ctime profile
cp:是否覆盖“./profile”? y
-rw-r--r-- 1 root root 1344 07-13 16:24 profile
-rw-r--r-- 1 root root 1344 07-13 16:25 profile

因为我之前运行过这个命令一次,所以会出现覆盖,不过这个覆盖出的好,刚才让我们看到了atime和ctime的时间的差别。

我们再回到touch利用touch修改文件时间:

1. 同时修改文件的修改时间和访问时间
touch -d "2010-05-31 08:10:30" install.log
2. 只修改文件的修改时间
touch -m -d "2010-05-31 08:10:30" install.log
3. 只修改文件的访问时间
touch -a -d "2010-05-31 08:10:30" install.log

下面再给一个rootkit木马常用的伎俩。就是把后一个文件的时间修改成和前一个相同。

touch -acmr /bin/ls /etc/sh.conf

另外touch还支持像date命令一样参数修改文件时间:

[root@web10 ~]# touch -d "2 days ago" install.log ; ll install.log
-rw-r--r-- 1 root root 33386 07-11 16:35 install.log

最后总结下常用的文件操作与时间的关系:

1、访问时间,读一次这个文件的内容,这个时间就会更新。比如对这个文件使用more命令。ls、stat命令都不会修改文件的访问时间。

2、修改时间,对文件内容修改一次,这个时间就会更新。比如:vim后保存文件。ls -l列出的时间就是这个时间。

3、状态改变时间。通过chmod命令更改一次文件属性,这个时间就会更新。查看文件的详细的状态、准确的修改时间等,可以通过stat命令 文件名。

怎么让Mac不休眠

苹果 Mac 电脑在默认状态下,使用电池时2分钟会关闭屏幕,使用电池时为10分钟关闭。可能有的朋友想要让 Mac 电脑处于永久打开状态,下面简单介绍下怎么设置的方法。

工具/原料

  • Mac

方法/步骤

  1. 先打开 Mac 系统中的“偏好设置”应用,如图所示

    怎么让Mac不休眠
  2. 在系统偏好设置中点击打开“节能器”选项,如图所示

    怎么让Mac不休眠
  3. 在节能器设置窗口中,先点击顶部的“电源适配器”栏目,如图所示

    怎么让Mac不休眠
  4. 接下来先把关闭显示器的时间调整为“永不”关闭,如图所示

    怎么让Mac不休眠
  5. 随后系统会提示我们是否要继续设置为永不关闭显示器,点击“好”按钮继续,如图所示

    怎么让Mac不休眠
  6. 如果你不想让显示器一直为打开状态的话,还可以勾选“当显示关闭时,防止进入睡眠”选项,如图所示

    怎么让Mac不休眠
  7. 同样地,系统也会提示我们这样会消耗更多的电量,问是否要继续,点击“好”按钮即可。

    怎么让Mac不休眠

HTTP认证与https简介

https://www.cnblogs.com/xzwblog/p/6834663.html

HTTP请求报头: Authorization    [ˌɔ:θəraɪˈzeɪʃn]
HTTP响应报头: WWW-Authenticate    [ɔ:ˈθentɪkeɪt]
HTTP认证是基于质询/回应(challenge/response)的认证模式。

HTTP认证

BASIC认证

BASIC认证概述

当一个客户端向HTTP服务器进行数据请求时,如果客户端未被认证,则HTTP服务器将通过基本认证过程对客户端的用户名及密码进行验证,以决定用户是否合法。客户端在接收到HTTP服务器的身份认证要求后,会提示用户输入用户名及密码,然后将用户名及密码以BASE64加密,加密后的密文将附加于请求信息中, 如当用户名为anjuta,密码为:123456时,客户端将用户名和密码用“:”合并,并将合并后的字符串用BASE64加密为密文,并于每次请求数据时,将密文附加于请求头(Request Header)中。HTTP服务器在每次收到请求包后,根据协议取得客户端附加的用户信息(BASE64加密的用户名和密码),解开请求包,对用户名及密码进行验证,如果用户名及密码正确,则根据客户端请求,返回客户端所需要的数据;否则,返回错误代码或重新要求客户端提供用户名及密码。

BASIC认证的过程

基本认证步骤:
1、客户端访问一个受http基本认证保护的资源。
2、服务器返回401状态,要求客户端提供用户名和密码进行认证。(验证失败的时候,响应头会加上WWW-Authenticate: Basic realm=”请求域”。)
401 Unauthorized
WWW-Authenticate: Basic realm=”WallyWorld”
3、客户端将输入的用户名密码用Base64进行编码后,采用非加密的明文方式传送给服务器。
Authorization: Basic xxxxxxxxxx.
4、服务器将Authorization头中的用户名密码解码并取出,进行验证,如果认证成功,则返回相应的资源。如果认证失败,则仍返回401状态,要求重新进行认证。

BASIC认证的JAVA实现代码

     HttpSession session=request.getSession();
     String user=(String)session.getAttribute("user");
     String pass;
     if(user==null){
     try{
        response.setCharacterEncoding("GBK");
        PrintWriter ut=response.getWriter();
        String authorization=request.getHeader("authorization");
        if(authorization==null||authorization.equals("")){
            response.setStatus(401);
            response.setHeader("WWW-authenticate","Basic realm=\"请输入管理员密码\"");
            out.print("对不起你没有权限!!");
            return;
        }
    String userAndPass=new String(new BASE64Decoder().decodeBuffer(authorization.split(" ")[1]));
    if(userAndPass.split(":").length<2){
        response.setStatus(401);
        response.setHeader("WWW-authenticate","Basic realm=\"请输入管理员密码\"");
        out.print("对不起你没有权限!!");
        return;
    }
    user=userAndPass.split(":")[0];
    pass=userAndPass.split(":")[1];
    if(user.equals("111")&&pass.equals("111")){
        session.setAttribute("user",user);
        RequestDispatcher dispatcher=request.getRequestDispatcher("index.jsp");
        dispatcher.forward(request,response);
    }else{
        response.setStatus(401);
        response.setHeader("WWW-authenticate","Basic realm=\"请输入管理员密码\"");
        out.print("对不起你没有权限!!");
        return;
    }
     }catch(Exception ex){
        ex.printStackTrace();
     }
     }else{
        RequestDispatcher dispatcher=request.getRequestDispatcher("index.jsp");
        dispatcher.forward(request,response);
    }

特记事项:

1、Http是无状态的,同一个客户端对同一个realm内资源的每一个访问会被要求进行认证。
2、客户端通常会缓存用户名和密码,并和authentication realm一起保存,所以,一般不需要你重新输入用户名和密码。
3、以非加密的明文方式传输,虽然转换成了不易被人直接识别的字符串,但是无法防止用户名密码被恶意盗用。虽然用肉眼看不出来,但用程序很容易解密。

优点:

基本认证的一个优点是基本上所有流行的网页浏览器都支持基本认证。基本认证很少在可公开访问的互联网网站上使用,有时候会在小的私有系统中使用(如路由器
网页管理接口)。后来的机制HTTP摘要认证是为替代基本认证而开发的,允许密钥以相对安全的方式在不安全的通道上传输。

缺点:

  • 虽然基本认证非常容易实现,但该方案建立在以下的假设的基础上,即:客户端和服务器主机之间的连接是安全可信的。特别是,如果没有使用SSL/TLS这样的传输
    层安全的协议,那么以明文传输的密钥和口令很容易被拦截。该方案也同样没有对服务器返回的信息提供保护。
  • 现存的浏览器保存认证信息直到标签页或浏览器被关闭,或者用户清除历史记录。HTTP没有为服务器提供一种方法指示客户端丢弃这些被缓存的密钥。这意味着服务
    器端在用户不关闭浏览器的情况下,并没有一种有效的方法来让用户登出。

HTTP OAuth认证

OAuth对于Http来说,就是放在Authorization header中的不是用户名密码, 而是一个token。微软的Skydrive就是使用这样的方式。
参考:http://www.tuicool.com/articles/qqeuE3

摘要认证

digest authentication(HTTP1.1提出的基本认证的替代方法)
这个认证可以看做是基本认证的增强版本,不包含密码的明文传递。

引入了一系列安全增强的选项;“保护质量”(qop)、随机数计数器由客户端增加、以及客户生成的随机数。

在HTTP摘要认证中使用 MD5 加密是为了达成”不可逆的”,也就是说,当输出已知的时候,确定原始的输入应该是相当困难的。如果密码本身太过简单,也许可以
通过尝试所有可能的输入来找到对应的输出(穷举攻击),甚至可以通过字典或者适当的查找表加快查找速度。

示例及说明
下面的例子仅仅涵盖了“auth”保护质量的代码,因为在撰写期间,所知道的只有Opera和Konqueror网页浏览器支持“auth-int”(带完整性保护的认证)。

典型的认证过程包括如下步骤:

客户端请求一个需要认证的页面,但是不提供用户名和密码。通常这是由于用户简单的输入了一个地址或者在页面中点击了某个超链接。
服务器返回401 “Unauthorized” 响应代码,并提供认证域(realm),以及一个随机生成的、只使用一次的数值,称为密码随机数 nonce。
此时,浏览器会向用户提示认证域(realm)(通常是所访问的计算机或系统的描述),并且提示用户名和密码。用户此时可以选择取消。
一旦提供了用户名和密码,客户端会重新发送同样的请求,但是添加了一个认证头包括了响应代码。

注意:客户端可能已经拥有了用户名和密码,因此不需要提示用户,比如以前存储在浏览器里的。

客户端请求 (无认证):

    GET /dir/index.html HTTP/1.0
    Host: localhost
    (跟随一个新行,形式为一个回车再跟一个换行)

服务器响应:

    HTTP/1.0 401 Unauthorized
    Server: HTTPd/0.9
    Date: Sun, 10 Apr 2005 20:26:47 GMT
    WWW-Authenticate: Digest realm="testrealm@host.com",   //认证域
    qop="auth,auth-int",   //保护质量
    nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",  //服务器密码随机数
    opaque="5ccc069c403ebaf9f0171e9517f40e41"
    Content-Type: text/html
    Content-Length: 311
    
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
     "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
    <HTML>
      <HEAD>
    <TITLE>Error</TITLE>
    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1">
      </HEAD>
      <BODY><H1>401 Unauthorized.</H1></BODY>
    </HTML>

客户端请求 (用户名 “Mufasa”, 密码 “Circle Of Life”):

    GET /dir/index.html HTTP/1.0
    Host: localhost
    Authorization: Digest username="Mufasa",
     realm="testrealm@host.com",
     nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
     uri="/dir/index.html",
     qop=auth,
     nc=00000001,//请求计数
     cnonce="0a4f113b",   //客户端密码随机数
     response="6629fae49393a05397450978507c4ef1",
     opaque="5ccc069c403ebaf9f0171e9517f40e41"
    (跟随一个新行,形式如前所述)。

服务器响应:

    HTTP/1.0 200 OK
    Server: HTTPd/0.9
    Date: Sun, 10 Apr 2005 20:27:03 GMT
    Content-Type: text/html
    Content-Length: 7984
    (随后是一个空行,然后是所请求受限制的HTML页面)

response 值由三步计算而成。当多个数值合并的时候,使用冒号作为分割符:

1、对用户名、认证域(realm)以及密码的合并值计算 MD5 哈希值,结果称为 HA1。
2、对HTTP方法以及URI的摘要的合并值计算 MD5 哈希值,例如,”GET” 和 “/dir/index.html”,结果称为 HA2。
3、对HA1、服务器密码随机数(nonce)、请求计数(nc)、客户端密码随机数(cnonce)、保护质量(qop)以及 HA2 的合并值计算 MD5 哈希值。结果即为客户端提供的
response 值。

因为服务器拥有与客户端同样的信息,因此服务器可以进行同样的计算,以验证客户端提交的 response 值的正确性。在上面给出的例子中,结果是如下计算的。
(MD5()表示用于计算MD5哈希值的函数;“”表示接下一行;引号并不参与计算)

HA1 = MD5( "Mufasa:testrealm@host.com:Circle Of Life" )
       = 939e7578ed9e3c518a452acee763bce9

HA2 = MD5( "GET:/dir/index.html" )
       = 39aff3a2bab6126f332b942af96d3366

Response = MD5( "939e7578ed9e3c518a452acee763bce9:\
                         dcd98b7102dd2f0e8b11d0f600bfb0c093:\
00000001:0a4f113b:auth:\
39aff3a2bab6126f332b942af96d3366" )
= 6629fae49393a05397450978507c4ef1

此时客户端可以提交一个新的请求,重复使用服务器密码随机数(nonce)(服务器仅在每次“401”响应后发行新的nonce),但是提供新的客户端密码随机数(cnonce)。在后续的请求中,十六进制请求计数器(nc)必须比前一次使用的时候要大,否则攻击者可以简单的使用同样的认证信息重放老的请求。由服务器来确保在每个发出的密码随机数nonce时,计数器是在增加的,并拒绝掉任何错误的请求。显然,改变HTTP方法和/或计数器数值都会导致不同的 response值。

服务器应当记住最近所生成的服务器密码随机数nonce的值。也可以在发行每一个密码随机数nonce后,记住过一段时间让它们过期。如果客户端使用了一个过期的值,服务器应该响应“401”状态号,并且在认证头中添加stale=TRUE,表明客户端应当使用新提供的服务器密码随机数nonce重发请求,而不必提示用户其它用户名和口令。

Cookie认证机制:用户输入用户名和密码发起请求,服务器认证后给每个Session分配一个唯一的JSESSIONID,并通过Cookie发送给客户端。
当客户端发起新的请求的时候,将在Cookie头中携带这个JSESSIONID。这样服务器能够找到这个客户端对应的Session。默认的,当我们关闭浏览器的时候,客户端cookie会被删除,可以通过修改cookie 的expire time使cookie在一定时间内有效。但是服务器端的session不会被销毁,除非通过invalidate或超时。

Token Auth

常用的Token Auth(和Cookie Auth区别不大):

  1. 首次登陆,用户名和密码验证过之后,将sessionId保存在token中,或者将一个key保存在token中,key的值可以设置为用户唯一性的信息(账号/密码/身份认证机制(电话号/身份证号/支付宝账号/银行卡信息)…);当然我们在程序中的实现是保存UUID作为ticket。
  2. 设置token的有效期,并保存在服务器数据库中;
  3. 服务器将这个token值返回给客户端,客户端拿到 token 值之后,将 token 保存在 cookie 中,以后客户端再次发送网络请求(一般不是登录请求)的时候,就会将这个 token 值附带到参数中发送给服务器。服务器接收到客户端的请求之后,会取出token值与保存在本地(数据库)中的token值做对比!
    如果两个 token 值相同 :说明用户登录成功过!当前用户处于登录状态!如果没有这个token或者过期,则设置token为无效,并让用户重新登录。

这种方式在客户端变化不大,也要利用cookie,改动的是服务器端
过去:通过sessionId查找Tomcat服务器内存中是否有sessionId对应的session存在,若存在,则表示登陆过,然后从session找出用户信息;
现在:通过token查找数据库中是否有相同的token,并且token要处于有效期前,有的话通过token在数据库中找出用户信息,否则重新登录,(其实还包括sessionId的验证,因为jsp默认创建session)。
如果觉得查询数据库比较耗时,可以用memcache或者redis缓存一下。

首先说明一下session何时会被创建:

  • 1、 请求JSP页面时自动创建session,利用request.getSession(true);语句
    原因:
    由于HTTP是无状态协议,这意味着每次客户端检索网页时,都要单独打开一个服务器http连接,如果我同一个浏览器,不同页面打开你的主页10次,那就要进行10次连接和断开(TCP3次握手,4次挥手),浪费系统资源,http提供了一种长连接,keep-alive,相同会话的不同请求可以用同一连接,故jsp默认创建session。而session的创建过程中会自动将sessionId写入cookie的JSESSIONID中的,这样,只要不关闭浏览器,你在同一网站的任意网页跳转,由于每次请求都会携带同一个sessionId,不会重新创建新的会话,防止创建多个会话浪费系统资源。
    否则:黑客利用几台主机,疯狂的点击某一个JSP页面,如果每次点击都创建一个新的会话,可能使服务器崩溃。

例子:
登录函数:

// 用户登录操作
    public void login(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("gb2312");
        String account = request.getParameter("account");
        consumerDao = new ConsumerDao();
        ConsumerForm consumerForm = consumerDao.getConsumerForm(account);
        if (consumerForm == null) {
            request.setAttribute("information", "您输入的用户名不存在,请重新输入!");
        } else if (!consumerForm.getPassword().equals(request.getParameter("password"))) {
            request.setAttribute("information", "您输入的登录密码有误,请重新输入!");
        } else {

            request.setAttribute("form", consumerForm);
        }
        RequestDispatcher requestDispatcher = request.getRequestDispatcher("dealwith.jsp");
        requestDispatcher.forward(request, response);
    }

登录主页:

<body onselectstart="return false">
    <table width="800" height="496" border="0" align="center" cellpadding="0" cellspacing="0" background="images/login.jpg">
        <tr>
            <td valign="top">
                <table width="658" border="0">
                    <tr>
                        <td colspan="2">&nbsp;</td>
                    </tr>
                    <tr>
                        <td width="92" height="358">&nbsp;</td>
                        <td width="550" valign="bottom">
                            <form name="form1" method="post" action="ConsumerServlet?method=0&sign=0"
                                onSubmit="return userCheck()">
                                <table width="291" border="0" align="center" cellpadding="0" cellspacing="0">
                                    <tr>
                                        <td width="66" height="30">用户名:</td>
                                        <td width="225"><input name="account" type="text" class="inputinput" id="account" size="30"></td>
                                    </tr>
                                    <tr>
                                        <td height="30">密&nbsp;&nbsp;码:</td>
                                        <td><input name="password" type="password" class="inputinput" id="password" size="30"></td>
                                    </tr>
                                    <tr>
                                        <td height="30" colspan="2" align="center"><input type="image" class="inputinputinput" src="images/land.gif">
                                            &nbsp;&nbsp; <a href="#" onClick="javascript:document.form1.reset()"><img src="images/reset.gif"></a> &nbsp;&nbsp; <a
                                            href="consumer/accountAdd.jsp"><img src="images/register.gif"></a></td>
                                    </tr>
                                </table>
                            </form>
                        </td>
                    </tr>
                </table>
            </td>
        </tr>
    </table>
</body>

其中并没有利用session的语句,但是主界面,当还没有登录时:

可见有一个JSESSIONID,并且点击其他页面并没有开启新的JSESSIONID。但是换一个浏览器就会产生新的JSESSIONID,因为不同浏览器的会话缓存是不可以互相用的

登录以后还是这个JSESSIONID:

Session的销毁只有三种方式:
1.调用了session.invalidate()方法
2.session过期(超时)
3.服务器重新启动

单个JSP页面禁用session方式

<%@ page session="false">
  • 在servlet中,只要代码不调用Session相关的API就不会创建session
    request.getSession() 等价于 request.getSession(true)
    这两个方法的作用是相同的,查找请求中是否有关联的JSESSIONID,如果有则返回这个号码所对应的session对象,如果没有则生成一个新的session对象。所以说,通过此方法是一定可以获得一个session对象。
    request.getSession(false) 查找请求中是否有关联的JSESSIONID号,如果有则返回这个号码所对应的session对象,如果没有则返回一个null。
    注意在创建session的过程中,sessionId是自动写入cookie的JSESSIONID中的,如果禁用cookie,则通过url回传

参考的头条项目:

JSESSIONID和token(即ticket)同时存在,尽量不往session里放东西,将用户主键,过期时间等都存到和ticket一起的表中,优点:1.减少内存占用;2.Tomcat默认30分钟session销毁,采用token可以长时间保持登录状态,但可能就不是这一个session了;3.除了JSESSIONID验证一致性,增加一个token验证,防止黑客暴力破解JSESSIONID,但黑客可以获得JSESSIONID,估计获得token也不难,只是会麻烦一点。

牛客网:

淘宝网:

都用了token和session(NOWCODERCLINETID和cookie2,这个只是猜测),只是不是直接保存JSESSIONID,改名字了,如果攻击者不分析站点,就不能猜到Session名称,阻挡部分攻击。

真正的应用:JWT
通过token可以将用户的基本信息(非隐私的,比如UserId,过期时间,生成的随机key等)全部加密签名后放入token中,从而服务器端不需要保存用户登录信息,大大减轻服务器压力。用户认证完全靠token识别,通过签名来保证token没有被修改过(只有服务器才知道秘钥,比如常见的非对称加密算法),是服务器下发的token。在后续请求中,服务端只需要对用户请求中包含的token进行解码,验证用户登录是否过期。

很多大型网站也都在用,比如 Facebook,Twitter,Google+,Github 等等,比起传统的身份验证方法,Token 扩展性更强,也更安全点,非常适合用在 Web 应用或者移动应用上。

Token Auth优点:

  • 减轻服务器压力:通过token可以将用户的基本信息(非隐私的,比如UserId,过期时间,生成的随机key等)全部加密签名后放入token中,从而服务器端不需要保存用户登录信息,大大减轻服务器压力。用户认证完全靠token识别,通过签名来保证token没有被修改过(只有服务器才知道秘钥,比如常见的非对称加密算法),是服务器下发的token。
  • 支持跨域访问:因为服务器并没有保存登录状态,完全靠签名的token识别,那么另一个网站只要有对应的私钥,就可以对token验证,前提是传输的用户认证信息通过HTTP头传输;
  • 更适用CDN: 可以通过内容分发网络请求你服务端的所有资料(如:javascript,HTML,图片等),因为不需要同步服务器上的登录状态信息;
  • 性能更好: 因为从token中可以获得userId,不用查询登录状态表;

详细

但是都不能很好的预防会话劫持

什么是Realm

Tomcat提供Realm支持。
Tomcat使用Realm使某些特定的用户组具有访问某些特定的Web应用的权限,而没有权限的用户不能访问这个应用。
Tomcat提供了三种不同Realm对访问某个Web应用程序的用户进行相应的验证。

1、JDBCRealm,这个Realm将用户信息存在数据库里,通过JDBC从数据库中获得用户信息并进行验证。

2、JNDIRealm,将用户信息存在基于LDAP等目录服务的服务器里,通过JNDI技术从LDAP服务器中获取用户信息并进行验证。

3、MemoryRealm,将用户信息存在一个xml文件中,对用户进行验证时,将会从相应的xml文件中提取用户信息。manager(Tomcat提供的一个web应用程序)应用在进行验证时即使用此种Realm。Realm类似于Unix里面的group。在Unix中,一个group对应着系统的一定资源,某个group不能访问不属于它的资源。Tomcat用Realm来对不同的应用(类似系统资源)赋给不同的用户(类似group)。没有权限的用户则不能访问这个应用。

HTTPS传输协议原理

HTTPS(HTTP over SSL,实际上是在原有的 HTTP 数据外面加了一层 SSL 的封装。HTTP 协议原有的 GET、POST 之类的机制,基本上原封不动),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容请看SSL。

两种基本的加解密算法类型

  • 对称加密:密钥只有一个,加密解密为同一个密码,且加解密速度快,典型的对称加密算法有DES、AES等。
  • 非对称加密:密钥成对出现(且根据公钥无法推知私钥,根据私钥也无法推知公钥),加密解密使用不同密钥(公钥加密需要私钥解密,私钥加密需要公钥解密),相对对称加密速度较慢,典型的非对称加密算法有RSA、DSA等。

HTTPS通信过程

HTTPS通信的优点

客户端产生的密钥只有客户端和服务器端能得到;
加密的数据只有客户端和服务器端才能得到明文;
客户端到服务端的通信是安全的。

SSL/TLS协议

SSL/TLS协议希望达到:
(1) 所有信息都是加密传播,第三方无法窃听。
(2) 具有校验机制,一旦被篡改,通信双方会立刻发现。
(3) 配备身份证书,防止身份被冒充。

历史

1994年,NetScape公司设计了SSL协议(Secure Sockets Layer)的1.0版,但是未发布。
1995年,NetScape公司发布SSL 2.0版,很快发现有严重漏洞。
1996年,SSL 3.0版问世,得到大规模应用。
1999年,互联网标准化组织ISOC接替NetScape公司,发布了SSL的升级版TLS 1.0版。
2006年和2008年,TLS进行了两次升级,分别为TLS 1.1版和TLS 1.2版。最新的变动是2011年TLS 1.2的修订版。
目前,应用最广泛的是TLS 1.0,接下来是SSL 3.0。但是,主流浏览器都已经实现了TLS 1.2的支持。
TLS 1.0通常被标示为SSL 3.1,TLS 1.1为SSL 3.2,TLS 1.2为SSL 3.3。

基本的运行过程

SSL/TLS协议的基本思路是采用公钥加密法,也就是说,客户端先向服务器端索要公钥,然后用公钥加密信息,服务器收到密文后,用自己的私钥解密。

如何保证公钥不被篡改?
解决方法:将公钥放在数字证书中。只要证书是可信的,公钥就是可信的。

公钥加密计算量太大,如何减少耗用的时间?
解决方法:每一次对话(session),客户端和服务器端都生成一个”对话密钥”(session key),用它来加密信息。由于”对话密钥”是对称加密,所以运算速度非常快,而服务器公钥只用于加密”对话密钥”本身,这样就减少了加密运算的消耗时间。即在客户端与服务器间传输的数据是通过使用对称算法(如 DES 或 RC4)进行加密的。

因此,SSL/TLS协议的基本过程是这样的:
(1) 客户端向服务器端索要并验证公钥。
(2) 双方协商生成”对话密钥”。
(3) 双方采用”对话密钥”进行加密通信。
上面过程的前两步,又称为”握手阶段”(handshake)。

握手阶段的详细过程

“握手阶段”涉及四次通信,我们一个个来看。

1 客户端发出请求(ClientHello)
首先,客户端(通常是浏览器)先向服务器发出加密通信的请求,这被叫做ClientHello请求。
在这一步,客户端主要向服务器提供以下信息。
(1) 支持的协议版本,比如TLS 1.0版。
(2) 一个客户端生成的随机数,稍后用于生成”对话密钥”。
(3) 支持的加密方法,比如RSA公钥加密。
(4) 支持的压缩方法。
这里需要注意的是,客户端发送的信息之中不包括服务器的域名。也就是说,理论上服务器只能包含一个网站,否则会分不清应该向客户端提供哪一个网站的数字证书。这就是为什么通常一台服务器只能有一张数字证书的原因。
对于虚拟主机的用户来说,这当然很不方便。2006年,TLS协议加入了一个Server Name Indication扩展,允许客户端向服务器提供它所请求的域名。

2 服务器回应(SeverHello)
服务器收到客户端请求后,向客户端发出回应,这叫做SeverHello。服务器的回应包含以下内容。
(1) 确认使用的加密通信协议版本,比如TLS 1.0版本。如果浏览器与服务器支持的版本不一致,服务器关闭加密通信。
(2) 一个服务器生成的随机数,稍后用于生成”对话密钥”。
(3) 确认使用的加密方法,比如RSA公钥加密。
(4) 服务器证书。
除了上面这些信息,如果服务器需要确认客户端的身份,就会再包含一项请求,要求客户端提供”客户端证书”。比如,金融机构往往只允许认证客户连入自己的网络,就会向正式客户提供USB密钥,里面就包含了一张客户端证书。

3 客户端回应
客户端收到服务器回应以后,首先验证服务器证书。如果证书不是可信机构颁布、或者证书中的域名与实际域名不一致、或者证书已经过期,就会向访问者显示一个警告,由其选择是否还要继续通信。
如果证书没有问题,客户端就会从证书中取出服务器的公钥。然后,向服务器发送下面三项信息。
(1) 一个随机数。该随机数用服务器公钥加密,防止被窃听。
(2) 编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。
(3) 客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供服务器校验。
上面第一项的随机数,是整个握手阶段出现的第三个随机数,又称”pre-master key”。有了它以后,客户端和服务器就同时有了三个随机数,接着双方就用事先商定的加密方法,各自生成本次会话所用的同一把”会话密钥”。

至于为什么一定要用三个随机数,来生成”会话密钥”:
“不管是客户端还是服务器,都需要随机数,这样生成的密钥才不会每次都一样。由于SSL协议中证书是静态的,因此十分有必要引入一种随机因素来保证协商出来的密钥的随机性。
对于RSA密钥交换算法来说,pre-master-key本身就是一个随机数,再加上hello消息中的随机,三个随机数通过一个密钥导出器最终导出一个对称密钥。
pre master的存在在于SSL协议不信任每个主机都能产生完全随机的随机数,如果随机数不随机,那么pre master secret就有可能被猜出来,那么仅适用pre master secret作为密钥就不合适了,因此必须引入新的随机因素,那么客户端和服务器加上pre master secret三个随机数一同生成的密钥就不容易被猜出了,一个伪随机可能完全不随机,可是是三个伪随机就十分接近随机了,每增加一个自由度,随机性增加的可不是一。”
此外,如果前一步,服务器要求客户端证书,客户端会在这一步发送证书及相关信息。

4 服务器的最后回应
服务器收到客户端的第三个随机数pre-master key之后,计算生成本次会话所用的”会话密钥”。然后,向客户端最后发送下面信息。
(1)编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。
(2)服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供客户端校验。
至此,整个握手阶段全部结束。接下来,客户端与服务器进入加密通信,就完全是使用普通的HTTP协议,只不过用”会话密钥”加密内容。

非对称加密

需要一对密钥,一个是私人密钥,另一个则是公开密钥。这两个密钥是数学相关,用某用户密钥加密后所得的信息,只能用该用户的解密密钥才能解密。如果知道了其中一个,并不能计算出另外一个。因此如果公开了一对密钥中的一个,并不会危害到另外一个的秘密性质。称公开的密钥为公钥;不公开的密钥为私钥。
如果加密密钥是公开的,这用于客户给私钥所有者上传加密的数据,这被称作为公开密钥加密(狭义)。例如,网络银行的客户发给银行网站的操作信息的加密数据,采用公钥加密。
如果解密密钥是公开的,用私钥加密的信息,可以用公钥对其解密,用于客户验证持有私钥一方发布的数据或文件是完整准确的,接收者由此可知这条信息确实来自于拥有私钥的某人,这被称作数字签名,公钥的形式就是数字证书
每个人都有一对“钥匙”(数字身份),其中一个只有她/他本人知道(私钥),另一个公开的(公钥)。签名的时候用私钥,验证签名的时候用公钥。又因为任何人都可以落款申称她/他就是使用者本人,因此公钥必须向接受者信任的人(身份认证机构)来注册。注册后身份认证机构给使用者发一数字证书。对文件签名后,使用者把此数字证书连同文件及签名一起发给接受者,接受者向身份认证机构求证是否真地是用使用者的密钥签发的文件。
在中国,数字签名是具法律效力的,正在被普遍使用。
使用最广泛的是RSA算法

RSA算法原理

1977年,三位数学家Rivest、Shamir 和 Adleman 设计了一种算法,可以实现非对称加密。这种算法用他们三个人的名字命名,叫做RSA算法。
这种算法非常可靠,密钥越长,它就越难破解。根据已经披露的文献,目前被破解的最长RSA密钥是768个二进制位。也就是说,长度超过768位的密钥,还无法破解(至少没人公开宣布)。因此可以认为,1024位的RSA密钥基本安全,2048位的密钥极其安全。

“对极大整数做因数分解的难度决定了RSA算法的可靠性。换言之,对一极大整数做因数分解愈困难,RSA算法愈可靠。
假如有人找到一种快速因数分解的算法,那么RSA的可靠性就会极度下降。但找到这样的算法的可能性是非常小的。今天只有短的RSA密钥才可能被暴力破解。到2008年为止,世界上还没有任何可靠的攻击RSA算法的方式。
只要密钥长度足够长,用RSA加密的信息实际上是不能被解破的。”
RSA性能是非常低的,原因在于寻找大素数、大数计算、数据分割需要耗费很多的CPU周期,所以一般的HTTPS连接只在握手时使用非对称加密,通过握手交换对称加密密钥(其实只有第三次通信,客户端向服务器发送随机数时用公钥加密),在之后的通信走对称加密。

互质关系

如果两个正整数,除了1以外,没有其他公因子,我们就称这两个数是互质关系(coprime)。比如,15和32没有公因子,所以它们是互质关系。这说明,不是质数也可以构成互质关系。
关于互质关系,不难得到以下结论:
1. 任意两个质数构成互质关系,比如13和61。
2. 一个数是质数,另一个数只要不是前者的倍数,两者就构成互质关系,比如3和10。
3. 如果两个数之中,较大的那个数是质数,则两者构成互质关系,比如97和57。
4. 1和任意一个自然数是都是互质关系,比如1和99。
5. p是大于1的整数,则p和p-1构成互质关系,比如57和56。
6. p是大于1的奇数,则p和p-2构成互质关系,比如17和15。

欧拉函数

任意给定正整数n,请问在小于等于n的正整数之中,有多少个与n构成互质关系?(比如,在1到8之中,有多少个数与8构成互质关系?)
计算这个值的方法就叫做欧拉函数,以φ(n)表示。在1到8之中,与8形成互质关系的是1、3、5、7,所以 φ(n) = 4。

因为任意一个大于1的正整数,都可以写成一系列质数的积。

欧拉函数的通用计算公式

欧拉定理
模反元素

详细看:
http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html

密钥生成的步骤

假设爱丽丝要与鲍勃进行加密通信,她该怎么生成公钥和私钥呢?

第一步,随机选择两个不相等的质数p和q。
爱丽丝选择了61和53。(实际应用中,这两个质数越大,就越难破解。)

第二步,计算p和q的乘积n。
爱丽丝就把61和53相乘。
n = 61×53 = 3233
n的长度就是密钥长度。3233写成二进制是110010100001,一共有12位,所以这个密钥就是12位。实际应用中,RSA密钥一般是1024位,重要场合则为2048位。

第三步,计算n的欧拉函数φ(n)。
根据公式:
φ(n) = (p-1)(q-1)
爱丽丝算出φ(3233)等于60×52,即3120。

第四步,随机选择一个整数e,条件是1< e < φ(n),且e与φ(n) 互质。
爱丽丝就在1到3120之间,随机选择了17。(实际应用中,常常选择65537。)

第五步,计算e对于φ(n)的模反元素d。
所谓”模反元素”就是指有一个整数d,可以使得ed被φ(n)除的余数为1。
ed ≡ 1 (mod φ(n))
这个式子等价于
ed – 1 = kφ(n)
于是,找到模反元素d,实质上就是对下面这个二元一次方程求解。
ex + φ(n)y = 1
已知 e=17, φ(n)=3120,
17x + 3120y = 1
这个方程可以用”扩展欧几里得算法”求解,此处省略具体过程。总之,爱丽丝算出一组整数解为 (x,y)=(2753,-15),即 d=2753。
至此所有计算完成。

第六步,将n和e封装成公钥,n和d封装成私钥。
在爱丽丝的例子中,n=3233,e=17,d=2753,所以公钥就是 (3233,17),私钥就是(3233, 2753)。
实际应用中,公钥和私钥的数据都采用ASN.1格式表达(实例)。

结论:如果n可以被因数分解,d就可以算出,也就意味着私钥被破解。
大整数的因数分解,是一件非常困难的事情。目前,除了暴力破解,还没有发现别的有效方法。

详细看:
http://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html

误区

HTTPS无法缓存

在大家的观念里,出于安全考虑,浏览器不会在本地保存HTTPS缓存。 实际上, 只要在HTTP头中使用特定命令,HTTPS是可以缓存的。
Microsoft 项目经理Eric Lawrence 写道:
比如,如果头命令是Cache-Control:max-age=600,那么HTTPS的网页就将被IE缓存10分钟,IE的缓存策略与是否使用HTTPS协议无线。其它浏览器也有类似的操作方法。
Firefox默认只在内存中缓存HTTPS。但是,只要头命令中有Cache-Control:Public,缓存就会被写到硬盘上。下面的图片显示,Firefox的硬盘缓存中有HTTPS内容,头命令正是Cache-Control:Public。

有了HTTPS,Cookie和查询字符串就安全了

虽然无法直接从HTTPS数据中读取Cookie和查询字符串,但是你仍然需要使它们的值变得难以预测。
比如,曾经有一家英国银行,直接使用顺序排列的数值表示session id:
黑客可以先注册一个账户,找到这个cookie,看到这个值的表示方法。然后,改动cookie,从而劫持其他人的session id。至于查询字符串,也可以通过类似方式泄漏。