标签: 密码

  • GPG 加密及签名

    1、先产生密钥对

    #gpg –gen-key
    这样会在用户家目录生成一个./gnupg的目录,然后会要求你回答一系列问题,前面三个按默认即可。
    进入到real name,是要求你输入用户ID,注意姓名要5个字符长。这里假设为davidway
    在Email address处,填写上自己的邮箱地址,假设为root@aikaiyuan.com
    在comment处,填写一些注释信息
    输入大写字母“O”,回车确认。
    在Enter passphrase处,输入导入私钥用的密码句,这里假设为davidway,密码句是用来保护私钥的,一定要牢记。这里输入时是没有回显的,且要输入两次。

    2、导出公钥(公钥应公之于众,以便别人使用你的公钥来加密文件)

    命令各式:
    #gpg -o name.gpg -a –export name
    其中name为用户ID
    name.gpg为导出的公钥文件,文件名必须后缀为gpg
    例如:
    #gpg -o davidway.gpg -a –export davidway
    这样就把用户ID为davidway的公钥导出来了。

    3、导入别人的公钥(以便给别人发送加密文件,公钥用来加密)

    #gpg –import someone.gpg
    上面someone.gpg是别人的公钥

    4、编辑公钥,以验证导入的公钥的真实性

    #gpg –edit-key someone
    someone是别人的用户ID
    出现命令提示符 >

    fpr
    查看用户someone的公钥的指纹,之后应设法核对指纹,以证明真实性。如果真实,则可以签署。
    查看someone的指纹,用下面这个命令
    #gpg –list-key

    sign
    签署这个公钥,这样以后再使用它加密时,就不会再警告

    check
    检查用户someone的公钥已有的签名
    出现sig! 3 sig! 1 表示已完成。

    输入quit,回车,再输入y保存退出

    5、查看公钥

    #gpg –list-key

    6、用别人的公钥加密文件

    命令格式:
    #gpg -o doc.gpg -er name doc
    其中name是选择谁的公钥加密,即谁是文件的接收者。
    doc为要加密的文件,即原文件
    doc.gpg为命令执行后生成的加密的文件,这里要先指定好文件名
    例如:
    #gpg -o test.gpg -er someone test
    加密test文件后,生成test.gpg加密文件,发送给someone

    7、解密文件

    命令格式:
    #gpg -o doc.new -d doc.gpg
    其中doc.gpg是别人发给自己的加密过的文件
    doc.new是解密后生成的文件
    d表示解密
    例如:
    #gpg -o test.new -d test.gpg
    解密需导入私钥,这时会提示输入密码句,以导出私钥来解密

    8、使用对称密钥加密

    #gpg -o doc.gpg -c doc
    这种加密适用于本机文件加密,这时提示输入的密码句和私钥密码句没有联系,但一样不能忘记,因为解密时需要输入同样的密码句。

    9、数字签名

    命令格式:
    #gpg -o doc.sig -s doc
    其中doc是原文件,doc.sig包含了原文件和签名,是二进制的。这个命令会要求你输入你的私钥的密码句。
    #gpg -o doc.sig -ser name doc
    既签名又加密

    10、文本签名

    #gpg -o doc.sig –clearsign doc
    这样产生的doc.sig同样包含原文件和签名,其中签名是文本的,而原文件不变。

    11、分离式签名

    #gpg -o doc.sig -ab doc
    doc.sig仅包括签名,分离式签名的意思是原文件和签名是分开的。
    b 表示分离式签名detach-sign

    12、验证签名

    #gpg –verify doc.sig [doc]
    验证之前必须导入文件作者的公钥,对于分离式签名,最后还要加上原文件,即后面的doc。

    转载请注明:爱开源 » GPG 加密及签名

  • 理解OAuth 2.0

    OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版。

    本文对OAuth 2.0的设计思路和运行流程,做一个简明通俗的解释,主要参考材料为RFC 6749

    bg2014051201

    一、应用场景

    为了理解OAuth的适用场合,让我举一个假设的例子。

    有一个”云冲印”的网站,可以将用户储存在Google的照片,冲印出来。用户为了使用该服务,必须让”云冲印”读取自己储存在Google上的照片。

    bg2014051202

    问题是只有得到用户的授权,Google才会同意”云冲印”读取这些照片。那么,”云冲印”怎样获得用户的授权呢?

    传统方法是,用户将自己的Google用户名和密码,告诉”云冲印”,后者就可以读取用户的照片了。这样的做法有以下几个严重的缺点。

    1. “云冲印”为了后续的服务,会保存用户的密码,这样很不安全。
    2. Google不得不部署密码登录,而我们知道,单纯的密码登录并不安全。
    3. “云冲印”拥有了获取用户储存在Google所有资料的权力,用户没法限制”云冲印”获得授权的范围和有效期。
    4. 用户只有修改密码,才能收回赋予”云冲印”的权力。但是这样做,会使得其他所有获得用户授权的第三方应用程序全部失效。
    5. 只要有一个第三方应用程序被破解,就会导致用户密码泄漏,以及所有被密码保护的数据泄漏。

    OAuth就是为了解决上面这些问题而诞生的。

    二、名词定义

    在详细讲解OAuth 2.0之前,需要了解几个专用名词。它们对读懂后面的讲解,尤其是几张图,至关重要。

    1. Third-party application:第三方应用程序,本文中又称”客户端”(client),即上一节例子中的”云冲印”。
    2. HTTP service:HTTP服务提供商,本文中简称”服务提供商”,即上一节例子中的Google。
    3. Resource Owner:资源所有者,本文中又称”用户”(user)。
    4. User Agent:用户代理,本文中就是指浏览器。
    5. Authorization server:认证服务器,即服务提供商专门用来处理认证的服务器。
    6. Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器。

    知道了上面这些名词,就不难理解,OAuth的作用就是让”客户端”安全可控地获取”用户”的授权,与”服务商提供商”进行互动。

    三、OAuth的思路

    OAuth在”客户端”与”服务提供商”之间,设置了一个授权层(authorization layer)。”客户端”不能直接登录”服务提供商”,只能登录授权层,以此将用户与客户端区分开来。”客户端”登录授权层所用的令牌(token),与用户的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期。

    “客户端”登录授权层以后,”服务提供商”根据令牌的权限范围和有效期,向”客户端”开放用户储存的资料。

    四、运行流程

    OAuth 2.0的运行流程如下图,摘自RFC 6749。

    bg2014051203

    • (A)用户打开客户端以后,客户端要求用户给予授权。
    • (B)用户同意给予客户端授权。
    • (C)客户端使用上一步获得的授权,向认证服务器申请令牌。
    • (D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
    • (E)客户端使用令牌,向资源服务器申请获取资源。
    • (F)资源服务器确认令牌无误,同意向客户端开放资源。

    不难看出来,上面六个步骤之中,B是关键,即用户怎样才能给于客户端授权。有了这个授权以后,客户端就可以获取令牌,进而凭令牌获取资源。

    下面一一讲解客户端获取授权的四种模式。

    五、客户端的授权模式

    客户端必须得到用户的授权(authorization grant),才能获得令牌(access token)。OAuth 2.0定义了四种授权方式。

    • 授权码模式(authorization code)
    • 简化模式(implicit)
    • 密码模式(resource owner password credentials)
    • 客户端模式(client credentials)

    六、授权码模式

    授权码模式(authorization code)是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与”服务提供商”的认证服务器进行互动。

    bg2014051204

    它的步骤如下:

    • (A)用户访问客户端,后者将前者导向认证服务器。
    • (B)用户选择是否给予客户端授权。
    • (C)假设用户给予授权,认证服务器将用户导向客户端事先指定的”重定向URI”(redirection URI),同时附上一个授权码。
    • (D)客户端收到授权码,附上早先的”重定向URI”,向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。
    • (E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。
    • 下面是上面这些步骤所需要的参数。

    A步骤中,客户端申请认证的URI,包含以下参数:

    • response_type:表示授权类型,必选项,此处的值固定为”code”
    • client_id:表示客户端的ID,必选项
    • redirect_uri:表示重定向URI,可选项
    • scope:表示申请的权限范围,可选项
    • state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。

    下面是一个例子。

    GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
            &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
    Host: server.example.com
    

    C步骤中,服务器回应客户端的URI,包含以下参数:

    • code:表示授权码,必选项。该码的有效期应该很短,通常设为10分钟,客户端只能使用该码一次,否则会被授权服务器拒绝。该码与客户端ID和重定向URI,是一一对应关系。
    • state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。

    下面是一个例子。

    HTTP/1.1 302 Found
    Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
              &state=xyz
    

    D步骤中,客户端向认证服务器申请令牌的HTTP请求,包含以下参数:

    • grant_type:表示使用的授权模式,必选项,此处的值固定为”authorization_code”。
    • code:表示上一步获得的授权码,必选项。
    • redirect_uri:表示重定向URI,必选项,且必须与A步骤中的该参数值保持一致。
    • client_id:表示客户端ID,必选项。

    下面是一个例子。

    POST /token HTTP/1.1
    Host: server.example.com
    Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
    &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
    

    E步骤中,认证服务器发送的HTTP回复,包含以下参数:

    • access_token:表示访问令牌,必选项。
    • token_type:表示令牌类型,该值大小写不敏感,必选项,可以是bearer类型或mac类型。
    • expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
    • refresh_token:表示更新令牌,用来获取下一次的访问令牌,可选项。
    • scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。

    下面是一个例子。

         HTTP/1.1 200 OK
         Content-Type: application/json;charset=UTF-8
         Cache-Control: no-store
         Pragma: no-cache
    
         {
           "access_token":"2YotnFZFEjr1zCsicMWpAA",
           "token_type":"example",
           "expires_in":3600,
           "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
           "example_parameter":"example_value"
         }
    

    从上面代码可以看到,相关参数使用JSON格式发送(Content-Type: application/json)。此外,HTTP头信息中明确指定不得缓存。

    七、简化模式

    简化模式(implicit grant type)不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了”授权码”这个步骤,因此得名。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。

    bg2014051205

    它的步骤如下:

    • (A)客户端将用户导向认证服务器。
    • (B)用户决定是否给于客户端授权。
    • (C)假设用户给予授权,认证服务器将用户导向客户端指定的”重定向URI”,并在URI的Hash部分包含了访问令牌。
    • (D)浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。
    • (E)资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。
    • (F)浏览器执行上一步获得的脚本,提取出令牌。
    • (G)浏览器将令牌发给客户端。

    下面是上面这些步骤所需要的参数。

    A步骤中,客户端发出的HTTP请求,包含以下参数:

    • response_type:表示授权类型,此处的值固定为”token”,必选项。
    • client_id:表示客户端的ID,必选项。
    • redirect_uri:表示重定向的URI,可选项。
    • scope:表示权限范围,可选项。
    • state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。

    下面是一个例子。

        GET /authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz
            &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
        Host: server.example.com
    

    C步骤中,认证服务器回应客户端的URI,包含以下参数:

    • access_token:表示访问令牌,必选项。
    • token_type:表示令牌类型,该值大小写不敏感,必选项。
    • expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
    • scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。
    • state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。

    下面是一个例子。

         HTTP/1.1 302 Found
         Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA
                   &state=xyz&token_type=example&expires_in=3600
    

    在上面的例子中,认证服务器用HTTP头信息的Location栏,指定浏览器重定向的网址。注意,在这个网址的Hash部分包含了令牌。

    根据上面的D步骤,下一步浏览器会访问Location指定的网址,但是Hash部分不会发送。接下来的E步骤,服务提供商的资源服务器发送过来的代码,会提取出Hash中的令牌。

    八、密码模式

    密码模式(Resource Owner Password Credentials Grant)中,用户向客户端提供自己的用户名和密码。客户端使用这些信息,向”服务商提供商”索要授权。

    在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下,比如客户端是操作系统的一部分,或者由一个著名公司出品。而认证服务器只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。

    bg2014051206

    它的步骤如下:

    • (A)用户向客户端提供用户名和密码。
    • (B)客户端将用户名和密码发给认证服务器,向后者请求令牌。
    • (C)认证服务器确认无误后,向客户端提供访问令牌。

    B步骤中,客户端发出的HTTP请求,包含以下参数:

    • grant_type:表示授权类型,此处的值固定为”password”,必选项。
    • username:表示用户名,必选项。
    • password:表示用户的密码,必选项。
    • scope:表示权限范围,可选项。

    下面是一个例子。

         POST /token HTTP/1.1
         Host: server.example.com
         Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
         Content-Type: application/x-www-form-urlencoded
    
         grant_type=password&username=johndoe&password=A3ddj3w
    

    C步骤中,认证服务器向客户端发送访问令牌,下面是一个例子。

         HTTP/1.1 200 OK
         Content-Type: application/json;charset=UTF-8
         Cache-Control: no-store
         Pragma: no-cache
    
         {
           "access_token":"2YotnFZFEjr1zCsicMWpAA",
           "token_type":"example",
           "expires_in":3600,
           "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
           "example_parameter":"example_value"
         }
    

    上面代码中,各个参数的含义参见《授权码模式》一节。

    整个过程中,客户端不得保存用户的密码。

    九、客户端模式

    客户端模式(Client Credentials Grant)指客户端以自己的名义,而不是以用户的名义,向”服务提供商”进行认证。严格地说,客户端模式并不属于OAuth框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以自己的名义要求”服务提供商”提供服务,其实不存在授权问题。

    bg2014051207

    它的步骤如下:

    • (A)客户端向认证服务器进行身份认证,并要求一个访问令牌。
    • (B)认证服务器确认无误后,向客户端提供访问令牌。

    A步骤中,客户端发出的HTTP请求,包含以下参数:

    • granttype:表示授权类型,此处的值固定为”clientcredentials”,必选项。
    • scope:表示权限范围,可选项。
         POST /token HTTP/1.1
         Host: server.example.com
         Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
         Content-Type: application/x-www-form-urlencoded
    
         grant_type=client_credentials
    

    认证服务器必须以某种方式,验证客户端身份。

    B步骤中,认证服务器向客户端发送访问令牌,下面是一个例子。

         HTTP/1.1 200 OK
         Content-Type: application/json;charset=UTF-8
         Cache-Control: no-store
         Pragma: no-cache
    
         {
           "access_token":"2YotnFZFEjr1zCsicMWpAA",
           "token_type":"example",
           "expires_in":3600,
           "example_parameter":"example_value"
         }
    

    上面代码中,各个参数的含义参见《授权码模式》一节。

    十、更新令牌

    如果用户访问的时候,客户端的”访问令牌”已经过期,则需要使用”更新令牌”申请一个新的访问令牌。

    客户端发出更新令牌的HTTP请求,包含以下参数:

    • granttype:表示使用的授权模式,此处的值固定为”refreshtoken”,必选项。
    • refresh_token:表示早前收到的更新令牌,必选项。
    • scope:表示申请的授权范围,不可以超出上一次申请的范围,如果省略该参数,则表示与上一次一致。

    下面是一个例子。

         POST /token HTTP/1.1
         Host: server.example.com
         Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
         Content-Type: application/x-www-form-urlencoded
    
         grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
    

    转载请注明:爱开源 » 理解OAuth 2.0

  • Linux修改用户密码-交互式与非交互式

    最近管理的一批机器,有个需求是要统一修改一个帐号的用户名密码,比如将qa帐号的密码改为1234,后来还为了脚本化,很方便的执行,还使用了非交互式地修改用户的密码。简单记录一下吧。

    1. 交互式配置本地用户的密码:passwd 命令
    [root@host_221-81 ~]# passwd qa
    Changing password for user qa.
    New password:
    BAD PASSWORD: it is too short
    BAD PASSWORD: is too simple
    Retype new password:
    passwd: all authentication tokens updated successfully.
    
    1. 非交互式修改本地用户的密码:chpasswd
    # chpasswd命令使用起来很简洁[root@host_221-81 ~]# echo "qa:1234" | chpasswd
    
    # 使用passwd命令,也可以实现非交互式修改密码[root@host_221-81 ~]# echo "1234" | passwd --stdin "qa"
    Changing password for user qa.
    passwd: all authentication tokens updated successfully.
    
    1. 使用expect来处理交互式输入,从而实现非交互式的密码修改。
    #!/bin/sh# \exec expect -f"$0""$@"if{$argc!= 2}{puts "Usage: $argv0 <username> <passwd>"exit1}set password [lindex $argv1]
    spawn passwd[lindex $argv0]sleep1
    expect "assword:"
    send "$password\r"
    expect "assword:"
    send "$password\r"
    expect eof
    

    注意:脚本的第二行,这种写法可能比较陌生,这是在TCL语言中的语法,The backslash is recognized as part of a comment to sh, but in Tcl the backslash continues the comment into the next line which keeps the exec command from executing again.

    该脚本的执行结果为:

    [root@smilejay ~]# ./change-pwd-expect.sh qa 1234
    spawn passwd qa
    Changing password for user qa.
    New password:
    BAD PASSWORD: it is too short
    BAD PASSWORD: is too simple
    Retype new password:
    passwd: all authentication tokens updated successfully.
    

    参考资料:http://wiki.tcl.tk/708#pagetoc593413fa

    相关文章

    转载请注明:爱开源 » Linux修改用户密码-交互式与非交互式

  • 数字证书原理

    文中首先解释了加密解密的一些基础知识和概念,然后通过一个加密通信过程的例子说明了加密算法的作用,以及数字证书的出现所起的作用。接着对数字证书做一个详细的解释,并讨论一下windows中数字证书的管理,最后演示使用makecert生成数字证书。如果发现文中有错误的地方,或者有什么地方说得不够清楚,欢迎指出!

    1、基础知识

    这部分内容主要解释一些概念和术语,最好是先理解这部分内容。

    1.1、公钥密码体制(public-key cryptography)

    公钥密码体制分为三个部分,公钥、私钥、加密解密算法,它的加密解密过程如下:

    • 加密:通过加密算法和公钥对内容(或者说明文)进行加密,得到密文。加密过程需要用到公钥。
    • 解密:通过解密算法和私钥对密文进行解密,得到明文。解密过程需要用到解密算法和私钥。注意,由公钥加密的内容,只能由私钥进行解密,也就是说,由公钥加密的内容,如果不知道私钥,是无法解密的。

    公钥密码体制的公钥和算法都是公开的(这是为什么叫公钥密码体制的原因),私钥是保密的。大家都以使用公钥进行加密,但是只有私钥的持有者才能解密。在实际的使用中,有需要的人会生成一对公钥和私钥,把公钥发布出去给别人使用,自己保留私钥。

    1.2、对称加密算法(symmetric key algorithms)

    在对称加密算法中,加密使用的密钥和解密使用的密钥是相同的。也就是说,加密和解密都是使用的同一个密钥。因此对称加密算法要保证安全性的话,密钥要做好保密,只能让使用的人知道,不能对外公开。这个和上面的公钥密码体制有所不同,公钥密码体制中加密是用公钥,解密使用私钥,而对称加密算法中,加密和解密都是使用同一个密钥,不区分公钥和私钥。

    // 密钥,一般就是一个字符串或数字,在加密或者解密时传递给加密/解密算法。前面在公钥密码体制中说到的公钥、私钥就是密钥,公钥是加密使用的密钥,私钥是解密使用的密钥。

    1.3、非对称加密算法(asymmetric key algorithms)

    在非对称加密算法中,加密使用的密钥和解密使用的密钥是不相同的。前面所说的公钥密码体制就是一种非对称加密算法,他的公钥和是私钥是不能相同的,也就是说加密使用的密钥和解密使用的密钥不同,因此它是一个非对称加密算法。

    1.4、RSA简介

    RSA是一种公钥密码体制,现在使用得很广泛。如果对RSA本身有兴趣的,后面看我有没有时间写个RSA的具体介绍。

    RSA密码体制是一种公钥密码体制,公钥公开,私钥保密,它的加密解密算法是公开的。 由公钥加密的内容可以并且只能由私钥进行解密,并且由私钥加密的内容可以并且只能由公钥进行解密。也就是说,RSA的这一对公钥、私钥都可以用来加密和解密,并且一方加密的内容可以由并且只能由对方进行解密

    1.5、签名和加密

    我们说加密,是指对某个内容加密,加密后的内容还可以通过解密进行还原。 比如我们把一封邮件进行加密,加密后的内容在网络上进行传输,接收者在收到后,通过解密可以还原邮件的真实内容。

    这里主要解释一下签名,签名就是在信息的后面再加上一段内容,可以证明信息没有被修改过,怎么样可以达到这个效果呢?一般是对信息做一个hash计算得到一个hash值,注意,这个过程是不可逆的,也就是说无法通过hash值得出原来的信息内容。在把信息发送出去时,把这个hash值加密后做为一个签名和信息一起发出去。 接收方在收到信息后,会重新计算信息的hash值,并和信息所附带的hash值(解密后)进行对比,如果一致,就说明信息的内容没有被修改过,因为这里hash计算可以保证不同的内容一定会得到不同的hash值,所以只要内容一被修改,根据信息内容计算的hash值就会变化。当然,不怀好意的人也可以修改信息内容的同时也修改hash值,从而让它们可以相匹配,为了防止这种情况,hash值一般都会加密后(也就是签名)再和信息一起发送,以保证这个hash值不被修改。至于如何让别人可以解密这个签名,这个过程涉及到数字证书等概念,我们后面在说到数字证书时再详细说明,这里您先只需先理解签名的这个概念。

    2、一个加密通信过程的演化

    我们来看一个例子,现在假设“服务器”和“客户”要在网络上通信,并且他们打算使用RSA(参看前面的RSA简介)来对通信进行加密以保证谈话内容的安全。由于是使用RSA这种公钥密码体制,“服务器”需要对外发布公钥(算法不需要公布,RSA的算法大家都知道),自己留着私钥。“客户”通过某些途径拿到了“服务器”发布的公钥,客户并不知道私钥。“客户”具体是通过什么途径获取公钥的,我们后面再来说明,下面看一下双方如何进行保密的通信:

    2.1 第一回合:

    “客户”->“服务器”:你好

    “服务器”->“客户”:你好,我是服务器

    “客户”->“服务器”:????

    因为消息是在网络上传输的,有人可以冒充自己是“服务器”来向客户发送信息。例如上面的消息可以被黑客截获如下:

    “客户”->“服务器”:你好

    “服务器”->“客户”:你好,我是服务器

    “客户”->“黑客”:你好 // 黑客在“客户”和“服务器”之间的某个路由器上截获“客户”发给服务器的信息,然后自己冒充“服务器”

    “黑客”->“客户”:你好,我是服务器

    因此“客户”在接到消息后,并不能肯定这个消息就是由“服务器”发出的,某些“黑客”也可以冒充“服务器”发出这个消息。如何确定信息是由“服务器”发过来的呢?有一个解决方法,因为只有服务器有私钥,所以如果只要能够确认对方有私钥,那么对方就是“服务器”。因此通信过程可以改进为如下:

    2.2 第二回合:

    “客户”->“服务器”:你好

    “服务器”->“客户”:你好,我是服务器

    “客户”->“服务器”:向我证明你就是服务器

    “服务器”->“客户”:你好,我是服务器 {你好,我是服务器}[私钥|RSA]

    // 意这里约定一下,{} 表示RSA加密后的内容,[ | ]表示用什么密钥和算法进行加密,后面的示例中都用这种表示方式,例如上面的 {你好,我是服务器}[私钥|RSA]就表示用私钥对“你好,我是服务器”进行加密后的结果。

    为了向“客户”证明自己是“服务器”, “服务器”把一个字符串用自己的私钥加密,把明文和加密后的密文一起发给“客户”。对于这里的例子来说,就是把字符串 “你好,我是服务器”和这个字符串用私钥加密后的内容 {你好,我是服务器}[私钥|RSA] 发给客户。

    “客户”收到信息后,她用自己持有的公钥解密密文,和明文进行对比,如果一致,说明信息的确是由服务器发过来的。也就是说“客户”把 {你好,我是服务器}[私钥|RSA] 这个内容用公钥进行解密,然后和“你好,我是服务器”对比。因为由“服务器”用私钥加密后的内容,由并且只能由公钥进行解密,私钥只有“服务器”持有,所以如果解密出来的内容是能够对得上的,那说明信息一定是从“服务器”发过来的。

    假设“黑客”想冒充“服务器”:

    “黑客”->“客户”:你好,我是服务器

    “客户”->“黑客”:向我证明你就是服务器

    “黑客”->“客户”:你好,我是服务器 {你好,我是服务器}[???|RSA] //这里黑客无法冒充,因为他不知道私钥,无法用私钥加密某个字符串后发送给客户去验证。

    “客户”->“黑客”:????

    由于“黑客”没有“服务器”的私钥,因此它发送过去的内容,“客户”是无法通过服务器的公钥解密的,因此可以认定对方是个冒牌货!

    到这里为止,“客户”就可以确认“服务器”的身份了,可以放心和“服务器”进行通信,但是这里有一个问题,通信的内容在网络上还是无法保密。为什么无法保密呢?通信过程不是可以用公钥、私钥加密吗?其实用RSA的私钥和公钥是不行的,我们来具体分析下过程,看下面的演示:

    2.3 第三回合:

    “客户”->“服务器”:你好

    “服务器”->“客户”:你好,我是服务器

    “客户”->“服务器”:向我证明你就是服务器

    “服务器”->“客户”:你好,我是服务器 {你好,我是服务器}[私钥|RSA]

    “客户”->“服务器”:{我的帐号是aaa,密码是123,把我的余额的信息发给我看看}[公钥|RSA]

    “服务器”->“客户”:{你的余额是100元}[私钥|RSA]

    注意上面的的信息 {你的余额是100元}[私钥],这个是“服务器”用私钥加密后的内容,但是我们之前说了,公钥是发布出去的,因此所有的人都知道公钥,所以除了“客户”,其它的人也可以用公钥对{你的余额是100元}[私钥]进行解密。所以如果“服务器”用私钥加密发给“客户”,这个信息是无法保密的,因为只要有公钥就可以解密这内容。然而“服务器”也不能用公钥对发送的内容进行加密,因为“客户”没有私钥,发送个“客户”也解密不了。

    这样问题就又来了,那又如何解决呢?在实际的应用过程,一般是通过引入对称加密来解决这个问题,看下面的演示:

    2.4 第四回合:

    “客户”->“服务器”:你好

    “服务器”->“客户”:你好,我是服务器

    “客户”->“服务器”:向我证明你就是服务器

    “服务器”->“客户”:你好,我是服务器 {你好,我是服务器}[私钥|RSA]

    “客户”->“服务器”:{我们后面的通信过程,用对称加密来进行,这里是对称加密算法和密钥}[公钥|RSA] //蓝色字体的部分是对称加密的算法和密钥的具体内容,客户把它们发送给服务器。

    “服务器”->“客户”:{OK,收到!}[密钥|对称加密算法]

    “客户”->“服务器”:{我的帐号是aaa,密码是123,把我的余额的信息发给我看看}[密钥|对称加密算法]

    “服务器”->“客户”:{你的余额是100元}[密钥|对称加密算法]

    在上面的通信过程中,“客户”在确认了“服务器”的身份后,“客户”自己选择一个对称加密算法和一个密钥,把这个对称加密算法和密钥一起用公钥加密后发送给“服务器”。注意,由于对称加密算法和密钥是用公钥加密的,就算这个加密后的内容被“黑客”截获了,由于没有私钥,“黑客”也无从知道对称加密算法和密钥的内容。

    由于是用公钥加密的,只有私钥能够解密,这样就可以保证只有服务器可以知道对称加密算法和密钥,而其它人不可能知道(这个对称加密算法和密钥是“客户”自己选择的,所以“客户”自己当然知道如何解密加密)。这样“服务器”和“客户”就可以用对称加密算法和密钥来加密通信的内容了。

    总结一下,RSA加密算法在这个通信过程中所起到的作用主要有两个:

    • 因为私钥只有“服务器”拥有,因此“客户”可以通过判断对方是否有私钥来判断对方是否是“服务器”。
    • 客户端通过RSA的掩护,安全的和服务器商量好一个对称加密算法和密钥来保证后面通信过程内容的安全。

    如果这里您理解了为什么不用RSA去加密通信过程,而是要再确定一个对称加密算法来保证通信过程的安全,那么就说明前面的内容您已经理解了。(如果不清楚,再看下2.3和2.4,如果还是不清楚,那应该是我们说清楚,您可以留言提问。)

    到这里,“客户”就可以确认“服务器”的身份,并且双方的通信内容可以进行加密,其他人就算截获了通信内容,也无法解密。的确,好像通信的过程是比较安全了。

    但是这里还留有一个问题,在最开始我们就说过,“服务器”要对外发布公钥,那“服务器”如何把公钥发送给“客户”呢?我们第一反应可能会想到以下的两个方法:

    a)把公钥放到互联网的某个地方的一个下载地址,事先给“客户”去下载。

    b)每次和“客户”开始通信时,“服务器”把公钥发给“客户”。

    但是这个两个方法都有一定的问题,

    对于a)方法,“客户”无法确定这个下载地址是不是“服务器”发布的,你凭什么就相信这个地址下载的东西就是“服务器”发布的而不是别人伪造的呢,万一下载到一个假的怎么办?另外要所有的“客户”都在通信前事先去下载公钥也很不现实。

    对于b)方法,也有问题,因为任何人都可以自己生成一对公钥和私钥,他只要向“客户”发送他自己的私钥就可以冒充“服务器”了。示意如下:

    “客户”->“黑客”:你好 //黑客截获“客户”发给“服务器”的消息

    “黑客”->“客户”:你好,我是服务器,这个是我的公钥 //黑客自己生成一对公钥和私钥,把公钥发给“客户”,自己保留私钥

    “客户”->“黑客”:向我证明你就是服务器

    “黑客”->“客户”:你好,我是服务器 {你好,我是服务器}[黑客自己的私钥|RSA] //客户收到“黑客”用私钥加密的信息后,是可以用“黑客”发给自己的公钥解密的,从而会误认为“黑客”是“服务器”

    因此“黑客”只需要自己生成一对公钥和私钥,然后把公钥发送给“客户”,自己保留私钥,这样由于“客户”可以用黑客的公钥解密黑客的私钥加密的内容,“客户”就会相信“黑客”是“服务器”,从而导致了安全问题。这里问题的根源就在于,大家都可以生成公钥、私钥对,无法确认公钥对到底是谁的。如果能够确定公钥到底是谁的,就不会有这个问题了。例如,如果收到“黑客”冒充“服务器”发过来的公钥,经过某种检查,如果能够发现这个公钥不是“服务器”的就好了。

    为了解决这个问题,数字证书出现了,它可以解决我们上面的问题。先大概看下什么是数字证书,一个证书包含下面的具体内容:

    • 证书的发布机构
    • 证书的有效期
    • 公钥
    • 证书所有者(Subject)
    • 签名所使用的算法
    • 指纹以及指纹算法

    证书的内容的详细解释会在后面详细解释,这里先只需要搞清楚一点,数字证书可以保证数字证书里的公钥确实是这个证书的所有者(Subject)的,或者证书可以用来确认对方的身份。也就是说,我们拿到一个数字证书,我们可以判断出这个数字证书到底是谁的。至于是如何判断的,后面会在详细讨论数字证书时详细解释。现在把前面的通信过程使用数字证书修改为如下:

    2.5 第五回合:

    “客户”->“服务器”:你好

    “服务器”->“客户”:你好,我是服务器,这里是我的数字证书 //这里用证书代替了公钥

    “客户”->“服务器”:向我证明你就是服务器

    “服务器”->“客户”:你好,我是服务器 {你好,我是服务器}[私钥|RSA]

    注意,上面第二次通信,“服务器”把自己的证书发给了“客户”,而不是发送公钥。“客户”可以根据证书校验这个证书到底是不是“服务器”的,也就是能校验这个证书的所有者是不是“服务器”,从而确认这个证书中的公钥的确是“服务器”的。后面的过程和以前是一样,“客户”让“服务器”证明自己的身份,“服务器”用私钥加密一段内容连同明文一起发给“客户”,“客户”把加密内容用数字证书中的公钥解密后和明文对比,如果一致,那么对方就确实是“服务器”,然后双方协商一个对称加密来保证通信过程的安全。到这里,整个过程就完整了,我们回顾一下:

    2.6 完整过程:

    step1: “客户”向服务端发送一个通信请求

    “客户”->“服务器”:你好

    step2: “服务器”向客户发送自己的数字证书。证书中有一个公钥用来加密信息,私钥由“服务器”持有

    “服务器”->“客户”:你好,我是服务器,这里是我的数字证书

    step3: “客户”收到“服务器”的证书后,它会去验证这个数字证书到底是不是“服务器”的,数字证书有没有什么问题,数字证书如果检查没有问题,就说明数字证书中的公钥确实是“服务器”的。检查数字证书后,“客户”会发送一个随机的字符串给“服务器”用私钥去加密,服务器把加密的结果返回给“客户”,“客户”用公钥解密这个返回结果,如果解密结果与之前生成的随机字符串一致,那说明对方确实是私钥的持有者,或者说对方确实是“服务器”。

    “客户”->“服务器”:向我证明你就是服务器,这是一个随机字符串 //前面的例子中为了方便解释,用的是“你好”等内容,实际情况下一般是随机生成的一个字符串。

    “服务器”->“客户”:{一个随机字符串}[私钥|RSA]

    step4: 验证“服务器”的身份后,“客户”生成一个对称加密算法和密钥,用于后面的通信的加密和解密。这个对称加密算法和密钥,“客户”会用公钥加密后发送给“服务器”,别人截获了也没用,因为只有“服务器”手中有可以解密的私钥。这样,后面“服务器”和“客户”就都可以用对称加密算法来加密和解密通信内容了。

    “服务器”->“客户”:{OK,已经收到你发来的对称加密算法和密钥!有什么可以帮到你的?}[密钥|对称加密算法]

    “客户”->“服务器”:{我的帐号是aaa,密码是123,把我的余额的信息发给我看看}[密钥|对称加密算法]

    “服务器”->“客户”:{你好,你的余额是100元}[密钥|对称加密算法]

    …… //继续其它的通信

    2.7 其它问题:

    上面的过程已经十分接近HTTPS的真实通信过程了,完全可以按照这个过程去理解HTTPS的工作原理。但是我为了方便解释,上面有些细节没有说到,有兴趣的人可以看下这部分的内容。可以跳过不看,无关紧要。

    【问题1】

    上面的通信过程中说到,在检查完证书后,“客户”发送一个随机的字符串给“服务器”去用私钥加密,以便判断对方是否真的持有私钥。但是有一个问题,“黑客”也可以发送一个字符串给“服务器”去加密并且得到加密后的内容,这样对于“服务器”来说是不安全的,因为黑客可以发送一些简单的有规律的字符串给“服务器”加密,从而寻找加密的规律,有可能威胁到私钥的安全。所以说,“服务器”随随便便用私钥去加密一个来路不明的字符串并把结果发送给对方是不安全的。

    〖解决方法〗

    每次收到“客户”发来的要加密的的字符串时,“服务器”并不是真正的加密这个字符串本身,而是把这个字符串进行一个hash计算,加密这个字符串的hash值(不加密原来的字符串)后发送给“客户”,“客户”收到后解密这个hash值并自己计算字符串的hash值然后进行对比是否一致。也就是说,“服务器”不直接加密收到的字符串,而是加密这个字符串的一个hash值,这样就避免了加密那些有规律的字符串,从而降低被破解的机率。“客户”自己发送的字符串,因此它自己可以计算字符串的hash值,然后再把“服务器”发送过来的加密的hash值和自己计算的进行对比,同样也能确定对方是否是“服务器”。

    【问题2】

    在双方的通信过程中,“黑客”可以截获发送的加密了的内容,虽然他无法解密这个内容,但是他可以捣乱,例如把信息原封不动的发送多次,扰乱通信过程。

    〖解决方法〗

    可以给通信的内容加上一个序号或者一个随机的值,如果“客户”或者“服务器”接收到的信息中有之前出现过的序号或者随机值,那么说明有人在通信过程中重发信息内容进行捣乱,双方会立刻停止通信。有人可能会问,如果有人一直这么捣乱怎么办?那不是无法通信了? 答案是的确是这样的,例如有人控制了你连接互联网的路由器,他的确可以针对你。但是一些重要的应用,例如军队或者政府的内部网络,它们都不使用我们平时使用的公网,因此一般人不会破坏到他们的通信。

    【问题3】

    在双方的通信过程中,“黑客”除了简单的重复发送截获的消息之外,还可以修改截获后的密文修改后再发送,因为修改的是密文,虽然不能完全控制消息解密后的内容,但是仍然会破坏解密后的密文。因此发送过程如果黑客对密文进行了修改,“客户”和“服务器”是无法判断密文是否被修改的。虽然不一定能达到目的,但是“黑客”可以一直这样碰碰运气。

    〖解决方法〗

    在每次发送信息时,先对信息的内容进行一个hash计算得出一个hash值,将信息的内容和这个hash值一起加密后发送。接收方在收到后进行解密得到明文的内容和hash值,然后接收方再自己对收到信息内容做一次hash计算,与收到的hash值进行对比看是否匹配,如果匹配就说明信息在传输过程中没有被修改过。如果不匹配说明中途有人故意对加密数据进行了修改,立刻中断通话过程后做其它处理。

    3. 证书的构成和原理
    3.1 证书的构成和原理

    之前已经大概说了一个证书由什么构成,但是没有仔细进行介绍,这里对证书的内容做一个详细的介绍。先看下一个证书到底是个什么东西,在windows下查看一个证书时,界面是这样的,我们主要关注一下Details Tab页,其中的内容比较长,我滚动内容后后抓了三个图,把完整的信息显示出来:

    b1

    里面的内容比较多——Version、Serial number、Signature algorithm 等等,挑几个重要的解释一下。

    ◆Issuer (证书的发布机构)

    指出是什么机构发布的这个证书,也就是指明这个证书是哪个公司创建的(只是创建证书,不是指证书的使用者)。对于上面的这个证书来说,就是指”SecureTrust CA”这个机构。

    ◆Valid from , Valid to (证书的有效期)

    也就是证书的有效时间,或者说证书的使用期限。 过了有效期限,证书就会作废,不能使用了。

    ◆Public key (公钥)

    这个我们在前面介绍公钥密码体制时介绍过,公钥是用来对消息进行加密的,第2章的例子中经常用到的。这个数字证书的公钥是2048位的,它的值可以在图的中间的那个对话框中看得到,是很长的一串数字。

    ◆Subject (主题)

    这个证书是发布给谁的,或者说证书的所有者,一般是某个人或者某个公司名称、机构的名称、公司网站的网址等。 对于这里的证书来说,证书的所有者是Trustwave这个公司。

    ◆Signature algorithm (签名所使用的算法)

    就是指的这个数字证书的数字签名所使用的加密算法,这样就可以使用证书发布机构的证书里面的公钥,根据这个算法对指纹进行解密。指纹的加密结果就是数字签名(第1.5节中解释过数字签名)。

    ◆Thumbprint, Thumbprint algorithm (指纹以及指纹算法)

    这个是用来保证证书的完整性的,也就是说确保证书没有被修改过,这东西的作用和2.7中说到的第3个问题类似。 其原理就是在发布证书时,发布者根据指纹算法(一个hash算法)计算整个证书的hash值(指纹)并和证书放在一起,使用者在打开证书时,自己也根据指纹算法计算一下证书的hash值(指纹),如果和刚开始的值对得上,就说明证书没有被修改过,因为证书的内容被修改后,根据证书的内容计算的出的hash值(指纹)是会变化的。 注意,这个指纹会使用”SecureTrust CA”这个证书机构的私钥用签名算法(Signature algorithm)加密后和证书放在一起。

    注意,为了保证安全,在证书的发布机构发布证书时,证书的指纹和指纹算法,都会加密后再和证书放到一起发布,以防有人修改指纹后伪造相应的数字证书。这里问题又来了,证书的指纹和指纹算法用什么加密呢?他们是用证书发布机构的私钥进行加密的。可以用证书发布机构的公钥对指纹和指纹算法解密,也就是说证书发布机构除了给别人发布证书外,他自己本身也有自己的证书。证书发布机构的证书是哪里来的呢???这个证书发布机构的数字证书(一般由他自己生成)在我们的操作系统刚安装好时(例如windows xp等操作系统),这些证书发布机构的数字证书就已经被微软(或者其它操作系统的开发机构)安装在操作系统中了,微软等公司会根据一些权威安全机构的评估选取一些信誉很好并且通过一定的安全认证的证书发布机构,把这些证书发布机构的证书默认就安装在操作系统里面了,并且设置为操作系统信任的数字证书。这些证书发布机构自己持有与他自己的数字证书对应的私钥,他会用这个私钥加密所有他发布的证书的指纹作为数字签名。

    3.2 如何向证书的发布机构去申请证书

    举个例子方便大家理解,假设我们公司”ABC Company”花了1000块钱,向一个证书发布机构”SecureTrust CA”为我们自己的公司”ABC Company”申请了一张证书,注意,这个证书发布机构”SecureTrust CA”是一个大家公认并被一些权威机构接受的证书发布机构,我们的操作系统里面已经安装了”SecureTrust CA”的证书。”SecureTrust CA”在给我们发布证书时,把Issuer,Public key,Subject,Valid from,Valid to等信息以明文的形式写到证书里面,然后用一个指纹算法计算出这些数字证书内容的一个指纹,并把指纹和指纹算法用自己的私钥进行加密,然后和证书的内容一起发布,同时”SecureTrust CA”还会给一个我们公司”ABC Company”的私钥给到我们。我们花了1000块钱买的这个证书的内容如下:

    ×××××××××××××××证书内容开始×××××××××××××××××

    Issuer : SecureTrust CA

    Subject : ABC Company

    Valid from : 某个日期

    Valid to: 某个日期

    Public Key : 一串很长的数字

    …… 其它的一些证书内容……

    {证书的指纹和计算指纹所使用的指纹算法}[SecureTrust CA的私钥|RSA] //这个就是”SecureTrust CA”对这个证书的一个数字签名,表示这个证书确实是他发布的,有什么问题他会负责(收了我们1000块,出了问题肯定要负责任的)

    ×××××××××××××××证书内容结束×××××××××××××××××

    // 记不记得前面的约定?{} 表示RSA加密后的内容,[ | ]表示用什么密钥和算法进行加密

    我们”ABC Company”申请到这个证书后,我们把证书投入使用,我们在通信过程开始时会把证书发给对方,对方如何检查这个证书的确是合法的并且是我们”ABC Company”公司的证书呢?首先应用程序(对方通信用的程序,例如IE、OUTLook等)读取证书中的Issuer(发布机构)为”SecureTrust CA” ,然后会在操作系统中受信任的发布机构的证书中去找”SecureTrust CA”的证书,如果找不到,那说明证书的发布机构是个水货发布机构,证书可能有问题,程序会给出一个错误信息。 如果在系统中找到了”SecureTrust CA”的证书,那么应用程序就会从证书中取出”SecureTrust CA”的公钥,然后对我们”ABC Company”公司的证书里面的指纹和指纹算法用这个公钥进行解密,然后使用这个指纹算法计算”ABC Company”证书的指纹,将这个计算的指纹与放在证书中的指纹对比,如果一致,说明”ABC Company”的证书肯定没有被修改过并且证书是”SecureTrust CA” 发布的,证书中的公钥肯定是”ABC Company”的。对方然后就可以放心的使用这个公钥和我们”ABC Company”进行通信了。

    ★这个部分非常重要,一定要理解,您可以重新回顾一下之前的两章“1、基础知识”和“ 2、一个加密通信过程的演化”,然后再来理解这部分的内容。如果您把这节的内容看了几遍还没有搞懂证书的工作原理,您可以留言指出我没有说清楚的内容,我好方便进行修正。

    3.3 证书的发布机构

    前面已经初步介绍了一下证书发布机构,这里再深入讨论一下。

    其实所有的公司都可以发布证书,我们自己也可以去注册一家公司来专门给别人发布证书。但是很明显,我们自己的专门发布证书的公司是不会被那些国际上的权威机构认可的,人家怎么知道你是不是个狗屁皮包公司?因此微软在它的操作系统中,并不会信任我们这个证书发布机构,当应用程序在检查证书的合法信的时候,一看证书的发布机构并不是操作系统所信任的发布机构,就会抛出错误信息。也就是说windows操作系统中不会预先安装好我们这个证书发布机构的证书,不信任我们这个发布机构。

    不受信任的证书发布机构的危害

    为什么一个证书发布机构受不受信任这么重要?我们举个例子。假设我们开了一个狗屁公司来为别人发布证书,并且我和微软有一腿,微软在他们的操作系统中把我设置为了受信任的证书发布机构。现在如果有个小公司叫Wicrosoft 花了10块钱让我为他们公司申请了一个证书,并且公司慢慢壮大,证书的应用范围也越来越广。然后有个奸商的公司JS Company想冒充Wicrosoft,于是给了我¥10000,让我为他们颁布一个证书,但是证书的名字(Subject)要写Wicrosoft,假如我为了这¥10000,真的把证书给了他们,那么他们以后就可以使用这个证书来冒充Wicrosoft了。

    如果是一个优秀的证书发布机构,比如你要向他申请一个名字叫Wicrosoft的证书,它会让你提供很多资料证明你确实可以代表Wicrosoft这个公司,也就是说他回去核实你的身份。证书发布机构是要为他发布出的证书负法律责任的。

    到这里,你可能会想,TMD,那我们自己就不能发布证书吗?就一定要花钱去申请?当然不是,我们自己也可以成立证书发布机构,但是需要通过一些安全认证等等,只是有点麻烦。另外,如果数字证书只是要在公司内部使用,公司可以自己给自己生成一个证书,在公司的所有机器上把这个证书设置为操作系统信任的证书发布机构的证书(这句话仔细看清楚,有点绕口),这样以后公司发布的证书在公司内部的所有机器上就可以通过验证了(在发布证书时,把这些证书的Issuer(发布机构)设置为我们自己的证书发布机构的证书的Subject(主题)就可以了)。但是这只限于内部应用,因为只有我们公司自己的机器上设置了信任我们自己这个所谓的证书发布机构,而其它机器上并没有事先信任我们这个证书发布机构,所以在其它机器上,我们发布的证书就无法通过安全验证。

    4. 在windows中对数字证书进行管理
    4.1 查看、删除、安装 数字证书

    我们在上一章中说到了,我们的操作系统中会预先安装好一些证书发布机构的证书,我们看下在windows中如何找到这些证书,步骤如下:

    1)开始菜单->运行,输入mmc,回车

    2)在打开的窗口中选择 File-> Add/Remove Snap-in…

    3)然后在弹出的对话框的 Standalone Tab页里面点击 Add… 按钮

    4)在弹出的对对话框中选择 certificates 后点击 Add 按钮

    具体的步骤如下图所示:

    b2

    上面的步骤结束后,会又弹出一个对话框,里面有三个单选按钮如下:

    • My user account
    • Service account
    • Computer account

    可以选择第一或者第三个选项,用来查看当前用户的证书或整个计算里面安装的证书。我们这里就默认选择第一个,平时一般安装证书的时候都会给所有用户安装,所以选择第一个和第三个选项看到的证书会差不多。我们在左边的导航树中选中受信任的证书发布机构(Trusted Root Certificate Authorities),然后点击下面的证书(Certificates),在右边的区域中就可以看到所有的受信任的证书发布机构的证书。

    b3

    注意上面的图片中,右边我们选中的这个证书发布机构”SecureTrust CA”,我们前面在第3章3.2节中举例子的时候,就是去向这个证书发布机构申请的证书,由于我们申请的证书是这个机构发布的,所以应用程序在检查我们的证书的发布机构时(会检查我们证书的签名,确认是该机构发布的证书),就会发现是可以信任的证书发布机构,从而就会相信我们证书的真实性。

    删除数字证书很简单,直接在右边的列表中右键然后删除就可以了。

    数字证书的安装也比较简单,直接双击数字证书文件,会打开数字证书,对话框下面会有一个Install Certificate按钮,点击后就可以根据向导进行安装,如下图所示:

    b4

    这个证书是我自己生成的测试证书,在证书的导入向导里面,它会让你选择导入到什么位置,如果是一个我们自己信任的证书发布机构自己的证书,只要导入到Certificate Authorities就可以了。Trusted Root Certificate Authorities, Intermediate Certification Authorities, Third-Party Root Certification Authorities 都是可以的,他们只是对证书的发布机构做了一个分类,还有一些其它的证书类型,例如Personal(个人证书)等等,具体就不介绍了。安装的时候一般来说可以用默认的选择项一直”下一步”到底。

    4.2 如何自己创建证书

    每个证书发布机构都有自己的用来创建证书的工具,当然,具体他们怎么去创建一个证书的我也不太清楚,不同类型的证书都有一定的格式和规范,我没有仔细去研究过这部分内容。 微软为我们提供了一个用来创建证书的工具makecert.exe,在安装Visual Studio的时候会安装上。如果没有安装也无所谓,可以上网去下一个,搜索makecert就可以了。可以直接从我的博客下载,这是链接

    向一些正规的证书发布机构申请证书一般是要收费的(因为别人要花时间检查你的身份,确认有没有同名的证书等等),这里我们看下如何自己创建一个证书,为后面在IIS中配置Https做准备。

    我们用到的是makecert这个工具,微软有很详细的使用帮助,我这里只做一个简单的解释,详细的各种参数和使用方法请查看MSDN的makecert的帮助。但是里面有些参数说得不够清楚,而且还有遗漏的,可以参看我后面的解释作为一个补充。

    先看下makecert最简单的使用方式:

    makecert.exe test.cer

    上面的命令会在makecert.exe所在的目录生成一个证书文件test.cer的数字证书文件。可以双击证书打开,看看证书的内容如下:

    b5

    证书的发布机构是”Root Agency”,证书的主题(证书发布给谁)是”Joe’s-Software-Emporium”,因为我们没有指定把证书发布给谁,makecert自己给我们随便生成了一个公司的名字。另外还指定了公钥、签名算法(用来解密签名)、指纹和指纹算法等。

    注意,因为这个证书是由微软的工具生成的,严格来说它没什么发布机构,所以微软虚拟了一个叫做”Root Agency”的发布机构,默认情况下,windows里面安装了这个所谓的证书发布机构的证书,但是这证书默认情况下不是受信任的,原因很简单,这样做大家都可以用makecert来制作合法的数字证书了。如果我们自己硬是要,也可以把它设置为受信任的。

    下面我们看下其它的参数,比如我们要给网站 www.jefferysun.com 生成一个证书MyCA.cer,假设我们把makecert.exe放在C:盘下,命令行如下:

    makecert -r -pe -n “CN=10.30.146.206″ -b 01/01/2000 -e 01/01/2036 -eku 1.3.6.1.5.5.7.3.1 -ss my -sr localMachine -sky exchange -sp “Microsoft RSA SChannel Cryptographic Provider” -sy 12

    C:> makecert.exe –pe -r –n “CN=www.jefferysun.com” -ss my -sr LocalMachine -a sha1 -len 2048 MyCA.cer

    解释一下makecert的常用参数的意思:

    • -n 指定主题的名字,这个是有固定的格式的, CN=主题名字 ,CN应该是Certificate Name的缩写。我这里的主题的名字就是我们的IIS所在机器的IP。这里可以指定一些主题的其它附加信息,例如 O= *** 表示组织信息等等。
    • -r 创建自签署证书,意思就是说在生成证书时,将证书的发布机构设置为自己。
    • -pe 将所生成的私钥标记为可导出。注意,服务器发送证书给客户端的时候,客户端只能从证书里面获取公钥,私钥是无法获取的。如果我们指定了这个参数,证书在安装在机器上后,我们还可以从证书中导出私钥,默认情况下是不能导出私钥的。正规的途径发布的证书,是不可能让你导出私钥的。
    • -b –e 证书的有效期
    • -ss 证书的存储名称,就是windows证书存储区的目录名,如果不存在在的话就创建一个。
    • -sr 证书的存储位置,只有currentuser(默认值)或 localmachine两个值。
    • -sv 指定保存私钥的文件,文件里面除了包含私钥外,其实也包含了证书。这个文件是需要保密的,这个文件在服务端配置时是需要用到的。
    • 这个CN=10.30.146.206要与自己的服务器相对应,要不然在配置HTTPS的时候会出现错误
    • -a 指定签名算法,必须是md5或rsa1。(还记得签名算法的作用不?可以看一下3章的第1节中关于签名算法的介绍)
    • -in 指定证书发布机构的名称
    • -len 这个参数在中文的帮助文档中好像没有提到,但是这个其实很重要,用于指定公钥的位数,越大越安全,默认值是1024,推荐2048。我试了下,这个不为1024的倍数也是可以的。

    生成证书后可以进行安装,安装过程可以参看4.1节。

    转载请注明:爱开源 » 数字证书原理

  • 使用RDO 安装OpenStack Icehouse

    OpenStack 每半年发布一个版本,Icehouse 是最近的一个版本,相对于Havana 提供了更多的功能和驱动支持。本文是使用RedHat 提供的RDO 脚本进行部署的文档。
    RDO 部署方式比较快捷,但由于相关的yum 源都在国外,若直接安装,经常出现rpm 包获取失败导致的问题。故建议部署前,先把相关的软件源镜像到本地,修改DNS 指向。(注意,不能直接修改repos 库中的位置,因为在多节点部署时,RDO 会自动安装epel、forman 等repo文件,手动修改是来不及的)
    本文采用两节点方式部署,第一个节点node01 作为控制节点(身份认证、网络服务、计算调度服务、Cinder服务、镜像服务等)+计算节点;第二个节点node02 作为单纯的计算节点扩展。相关详细的概念请见OpenStack 官网,这里不再一一说明。

    1.系统环境
    两个节点:

    node01.linuxfly.org
    eth0: 192.168.48.213
    eth1: 10.0.48.213
    
    node02.linuxfly.org
    eth0: 192.168.48.214
    eth1: 10.0.48.214
    

    eth0作为管理网卡和外部网络连接网卡;eth1作为gre 通道的连接网卡,也是两个节点间数据沟通的网卡。

    2.配置本地软件源
    使用192.168.86.37 上的本地yum 源,根据新的脚本执行情况进行修改。
    安装前,需要确保rdo.fedorapeople.org 可正常解析到192.168.86.37:

    [root@gd2-cloud-037 ~]# vi /var/named/fedorapeople.org.master.zone
    $TTL 1D
    @       IN SOA root.repos.fedorapeople.org. repos.fedorapeople.org. (
                                            0       ; serial
                                            1D      ; refresh
                                            1H      ; retry
                                            1W      ; expire
                                            3H )    ; minimum
            NS      repos.fedorapeople.org.
    
    repos           IN      A       192.168.86.37
    rdo             IN      A       192.168.86.37
    

    测试:

    ping -c2 rdo.fedorapeople.org
    

    还要修改http 的虚拟主机配置:

    [root@gd2-cloud-037 ~]# vi /etc/httpd/conf.d/yum_vhost.conf
    
        ServerAdmin webmaster@vclound.com
        DocumentRoot /var/www/html/root/repos.fedorapeople.org/repos
        ServerName rdo.fedorapeople.org
        ErrorLog logs/rdo.fedorapeople.org-error_log
        CustomLog logs/rdo.fedorapeople.org-access_log common
    

    否则在使用RDO 安装时,可能会遇到错误:

    2014-05-23 18:18:07::INFO::shell::78::root:: [192.168.48.214] Executing script:
    (rpm -q 'rdo-release-icehouse' || yum install -y --nogpg http://rdo.fedorapeople.org/openstack/openstack-icehouse/rdo-release-icehouse-3.noarch.rpm) || true
    2014-05-23 18:18:19::INFO::shell::78::root:: [192.168.48.214] Executing script:
    yum-config-manager --enable openstack-icehouse
    2014-05-23 18:18:19::ERROR::run_setup::892::root:: Traceback (most recent call last):
      File "/usr/lib/python2.6/site-packages/packstack/installer/run_setup.py", line 887, in main
        _main(confFile)
      File "/usr/lib/python2.6/site-packages/packstack/installer/run_setup.py", line 574, in _main
        runSequences()
      File "/usr/lib/python2.6/site-packages/packstack/installer/run_setup.py", line 553, in runSequences
        controller.runAllSequences()
      File "/usr/lib/python2.6/site-packages/packstack/installer/setup_controller.py", line 84, in runAllSequences
        sequence.run(self.CONF)
      File "/usr/lib/python2.6/site-packages/packstack/installer/core/sequences.py", line 96, in run
        step.run(config=config)
      File "/usr/lib/python2.6/site-packages/packstack/installer/core/sequences.py", line 43, in run
        raise SequenceError(str(ex))
    SequenceError: Failed to set RDO repo on host 192.168.48.214:
    RPM file seems to be installed, but appropriate repo file is probably missing in /etc/yum.repos.d/
    
    2014-05-23 18:18:19::INFO::shell::78::root:: [192.168.48.213] Executing script:
    rm -rf /var/tmp/packstack/0c97dceac80e41b081bc8316ae439d88
    2014-05-23 18:18:19::INFO::shell::78::root:: [192.168.48.214] Executing script:
    rm -rf /var/tmp/packstack/20a0e6a1d0ba4ab0a0c4490ba3dc9fce
    

    启动防火墙:

    [root@node01 ~]# iptables -L -n
    Chain INPUT (policy ACCEPT)
    target     prot opt source               destination
    
    Chain FORWARD (policy ACCEPT)
    target     prot opt source               destination
    
    Chain OUTPUT (policy ACCEPT)
    target     prot opt source               destination
    [root@node01 ~]# service iptables save
    iptables:将防火墙规则保存到 /etc/sysconfig/iptables:     [确定]
    
    [root@node02 ~]# iptables -L -n
    Chain INPUT (policy ACCEPT)
    target     prot opt source               destination
    
    Chain FORWARD (policy ACCEPT)
    target     prot opt source               destination
    
    Chain OUTPUT (policy ACCEPT)
    target     prot opt source               destination
    [root@node02 ~]# service iptables save
    iptables:将防火墙规则保存到 /etc/sysconfig/iptables:     [确定]
    

    如果不执行该动作,可能执行RDO 时会遇到错误:

    ERROR : Error appeared during Puppet run: 192.168.48.214_prescript.pp
    Error: Could not start Service[iptables]: Execution of '/sbin/service iptables start' returned 6:
    

    3.安装软件

    [root@node01 ~]# wget http://rdo.fedorapeople.org/openstack/openstack-icehouse/rdo-release-icehouse-3.noarch.rpm
    [root@node01 ~]# rpm -ivh rdo-release-icehouse-3.noarch.rpm
    [root@node01 ~]# cat /etc/yum.repos.d/rdo-release.repo
    [openstack-icehouse]
    name=OpenStack Icehouse Repository
    baseurl=http://repos.fedorapeople.org/repos/openstack/openstack-icehouse/epel-6/
    enabled=1
    skip_if_unavailable=0
    gpgcheck=1
    gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-RDO-Icehouse
    priority=98
    

    因为foreman 的源还没有同步到本地,把域名加入到两个节点上:

    [root@node01 ~]# echo ‘208.74.145.172 yum.theforeman.org’ >> /etc/hosts

    否则,会报:

    http://yum.theforeman.org/releases/1.5/el6/x86_64/repodata/repomd.xml: [Errno 14] PYCURL ERROR 22 – “The requested URL returned error: 404 Not Found”

    安装RDO 脚本:

    [root@node01 ~]# yum install -y openstack-packstack
    Installed:
    openstack-packstack.noarch 0:2014.1.1-0.12.dev1068.el6Dependency Installed:
    openstack-packstack-puppet.noarch 0:2014.1.1-0.12.dev1068.el6 openstack-puppet-modules.noarch 0:2014.1-11.1.el6 ruby.x86_64 0:1.8.7.352-13.el6 ruby-irb.x86_64 0:1.8.7.352-13.el6 ruby-libs.x86_64 0:1.8.7.352-13.el6
    ruby-rdoc.x86_64 0:1.8.7.352-13.el6 rubygem-json.x86_64 0:1.5.5-1.el6 rubygems.noarch 0:1.3.7-5.el6 Complete!

    使用/dev/sdb 作为lvm 提供给cinder 使用:

    引用

    [root@node01 ~]# pvcreate /dev/sdb
    Physical volume “/dev/sdb” successfully created
    [root@node01 ~]# vgcreate cinder-volumes /dev/sdb
    Volume group “cinder-volumes” successfully created
    [root@node01 ~]# vgdisplay
    — Volume group —
    VG Name cinder-volumes
    System ID
    Format lvm2
    Metadata Areas 1
    Metadata Sequence No 1
    VG Access read/write
    VG Status resizable
    MAX LV 0
    Cur LV 0
    Open LV 0
    Max PV 0
    Cur PV 1
    Act PV 1
    VG Size 100.00 GiB
    PE Size 4.00 MiB
    Total PE 25599
    Alloc PE / Size 0 / 0
    Free PE / Size 25599 / 100.00 GiB
    VG UUID YkbC1M-UJuf-WXKS-se8W-yoZx-Y8JU-cvj9fN

    生成应答文件:

    [root@node01 ~]# packstack –gen-answer-file=openstack-icehouse-test-20140523.txt

    修改应答文件:
    确认需要安装的服务,以及相关服务的数据库密码,登陆密码等信息。

    引用

    [root@node01 ~]# egrep -v ‘^$|^#’ openstack-icehouse-test-20140523.txt
    [general]
    CONFIG_SSH_KEY=
    CONFIG_MYSQL_INSTALL=y
    CONFIG_GLANCE_INSTALL=y
    CONFIG_CINDER_INSTALL=y
    CONFIG_NOVA_INSTALL=y
    CONFIG_NEUTRON_INSTALL=y
    CONFIG_HORIZON_INSTALL=y
    CONFIG_SWIFT_INSTALL=y
    CONFIG_CEILOMETER_INSTALL=y
    CONFIG_HEAT_INSTALL=y
    CONFIG_CLIENT_INSTALL=y
    CONFIG_NTP_SERVERS=192.168.86.37
    CONFIG_NAGIOS_INSTALL=y
    EXCLUDE_SERVERS=
    CONFIG_DEBUG_MODE=n
    CONFIG_VMWARE_BACKEND=n
    CONFIG_VCENTER_HOST=
    CONFIG_VCENTER_USER=
    CONFIG_VCENTER_PASSWORD=
    CONFIG_VCENTER_CLUSTER_NAME=
    CONFIG_MYSQL_HOST=192.168.48.213
    CONFIG_MYSQL_USER=root
    CONFIG_MYSQL_PW=e37cb47f36294ec1
    CONFIG_AMQP_SERVER=rabbitmq
    CONFIG_AMQP_HOST=192.168.48.213
    CONFIG_AMQP_ENABLE_SSL=n
    CONFIG_AMQP_ENABLE_AUTH=n
    CONFIG_AMQP_NSS_CERTDB_PW=18698078ad434bb2b6d630352b8cfcb1
    CONFIG_AMQP_SSL_PORT=5671
    CONFIG_AMQP_SSL_CERT_FILE=/etc/pki/tls/certs/amqp_selfcert.pem
    CONFIG_AMQP_SSL_KEY_FILE=/etc/pki/tls/private/amqp_selfkey.pem
    CONFIG_AMQP_SSL_SELF_SIGNED=y
    CONFIG_AMQP_AUTH_USER=amqp_user
    CONFIG_AMQP_AUTH_PASSWORD=f459146266d54cfb
    CONFIG_KEYSTONE_HOST=192.168.48.213
    CONFIG_KEYSTONE_DB_PW=e6406987528f4d86
    CONFIG_KEYSTONE_ADMIN_TOKEN=6ee2056522fe45229463ad91cd0f9911
    CONFIG_KEYSTONE_ADMIN_PW=linuxfly # 管理员的密码
    CONFIG_KEYSTONE_DEMO_PW=demo
    CONFIG_KEYSTONE_TOKEN_FORMAT=PKI
    CONFIG_GLANCE_HOST=192.168.48.213
    CONFIG_GLANCE_DB_PW=4962746651af47d1
    CONFIG_GLANCE_KS_PW=ef3b37da000d430c
    CONFIG_CINDER_HOST=192.168.48.213
    CONFIG_CINDER_DB_PW=de717f5796ff4bbc
    CONFIG_CINDER_KS_PW=c7560ce891bd4c98
    CONFIG_CINDER_BACKEND=lvm
    CONFIG_CINDER_VOLUMES_CREATE=no # 因为已经创建cinder 使用的lvm,这个卷就不需要创建了
    CONFIG_CINDER_VOLUMES_SIZE=20G
    CONFIG_CINDER_GLUSTER_MOUNTS=
    CONFIG_CINDER_NFS_MOUNTS=
    CONFIG_NOVA_API_HOST=192.168.48.213
    CONFIG_NOVA_CERT_HOST=192.168.48.213
    CONFIG_NOVA_VNCPROXY_HOST=192.168.48.213
    CONFIG_NOVA_COMPUTE_HOSTS=192.168.48.213,192.168.48.214 # 提供计算资源的节点地址
    CONFIG_NOVA_CONDUCTOR_HOST=192.168.48.213
    CONFIG_NOVA_DB_PW=ae2a88c5486e473f
    CONFIG_NOVA_KS_PW=2cea90eab44e4d31
    CONFIG_NOVA_SCHED_HOST=192.168.48.213
    CONFIG_NOVA_SCHED_CPU_ALLOC_RATIO=16.0
    CONFIG_NOVA_SCHED_RAM_ALLOC_RATIO=1.5
    CONFIG_NOVA_COMPUTE_PRIVIF=eth1
    CONFIG_NOVA_NETWORK_HOSTS=192.168.48.213
    CONFIG_NOVA_NETWORK_MANAGER=nova.network.manager.FlatDHCPManager
    CONFIG_NOVA_NETWORK_PUBIF=eth0
    CONFIG_NOVA_NETWORK_PRIVIF=eth1
    CONFIG_NOVA_NETWORK_FIXEDRANGE=192.168.32.0/22
    CONFIG_NOVA_NETWORK_FLOATRANGE=10.3.4.0/22
    CONFIG_NOVA_NETWORK_DEFAULTFLOATINGPOOL=nova
    CONFIG_NOVA_NETWORK_AUTOASSIGNFLOATINGIP=n
    CONFIG_NOVA_NETWORK_VLAN_START=100
    CONFIG_NOVA_NETWORK_NUMBER=1
    CONFIG_NOVA_NETWORK_SIZE=255
    CONFIG_NEUTRON_SERVER_HOST=192.168.48.213
    CONFIG_NEUTRON_KS_PW=9163ba62da104edc
    CONFIG_NEUTRON_DB_PW=c44e1326a3ae436a
    CONFIG_NEUTRON_L3_HOSTS=192.168.48.213
    CONFIG_NEUTRON_L3_EXT_BRIDGE=br-ex
    CONFIG_NEUTRON_DHCP_HOSTS=192.168.48.213
    CONFIG_NEUTRON_LBAAS_HOSTS=
    #CONFIG_NEUTRON_L2_PLUGIN=openvswitch # 默认L2使用openvswitch,改为ml2
    CONFIG_NEUTRON_L2_PLUGIN=ml2
    CONFIG_NEUTRON_METADATA_HOSTS=192.168.48.213
    CONFIG_NEUTRON_METADATA_PW=f65edbe4677a4928
    #CONFIG_NEUTRON_ML2_TYPE_DRIVERS=local # 默认为local,本地模式,改为gre模式,下面还有一些相关的选项
    CONFIG_NEUTRON_ML2_TYPE_DRIVERS=gre
    #CONFIG_NEUTRON_ML2_TENANT_NETWORK_TYPES=local
    CONFIG_NEUTRON_ML2_TENANT_NETWORK_TYPES=gre
    CONFIG_NEUTRON_ML2_MECHANISM_DRIVERS=openvswitch
    CONFIG_NEUTRON_ML2_FLAT_NETWORKS=*
    CONFIG_NEUTRON_ML2_VLAN_RANGES=
    #CONFIG_NEUTRON_ML2_TUNNEL_ID_RANGES=
    CONFIG_NEUTRON_ML2_TUNNEL_ID_RANGES=100:1000
    CONFIG_NEUTRON_ML2_VXLAN_GROUP=
    CONFIG_NEUTRON_ML2_VNI_RANGES=
    CONFIG_NEUTRON_L2_AGENT=openvswitch
    CONFIG_NEUTRON_LB_TENANT_NETWORK_TYPE=local
    CONFIG_NEUTRON_LB_VLAN_RANGES=
    CONFIG_NEUTRON_LB_INTERFACE_MAPPINGS=
    #CONFIG_NEUTRON_OVS_TENANT_NETWORK_TYPE=local
    CONFIG_NEUTRON_OVS_TENANT_NETWORK_TYPE=gre
    CONFIG_NEUTRON_OVS_VLAN_RANGES=
    CONFIG_NEUTRON_OVS_BRIDGE_MAPPINGS=
    CONFIG_NEUTRON_OVS_BRIDGE_IFACES=
    #CONFIG_NEUTRON_OVS_TUNNEL_RANGES=
    CONFIG_NEUTRON_OVS_TUNNEL_RANGES=100:1000
    CONFIG_NEUTRON_OVS_TUNNEL_IF=eth1
    CONFIG_NEUTRON_OVS_VXLAN_UDP_PORT=4789
    CONFIG_OSCLIENT_HOST=192.168.48.213
    CONFIG_HORIZON_HOST=192.168.48.213
    CONFIG_HORIZON_SSL=n
    CONFIG_SSL_CERT=
    CONFIG_SSL_KEY=
    CONFIG_SWIFT_PROXY_HOSTS=192.168.48.213
    CONFIG_SWIFT_KS_PW=3c978e1c9d8c4706
    CONFIG_SWIFT_STORAGE_HOSTS=192.168.48.213
    CONFIG_SWIFT_STORAGE_ZONES=1
    CONFIG_SWIFT_STORAGE_REPLICAS=1
    CONFIG_SWIFT_STORAGE_FSTYPE=ext4
    CONFIG_SWIFT_HASH=3da3ecfbf7784ad6
    CONFIG_SWIFT_STORAGE_SIZE=2G
    CONFIG_PROVISION_DEMO=n # 不创建demo 项目
    CONFIG_PROVISION_TEMPEST=n
    CONFIG_PROVISION_DEMO_FLOATRANGE=172.24.4.224/28
    CONFIG_PROVISION_TEMPEST_REPO_URI=https://github.com/openstack/tempest.git
    CONFIG_PROVISION_TEMPEST_REPO_REVISION=master
    CONFIG_PROVISION_ALL_IN_ONE_OVS_BRIDGE=n
    CONFIG_HEAT_HOST=192.168.48.213
    CONFIG_HEAT_DB_PW=8358dc2482164097
    CONFIG_HEAT_AUTH_ENC_KEY=237cb003b0134395
    CONFIG_HEAT_KS_PW=fded3b8f93a5463a
    #CONFIG_HEAT_CLOUDWATCH_INSTALL=n
    CONFIG_HEAT_CLOUDWATCH_INSTALL=y
    #CONFIG_HEAT_CFN_INSTALL=n
    CONFIG_HEAT_CFN_INSTALL=y
    CONFIG_HEAT_CLOUDWATCH_HOST=192.168.48.213
    CONFIG_HEAT_CFN_HOST=192.168.48.213
    CONFIG_CEILOMETER_HOST=192.168.48.213
    CONFIG_CEILOMETER_SECRET=658f7e3e604b4bb9
    CONFIG_CEILOMETER_KS_PW=8a413f3ab3e748ed
    CONFIG_MONGODB_HOST=192.168.48.213
    CONFIG_NAGIOS_HOST=192.168.48.213
    CONFIG_NAGIOS_PW=ab42ae321e744f78
    CONFIG_USE_EPEL=y
    CONFIG_REPO=
    CONFIG_RH_USER=
    CONFIG_RH_PW=
    CONFIG_RH_BETA_REPO=n
    CONFIG_SATELLITE_URL=
    CONFIG_SATELLITE_USER=
    CONFIG_SATELLITE_PW=
    CONFIG_SATELLITE_AKEY=
    CONFIG_SATELLITE_CACERT=
    CONFIG_SATELLITE_PROFILE=
    CONFIG_SATELLITE_FLAGS=
    CONFIG_SATELLITE_PROXY=
    CONFIG_SATELLITE_PROXY_USER=
    CONFIG_SATELLITE_PROXY_PW=

    这里admin 用户的密码通过CONFIG_KEYSTONE_ADMIN_PW=linuxfly 设置,在安装完毕后,会在/root目录下有个环境变量的文件,可导入其中的值,进行命令行的操作(见最后的示例):

    引用

    [root@node01 ~]# cat keystonerc_admin
    export OS_USERNAME=admin
    export OS_TENANT_NAME=admin
    export OS_PASSWORD=linuxfly
    export OS_AUTH_URL=http://192.168.48.213:5000/v2.0/
    export PS1='[u@h W(keystone_admin)]$ ‘

    执行RDO 安装:

    引用

    [root@node01 ~]# packstack –answer-file=openstack-icehouse-test-20140523.txt
    Welcome to Installer setup utility
    Packstack changed given value to required value /root/.ssh/id_rsa.pubInstalling:
    Clean Up [ DONE ]
    Setting up ssh keys [ DONE ]
    Discovering hosts’ details [ DONE ]
    Adding pre install manifest entries [ DONE ]
    Installing time synchronization via NTP [ DONE ]
    Adding MySQL manifest entries [ DONE ]
    Adding AMQP manifest entries [ DONE ]
    Adding Keystone manifest entries [ DONE ]
    Adding Glance Keystone manifest entries [ DONE ]
    Adding Glance manifest entries [ DONE ]
    Installing dependencies for Cinder [ DONE ]
    Adding Cinder Keystone manifest entries [ DONE ]
    Adding Cinder manifest entries [ DONE ]
    Checking if the Cinder server has a cinder-volumes vg[ DONE ]
    Adding Nova API manifest entries [ DONE ]
    Adding Nova Keystone manifest entries [ DONE ]
    Adding Nova Cert manifest entries [ DONE ]
    Adding Nova Conductor manifest entries [ DONE ]
    Creating ssh keys for Nova migration [ DONE ]
    Gathering ssh host keys for Nova migration [ DONE ]
    Adding Nova Compute manifest entries [ DONE ]
    Adding Nova Scheduler manifest entries [ DONE ]
    Adding Nova VNC Proxy manifest entries [ DONE ]
    Adding Nova Common manifest entries [ DONE ]
    Adding Openstack Network-related Nova manifest entries[ DONE ]
    Adding Neutron API manifest entries [ DONE ]
    Adding Neutron Keystone manifest entries [ DONE ]
    Adding Neutron L3 manifest entries [ DONE ]
    Adding Neutron L2 Agent manifest entries [ DONE ]
    Adding Neutron DHCP Agent manifest entries [ DONE ]
    Adding Neutron LBaaS Agent manifest entries [ DONE ]
    Adding Neutron Metadata Agent manifest entries [ DONE ]
    Adding OpenStack Client manifest entries [ DONE ]
    Adding Horizon manifest entries [ DONE ]
    Adding Swift Keystone manifest entries [ DONE ]
    Adding Swift builder manifest entries [ DONE ]
    Adding Swift proxy manifest entries [ DONE ]
    Adding Swift storage manifest entries [ DONE ]
    Adding Swift common manifest entries [ DONE ]
    Adding Heat manifest entries [ DONE ]
    Adding Heat Keystone manifest entries [ DONE ]
    Adding Heat CloudWatch API manifest entries [ DONE ]
    Adding Heat CloudFormation API manifest entries [ DONE ]
    Adding MongoDB manifest entries [ DONE ]
    Adding Ceilometer manifest entries [ DONE ]
    Adding Ceilometer Keystone manifest entries [ DONE ]
    Adding Nagios server manifest entries [ DONE ]
    Adding Nagios host manifest entries [ DONE ]
    Adding post install manifest entries [ DONE ]
    Preparing servers [ DONE ]
    Installing Dependencies [ DONE ]
    Copying Puppet modules and manifests [ DONE ]
    Applying 192.168.48.214_prescript.pp
    Applying 192.168.48.213_prescript.pp
    192.168.48.213_prescript.pp: [ DONE ]
    192.168.48.214_prescript.pp: [ DONE ]
    Applying 192.168.48.214_ntpd.pp
    Applying 192.168.48.213_ntpd.pp
    192.168.48.214_ntpd.pp: [ DONE ]
    192.168.48.213_ntpd.pp: [ DONE ]
    Applying 192.168.48.213_mysql.pp
    Applying 192.168.48.213_amqp.pp
    192.168.48.213_mysql.pp: [ DONE ]
    192.168.48.213_amqp.pp: [ DONE ]
    Applying 192.168.48.213_keystone.pp
    Applying 192.168.48.213_glance.pp
    Applying 192.168.48.213_cinder.pp
    192.168.48.213_keystone.pp: [ DONE ]
    192.168.48.213_glance.pp: [ DONE ]
    192.168.48.213_cinder.pp: [ DONE ]
    Applying 192.168.48.213_api_nova.pp
    192.168.48.213_api_nova.pp: [ DONE ]
    Applying 192.168.48.213_nova.pp
    Applying 192.168.48.214_nova.pp
    192.168.48.214_nova.pp: [ DONE ]
    192.168.48.213_nova.pp: [ DONE ]
    Applying 192.168.48.214_neutron.pp
    Applying 192.168.48.213_neutron.pp
    192.168.48.214_neutron.pp: [ DONE ]
    192.168.48.213_neutron.pp: [ DONE ]
    Applying 192.168.48.213_osclient.pp
    Applying 192.168.48.213_horizon.pp
    192.168.48.213_osclient.pp: [ DONE ]
    192.168.48.213_horizon.pp: [ DONE ]
    Applying 192.168.48.213_ring_swift.pp
    192.168.48.213_ring_swift.pp: [ DONE ]
    Applying 192.168.48.213_swift.pp
    Applying 192.168.48.213_heat.pp
    192.168.48.213_swift.pp: [ DONE ]
    192.168.48.213_heat.pp: [ DONE ]
    Applying 192.168.48.213_heatcw.pp
    Applying 192.168.48.213_heatcnf.pp
    192.168.48.213_heatcw.pp: [ DONE ]
    192.168.48.213_heatcnf.pp: [ DONE ]
    Applying 192.168.48.213_mongodb.pp
    192.168.48.213_mongodb.pp: [ DONE ]
    Applying 192.168.48.213_ceilometer.pp
    Applying 192.168.48.213_nagios.pp
    Applying 192.168.48.214_nagios_nrpe.pp
    Applying 192.168.48.213_nagios_nrpe.pp
    192.168.48.214_nagios_nrpe.pp: [ DONE ]
    192.168.48.213_ceilometer.pp: [ DONE ]
    192.168.48.213_nagios.pp: [ DONE ]
    192.168.48.213_nagios_nrpe.pp: [ DONE ]
    Applying 192.168.48.214_postscript.pp
    Applying 192.168.48.213_postscript.pp
    192.168.48.214_postscript.pp: [ DONE ]
    192.168.48.213_postscript.pp: [ DONE ]
    Applying Puppet manifests [ DONE ]
    Finalizing [ DONE ] * Installation completed successfully ***

    Additional information:
    * File /root/keystonerc_admin has been created on OpenStack client host 192.168.48.213. To use the command line tools you need to source the file.
    * To access the OpenStack Dashboard browse to http://192.168.48.213/dashboard .
    Please, find your login credentials stored in the keystonerc_admin in your home directory.
    * To use Nagios, browse to http://192.168.48.213/nagios username : nagiosadmin, password : ab42ae321e744f78
    * The installation log file is available at: /var/tmp/packstack/20140524-002156-yBxGmU/openstack-setup.log
    * The generated manifests are available at: /var/tmp/packstack/20140524-002156-yBxGmU/manifests

    4.查看OpenStack 的运行状态

    引用

    [root@node01 ~]# ovs-vsctl show
    c4b035f0-98e4-4868-9e14-094ca5a952f4
    Bridge br-tun
    Port br-tun
    Interface br-tun
    type: internal
    Port patch-int
    Interface patch-int
    type: patch
    options: {peer=patch-tun}
    Port “gre-0a0030d6″
    Interface “gre-0a0030d6″
    type: gre
    options: {in_key=flow, local_ip=”10.0.48.213″, out_key=flow, remote_ip=”10.0.48.214″}
    Bridge br-int
    Port br-int
    Interface br-int
    type: internal
    Port patch-tun
    Interface patch-tun
    type: patch
    options: {peer=patch-int}
    ovs_version: “1.11.0”
    [root@node02 ~]# ovs-vsctl show
    6d99ed4b-2030-4e6f-bee0-9b782977b76e
    Bridge br-tun
    Port br-tun
    Interface br-tun
    type: internal
    Port patch-int
    Interface patch-int
    type: patch
    options: {peer=patch-tun}
    Port “gre-0a0030d5″
    Interface “gre-0a0030d5″
    type: gre
    options: {in_key=flow, local_ip=”10.0.48.214″, out_key=flow, remote_ip=”10.0.48.213″}
    Bridge br-int
    Port patch-tun
    Interface patch-tun
    type: patch
    options: {peer=patch-int}
    Port br-int
    Interface br-int
    type: internal
    ovs_version: “1.11.0”

    5.调整相关参数
    1)创建外部网络使用的桥接端口
    如果不创建该桥接端口,实例无法连接路由网关、外部网络以及meta-data 服务,l3-agent.log 提示:

    引用

    2014-05-24 01:43:42.096 3283 ERROR neutron.agent.l3_agent [req-5cbe551e-28e5-40d8-b3df-8def91cb5f81 None] The external network bridge ‘br-ex’ does not exist

    创建步骤:

    引用

    [root@node01 ~]# cat /etc/sysconfig/network-scripts/ifcfg-eth0
    DEVICE=eth0
    BOOTPROTO=none
    IPV6INIT=no
    MTU=1500
    ONBOOT=yes
    HWADDR=00:50:56:81:9a:e1
    USERCTL=no
    [root@node01 ~]# cat /etc/sysconfig/network-scripts/ifcfg-br-ex
    DEVICE=br-ex
    BOOTPROTO=none
    IPV6INIT=no
    MTU=1500
    NM_CONTROLLED=no
    ONBOOT=yes
    IPADDR=192.168.48.213
    NETMASK=255.255.255.0
    GATEWAY=192.168.48.1
    DNS1=192.168.86.37
    USERCTL=no[root@node01 ~]# ovs-vsctl add-br br-ex; ovs-vsctl add-port br-ex eth0; service network restart
    重启neutron-l3-agent 服务。
    [root@node01 ~]# /etc/init.d/neutron-l3-agent restart
    停止 neutron-l3-agent: [确定]
    正在启动 neutron-l3-agent: [确定]

    2)使用Memcache 作为token 的后端服务

    引用

    [root@node01 ~]# vi /etc/keystone/keystone.conf
    # Controls the token construction, validation, and revocation
    # operations. Core providers are
    # “keystone.token.providers.[pki|uuid].Provider”. (string
    # value)
    #provider=
    #provider=keystone.token.providers.pki.Provider
    provider=keystone.token.providers.uuid.Provider# Keystone Token persistence backend driver. (string value)
    #driver=keystone.token.backends.sql.Token
    #driver=keystone.token.backends.sql.Token
    driver=keystone.token.backends.memcache.Token

    重启memcached 服务:

    引用

    [root@node01 ~]# /etc/init.d/memcached restart
    停止 memcached: [确定]
    正在启动 memcached: [确定]

    重启相关服务:

    引用

    [root@node01 ~]# for i in chkconfig –list|grep ‘3:启用’|egrep ‘neutron|openstack’|grep -v neutron-ovs-cleanup|awk ‘{print $1}’; do service $i restart; done
    停止 neutron-dhcp-agent: [确定]
    正在启动 neutron-dhcp-agent: [确定]
    停止 neutron-l3-agent: [确定]
    正在启动 neutron-l3-agent: [确定]
    停止 neutron-metadata-agent: [确定]
    正在启动 neutron-metadata-agent: [确定]
    停止 neutron-openvswitch-agent: [确定]
    正在启动 neutron-openvswitch-agent: [确定]
    停止 neutron: [确定]
    正在启动 neutron: [确定]
    停止 openstack-ceilometer-alarm-evaluator: [确定]
    正在启动 openstack-ceilometer-alarm-evaluator: [确定]
    停止 openstack-ceilometer-alarm-notifier: [确定]
    正在启动 openstack-ceilometer-alarm-notifier: [确定]
    停止 openstack-ceilometer-api: [确定]
    正在启动 openstack-ceilometer-api: [确定]
    停止 openstack-ceilometer-central: [确定]
    正在启动 openstack-ceilometer-central: [确定]
    停止 openstack-ceilometer-collector: [确定]
    正在启动 openstack-ceilometer-collector: [确定]
    停止 openstack-ceilometer-compute: [确定]
    正在启动 openstack-ceilometer-compute: [确定]
    停止 openstack-cinder-api: [确定]
    正在启动 openstack-cinder-api: [确定]
    停止 openstack-cinder-backup: [确定]
    正在启动 openstack-cinder-backup: [确定]
    停止 openstack-cinder-scheduler: [确定]
    正在启动 openstack-cinder-scheduler: [确定]
    停止 openstack-cinder-volume: [确定]
    正在启动 openstack-cinder-volume: [确定]
    停止 openstack-glance-api: [确定]
    正在启动 openstack-glance-api: [确定]
    停止 openstack-glance-registry: [确定]
    正在启动 openstack-glance-registry: [确定]
    停止 openstack-heat-api: [确定]
    正在启动 openstack-heat-api: [确定]
    停止 openstack-heat-api-cfn: [确定]
    正在启动 openstack-heat-api-cfn: [确定]
    停止 openstack-heat-api-cloudwatch: [确定]
    正在启动 openstack-heat-api-cloudwatch: [确定]
    停止 openstack-heat-engine: [确定]
    正在启动 openstack-heat-engine: [确定]
    停止 keystone: [确定]
    正在启动 keystone: [确定]
    停止 openstack-nova-api: [确定]
    正在启动 openstack-nova-api: [确定]
    停止 openstack-nova-cert: [确定]
    正在启动 openstack-nova-cert: [确定]
    停止 openstack-nova-compute: [确定]
    正在启动 openstack-nova-compute: [确定]
    停止 openstack-nova-conductor: [确定]
    正在启动 openstack-nova-conductor: [确定]
    停止 openstack-nova-consoleauth: [确定]
    正在启动 openstack-nova-consoleauth: [确定]
    停止 openstack-nova-novncproxy: [确定]
    正在启动 openstack-nova-novncproxy: [确定]
    停止 openstack-nova-scheduler: [确定]
    正在启动 openstack-nova-scheduler: [确定]
    Stopping openstack-swift-account: [确定]
    Starting openstack-swift-account: [确定]
    Stopping openstack-swift-account-auditor: [确定]
    Starting openstack-swift-account-auditor: [确定]
    Stopping openstack-swift-account-reaper: [确定]
    Starting openstack-swift-account-reaper: [确定]
    Stopping openstack-swift-account-replicator: [确定]
    Starting openstack-swift-account-replicator: [确定]
    Stopping openstack-swift-container: [确定]
    Starting openstack-swift-container: [确定]
    Stopping openstack-swift-container-auditor: [确定]
    Starting openstack-swift-container-auditor: [确定]
    Stopping openstack-swift-container-replicator: [确定]
    Starting openstack-swift-container-replicator: [确定]
    Stopping openstack-swift-container-updater: [确定]
    Starting openstack-swift-container-updater: [确定]
    Stopping openstack-swift-object: [确定]
    Starting openstack-swift-object: [确定]
    Stopping openstack-swift-object-auditor: [确定]
    Starting openstack-swift-object-auditor: [确定]
    Stopping openstack-swift-object-replicator: [确定]
    Starting openstack-swift-object-replicator: [确定]
    Stopping openstack-swift-object-updater: [确定]
    Starting openstack-swift-object-updater: [确定]
    Stopping openstack-swift-proxy: [确定]
    Starting openstack-swift-proxy: [确定]

    关闭crontab 的定时任务:

    引用

    [root@node01 ~]# crontab -u keystone -e
    # HEADER: This file was autogenerated at Sat May 24 00:28:13 +0800 2014 by puppet.
    # HEADER: While it can still be managed manually, it is definitely not recommended.
    # HEADER: Note particularly that the comments starting with ‘Puppet Name’ should
    # HEADER: not be deleted, as doing so could cause duplicate cron jobs.
    # Puppet Name: token-flush
    #*/1 * * * * /usr/bin/keystone-manage token_flush >/dev/null 2>&1

    因为keystone-manage token_flush 是针对SQL 保存token的情况实现的,如果不关闭,执行会报错:

    引用

    2014-05-26 11:07:01.847 7258 CRITICAL keystone [-] NotImplemented: The action you have requested has not been implemented.
    2014-05-26 11:07:01.847 7258 TRACE keystone Traceback (most recent call last):
    2014-05-26 11:07:01.847 7258 TRACE keystone File “/usr/bin/keystone-manage”, line 51, in
    2014-05-26 11:07:01.847 7258 TRACE keystone cli.main(argv=sys.argv, config_files=config_files)
    2014-05-26 11:07:01.847 7258 TRACE keystone File “/usr/lib/python2.6/site-packages/keystone/cli.py”, line 190, in main
    2014-05-26 11:07:01.847 7258 TRACE keystone CONF.command.cmd_class.main()
    2014-05-26 11:07:01.847 7258 TRACE keystone File “/usr/lib/python2.6/site-packages/keystone/cli.py”, line 154, in main
    2014-05-26 11:07:01.847 7258 TRACE keystone token_manager.driver.flush_expired_tokens()
    2014-05-26 11:07:01.847 7258 TRACE keystone File “/usr/lib/python2.6/site-packages/keystone/token/backends/kvs.py”, line 355, in flush_expired_tokens
    2014-05-26 11:07:01.847 7258 TRACE keystone raise exception.NotImplemented()
    2014-05-26 11:07:01.847 7258 TRACE keystone NotImplemented: The action you have requested has not been implemented.
    2014-05-26 11:07:01.847 7258 TRACE keystone

    简单验证:

    引用

    [root@node01 ~]# source keystonerc_admin
    [root@node01 ~(keystone_admin)]# nova service-list
    +——————+———————+———-+———+——-+—————————-+—————–+
    | Binary | Host | Zone | Status | State | Updated_at | Disabled Reason |
    +——————+———————+———-+———+——-+—————————-+—————–+
    | nova-consoleauth | node01.linuxfly.org | internal | enabled | up | 2014-05-26T03:08:08.000000 | – |
    | nova-conductor | node01.linuxfly.org | internal | enabled | up | 2014-05-26T03:08:06.000000 | – |
    | nova-scheduler | node01.linuxfly.org | internal | enabled | up | 2014-05-26T03:08:08.000000 | – |
    | nova-compute | node01.linuxfly.org | nova | enabled | up | 2014-05-26T03:08:09.000000 | – |
    | nova-compute | node02.linuxfly.org | nova | enabled | up | 2014-05-26T03:08:08.000000 | – |
    | nova-cert | node01.linuxfly.org | internal | enabled | up | 2014-05-26T03:08:06.000000 | – |
    +——————+———————+———-+———+——-+—————————-+—————–+
    [root@node01 ~(keystone_admin)]# neutron agent-list
    +————————————–+——————–+———————+——-+—————-+
    | id | agent_type | host | alive | admin_state_up |
    +————————————–+——————–+———————+——-+—————-+
    | 04990a48-4b83-4999-ae29-1fbc33e9de3a | Metadata agent | node01.linuxfly.org | :-) | True |
    | 1d5758ca-73fa-4729-bd95-6a4bf8066c5f | L3 agent | node01.linuxfly.org | :-) | True |
    | 3babec70-d4bd-4135-8bd6-097ab7e22a54 | Open vSwitch agent | node02.linuxfly.org | :-) | True |
    | 431d2a91-2bc8-4f95-b574-9f6dc94cb49d | DHCP agent | node01.linuxfly.org | :-) | True |
    | c706de15-50fb-495b-868b-bb7a228d64d1 | Open vSwitch agent | node01.linuxfly.org | :-) | True |
    +————————————–+——————–+———————+——-+—————-+

    安装完成。

    应答文件:openstack-icehouse-test-20140523

    解压为openstack-icehouse-test-20140523.txt,可使用packstack –answer-file 进行重复安装。

    1. 使用RDO 安装OpenStack Icehouse
    2. OpenStack Icehouse Horizon 一 创建项目和用户
    3. OpenStack Icehouse Horizon 二 创建网络

    转载请注明:爱开源 » 使用RDO 安装OpenStack Icehouse

  • 不使用 expect 实现自动化 ssh 密码认证

    一般来说,自动化通过 ssh 执行操作或者通过 scp 传文件首先得过 ssh 认证这一关。采用公钥认证是最方便安全的方式。但是有时候不得不使用密码认证。而 ssh 默认是直接读写终端来输出提示信息和读入密码的,所以没法直接用echo password | ssh ...的方式来认证。expect 是最常用的用于解决这类问题的工具。但是这玩意实在是很不好用,也不能保证一定安装过。

    好在 ssh 还是开了一扇窗,让我们可以实现这点。ssh 有个环境变量,SSH_ASKPASS ,设置了这个环境变量,并且当前会话不是终端时,ssh 在认证时会启动这个程序,从这个程序的标准输出来读取密码。这个功能本来是用于图形终端的,所以还要设置另一个环境变量 DISPLAY=’none:0′,让 ssh 不要试图访问 X11 。至于让进程脱离终端,使用 setsid 就可以了。下面这个例子就展示了自动化实现密码认证并执行命令。

    echo  'echo BEGIN!; ls /' | setsid env SSH_ASKPASS='/root/pswd.sh' DISPLAY='none:0' ssh root@127.0.0.1 2>&1
    Pseudo-terminal will not be allocated because stdin is not a terminal.
    BEGIN!
    bin
    boot
    data
    dev
    etc
    home
    lib
    lib64
    lost+found
    media
    mnt
    opt
    proc
    root
    sbin
    selinux
    srv
    sys
    tmp
    usr
    var
    

    例子里的 /root/pswd.sh只需要简单输出密码,并确保当前用户可执行就可以了。比如

    #!/bin/bash
    echo 'PASSWORD'
    

    转载请注明:爱开源 » 不使用 expect 实现自动化 ssh 密码认证

  • 新型任意文件读取漏洞的研究

    0x00 前言


    早前发现boooom在乌云上发了很多个任意文件读取的漏洞,都是形如

    http://target/../../../../etc/passwd
    

    这样。当时感觉很新奇,因为正常情况下,通常的服务器中间件是不允许直接读取web目录以外的文件的,为什么这样的漏洞却出现在了很多案例中。

    后来在lijiejie的文章给出了解释:http://www.lijiejie.com/python-django-directory-traversal/ ,原来是python这种新型web开发方式造成的问题。然后翻了下我自己以前用web.py、tornado开发的一些应用,果然也存在这样的问题。

    这个问题就像lijiejie说的那样,一方面是低版本django框架自身的一些漏洞,另一方面,就是开发者自身的疏忽造成的问题。

    这不得不提到现今开发框架与以前的一些区别。不管是python还是node、ruby的框架,都是一个可以自定义URL分配的框架,不再是像php或asp中那样根据目录结构来请求文件。所有的请求由用户定义规则,而框架内核部分解析、配发、执行。比如我们请求的“/login/”这个URL,很可能是被配发给一个LoginHandler类去处理了,而不是请求到/login/index.php上。

    这时候造成了一个问题,如果我们就是想去请求一个真实的文件,比如css、js等静态文件,怎么办?

    一般也会有一些区分,一些要求比较高的应用,多是采用了CDN缓存或负载均衡,nginx作为负载分配的处理器。当发现我们请求的url是一个静态文件的话,就直接由CDN或nginx返回相应文件。如下图:

    2015030401212143740

    那么这之中也存在这一个定义问题,什么请求才说明是要请求“静态文件”?只要以.css、.js结尾就可以吗?当然这也是一种方法,但一般应用会定义一个目录,如/static/,所有请求匹配“/static/(.*)”的会被认为是静态文件,所以开发者一般将静态文件放在这个目录下,我们用户就能够直接请求到他了。

    如果不存在CDN、nginx等平台,其实类似web.py、tornado这样的框架自己也定义了静态目录,在web.py下,默认的静态目录都是/static/,也就是在这个目录下的请求是不会经过URLPath的。如web.py文档中说到的:

    http://webpy.org/cookbook/staticfiles.zh-cn

    这时候,我就会有这个思考,框架内部如果是以/static/(.*)来匹配请求的话,如果我们求

    /static/../../../../../etc/passwd
    

    是不是就可以读取到/etcs/passwd文件?

    0x01 web.py下可能的任意文件读取漏洞研究


    我们先来看看web.py是怎样处理这种请求的:

    /static/../../../../../etc/passwd
    

    我们在web/httpserver.py中可以看到这样的代码:

    def do_GET(self):
        if self.path.startswith('/static/'):
            SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
        else:
            self.run_wsgi_app()
    

    当请求以/static/开头的话,就直接交给SimpleHTTPServer处理了。SimpleHTTPServer是python自带的一个简单的HTTP Server,我们在任意一个目录下执行

    python -m SimpleHTTPServer
    

    都会启动一个web服务器,可以直接通过HTTP协议访问目录下的文件。

    web.py的Server其实就是对SimpleHTTPServer的一个继承与重写,这里它简单的把这个请求交给父类SimpleHTTPServer处理,而这个HTTP Server当然不会允许请求到web目录(也就是./static/)以外的地方去,所以得到的回复是404:

    2015030401212152015

    框架本身保证了静态文件不会造成任意文件读取。但复杂的逻辑关系应用中,开发者往往不满足于/static/一种静态目录。比如,网站允许用户上传、下载文件,可能我们会新建一个uploadfile目录,按日期、时间专门保存上传的文件。

    那么,开发者为了让/uploadfile目录下的文件也能被直接访问,往往会这样写:

    #!/usr/bin/python
    import web
    
    urls = (
        '/uploadfile/(.*)', 'download',
        '/', 'hello',
    )
    app = web.application(urls, globals())
    
    class hello:
        def GET(self, name):
            if not name:
                name = 'World'
            return 'Hello, ' + name + '!'
    
    class download:
        def GET(self, filepath):
            try:
                with open("./uploadfile/%s" % filepath, "rb") as f:
                    content = f.read()
                return content
            except:
                return web.notfound("Sorry, the file you were looking for was not found.")
    
    if __name__ == "__main__":
        app.run()
    

    有个download类专门解析这类请求,直接在GET方法中读取文件,并作为response写入HTTP数据包。

    我们请求一个正常的文件/uploadfile/01.txt,是可以得到它的内容的:

    2015030401212283650

    但我们请求一个非uploadfile目录下的文件,却发现也能读取,导致了一个任意文件读取漏洞:

    2015030401212244694

    这就是由于开发者的失误,并没有检查我们传入的path是否合法而导致,与框架无关。

    0x02 Tornado下可能的任意文件读取漏洞研究


    tornado是一个全异步的web框架,它允许我们在配置中定义静态目录static_path。在tornado中,专门给出了一个方法来验证我们的请求是否在允许的目录内:

    def validate_absolute_path(self, root, absolute_path):
        """Validate and return the absolute path.
    
        ``root`` is the configured path for the `StaticFileHandler`,
        and ``path`` is the result of `get_absolute_path`
    
        This is an instance method called during request processing,
        so it may raise `HTTPError` or use methods like
        `RequestHandler.redirect` (return None after redirecting to
        halt further processing).  This is where 404 errors for missing files
        are generated.
    
        This method may modify the path before returning it, but note that
        any such modifications will not be understood by `make_static_url`.
    
        In instance methods, this method's result is available as
        ``self.absolute_path``.
    
        .. versionadded:: 3.1
        """
        root = os.path.abspath(root)
        # os.path.abspath strips a trailing /
        # it needs to be temporarily added back for requests to root/
        if not (absolute_path + os.path.sep).startswith(root):
            raise HTTPError(403, "%s is not in root static directory",
                            self.path)
        if (os.path.isdir(absolute_path) and
                self.default_filename is not None):
            # need to look at the request.path here for when path is empty
            # but there is some prefix to the path that was already
            # trimmed by the routing
            if not self.request.path.endswith("/"):
                self.redirect(self.request.path + "/", permanent=True)
                return
            absolute_path = os.path.join(absolute_path, self.default_filename)
        if not os.path.exists(absolute_path):
            raise HTTPError(404)
        if not os.path.isfile(absolute_path):
            raise HTTPError(403, "%s is not a file", self.path)
        return absolute_path
    

    这样,一旦请求不在我们定义的静态目录下,就会抛出“is not in root static directory”错误:

    2015030401212211169

    那么如果tornado中,也需要定义一个”/uploadfile/“作为用户上传目录,那么我们怎么做?

    文档中也提到了,我们只需要自定义一个URLPath即可,tornado内部有专门处理静态文件的控制器web.StaticFileHandler:

    application = web.Application([
    
        (r"/uploadfile/(.*)", web.StaticFileHandler, {"path": "/var/www"}),
    
    ])
    

    就算不考虑安全问题,作为一个异步的框架,如果我们还用同步的read、write这些IO函数自己去处理静态文件,也是不可取的。

    不过,在后面的研究中,我也发现了tornado的处理方式并不算完美,更多详情可以等这个洞公开后查看:

    WooYun: Python开源框架Tornado某缺陷可能造成文件读取漏洞

    0x03 Django中的问题


    Django低版本自身存在的漏洞导致的任意文件读取,实际上就是犯了我之前说的静态文件未检查的问题,如这个09年的BUG:

    https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2009-2659

    2015030401212396741

    正如我在web.py中说到的,如果django也单纯使用open、read来读取文件,而不检查PATH的合法性,同样能够造出任意文件读取。

    比如我们django的view如此写:

    from django.shortcuts import render
    from django.http import HttpResponse
    
    # Create your views here.
    
    def index(request, path):
        with open(path, "rb") as f:
            content = f.read()
        return HttpResponse(content)
    

    没有验证path的合法性,依旧可以出现任意文件读取的现象:

    2015030401212353218

    如上图,直接读取了sqlite数据库内容,拿下管理员账号密码。

    那么我们怎么在这样的应用中防御任意文件读取?我们简单修改django的view防御这个漏洞:

    from django.shortcuts import render
    from django.http import HttpResponse
    from django.http import Http404
    import os
    
    # Create your views here.
    
    def index(request, path):
        static = "uploadfile"
        root = os.path.join(os.getcwd(), static) + os.path.sep
        path = os.path.abspath(root + path)
        if not (path).startswith(root):
            raise Http404
        else:
            with open(path, "rb") as f:
                content = f.read()
            return HttpResponse(content)
    

    其实有些框架都有自己检查静态文件的方式,django自身应该也有的。像web.py这样的轻型框架没有自带函数检查的情况下,可以考虑用我上面写的这个方法来剔除不合法的静态文件路径。

    0x04 PHP等语言会不会出现这个问题?


    这个问题其实是有思考价值的。

    最开始我就提到了,现代的python/node/ruby等web开发框架与老式的php、asp等语言的区别,也是造成这个漏洞的原因之一就是因为URL的分配导致静态文件不能被直接访问到,所以需要自定义静态文件的访问方式。但一旦访问参数未检查,就造成任意文件读取问题。

    但传统php应用就是一个以目录形式访问的,静态文件访问应该不会经过php的,这确实是一个很大的区别。

    先不论我们的请求会不会经过php,看到zblog最新版中一个实际的案例。

    /zb_system/function/c_system_event.php
    

    大概415行:

    if (isset($_SERVER['SERVER_SOFTWARE']) && (strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false) && (isset($_GET['rewrite']) == true)){
        //iis+httpd.ini下如果存在真实文件
        $realurl = $zbp->path . urldecode($url);
        if(is_readable($realurl)&&is_file($realurl)){
            die(file_get_contents($realurl));
        }
        unset($realurl);
    }
    

    这里判断了

    $_SERVER['SERVER_SOFTWARE']
    

    是否包含Microsoft-IIS,当服务器中间件是IIS,而且

    $_GET['rewrite']
    

    的话,就进入这个if语句。再判断$url指向的文件是否存在,存在就把它使用file_get_contents读取并显示出来。

    实际上这段代码所做的工作和我们之前看到的python代码是一样的。为什么php也需要这样的工作,我们url请求的文件不应该就直接由webserver返回给用户了吗?

    实际上,这里开发者也考虑了url重写造成的问题。zblog重写的规则是将所有请求都指向index.php去处理,最后由index.php去处理,有可能我们的静态文件就被rewrite到index.php去了,这里的工作就是把被重写到php里的这个静态文件直接显示出来。

    可惜我还是才疏学浅,不知道怎么写rewrite规则才能让静态文件请求被重写到index.php里,所以也做不到任意文件读取了。

    但我们这里很明显的可以发现,zblog的开发者也没有检查这个$url是否在网站目录内,或是否在静态文件目录内,也是直接读取显示了。导致我们请求http://10.211.55.3/zblog/index.php?action=&rewrite=1是可以读取index.php的源码的(因为我没有IIS环境,我手工将SERVER_SOFTWARE改成IIS了~):

    2015030401212379345

    所以,传统PHP应用下是否可能存在这样的安全漏洞,这个问题还是有待继续研究的。理论上,我们如果写出一个这样的rewrite规则:将所有请求都交给index.php处理。那么,index.php的功能实际上就和之前的python框架主文件功能类似了。

    0x05 结语


    最后,自己也只是浅显得研究和讨论了几种python框架、php某个特殊情况的漏洞,但现代的开发技术,包括企业级的一些环境我并不熟悉也没怎么接触过,所以有什么欠考虑和不完善的地方,也需要各位去补充与纠正。

    转载请注明:爱开源 » 新型任意文件读取漏洞的研究

  • OpenVPN使用postfix邮箱帐号进行身份认证 Python脚本

    这几天配置OpenVPN,使用了用户名密码的身份认证方式,借助已有的postfix邮箱帐号,省去了再为每个人设置用户名密码的麻烦。

    原理很简单,OpenVPN服务器配置里有这样一句:

    auth-user-pass-verify /etc/openvpn/auth-postfix-mailbox.py via-env

    就是说要用/etc/openvpn/auth-postfix-mailbox.py这个脚本来验证用户名和密码。用户名和密码如何传递给它呢?via-env,环境变量。

    脚本如下:

    #!/usr/bin/env python

    import os
    import sys
    from MySQLdb import *
    import md5crypt

    def auth(username, password):
    conn = connect (host = ‘localhost’,
    user = ‘dbuser’,
    passwd = ‘dbpasswd’,
    db = ‘postfix’)
    cursor = conn.cursor()
    cursor.execute(“””
    select password from mailbox
    where username=%s
    and active=1
    “””, (username))
    row = cursor.fetchone()
    if row == None:
    return 1
    crypt = md5crypt.md5crypt(password, row[0])
    cursor.execute(“””
    select * from mailbox
    where username=%s
    and password=%s
    and active=1
    “””, (username,crypt))
    row = cursor.fetchone()
    cursor.close()
    conn.close()
    if row == None:
    return 1
    return 0

    def main():
    status = 0
    try:
    username = os.environ[‘username’]
    password = os.environ[‘password’]
    status = auth(username, password)
    except:
    sys.exit(1)

    sys.exit(status)

    if name == “main”:
    main()

    由于postfix使用md5认证,所以需要用md5crypt这个模块,从这里可以下载到。

    转载请注明:爱开源 » OpenVPN使用postfix邮箱帐号进行身份认证 Python脚本

  • HeartBleed漏洞对安卓客户端的影响

    本周互联网最大的安全事件莫过于HeartBleed漏洞(CVE-2014-0160)的广泛公开。 如果你还不了解详情,可以参考这个分析。简言之,是由于OpenSSL在响应”心跳”时,对于数据包中的长度域不加检查,导致向另一端泄露了程序中最多可达64kb的内存。这是一个高危漏洞,影响范围很广,原因如下:

    – 绝大多数网站安全登录时,使用了OpenSSL,并存在此漏洞。

    – 攻击者向服务端发起攻击,可以获得服务端进程内的64kb数据,这些数据可能是任何东西:账号、密码、私钥等等

    – 该漏洞以及poc已经被广泛公开,大量脚本小子(script kiddie)参与攻击服务器

    根据互联网工程任务组(IETF)的RFC6250 文档可以发现,在握手完成之后,心跳包是可以双向传送的。如果客户端在使用一个存在漏洞的openssl版本,服务端向客户端发送心跳请求,同样存在“心脏流血”的隐患。

    根据Google官方声明,HeartBleed仅仅影响到4.1.1版本的Android系统。并且google也已经将补丁方案推送至各大厂商。根据我们的分析,此漏洞确实仅在4.1.1上生效,原因是仅有4.1.1的系统上的/system/lib/libssl.so支持heartbeat扩展。

    Heart

    图中是4.1.1中导出的存在漏洞的函数

    客户端链接服务端,反被服务端攻击,是有可能的。但版本限制得比较严格,通常安卓客户端内存数据利用价值也相对较低,因此这个问题危害较小。我们建议系统版本为4.1.1的安卓手机用户:

    – 检查自己的系统更新,如果有更新可用,立即升级。

    – 使用360手机卫士保护手机系统,并开启网址拦截功能。 不要访问不可信的网站,避免来自服务端的攻击。

    – 仅使用360手机助手等可信的安全应用市场下载app,避免该app访问利用漏洞的服务器

    – 不要使用公共场所中不可信的免费WiFi

    扩展阅读:

    比想象中更恐怖!OpenSSL“心血”漏洞深入分析

    http://blog.wangzhan.360.cn/?p=165

    Common Vulnerabilities and Exposures

    https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-0160

    Google Services Updated to Address OpenSSL CVE-2014-0160 (the Heartbleed bug)http://googleonlinesecurity.blogspot.com/2014/04/google-services-updated-to-address.html

    CyanogenMod 对于该漏洞的patch

    https://github.com/CyanogenMod/android_external_openssl/commit/8eb23b21293400219a64414bf6fab03713ea6299

    Heartbleed only an issue for Android 4.1.1

    http://androidcommunity.com/heartbleed-only-an-issue-for-android-4-1-1-20140409/

    TLS & DTLS Heartbeat Extension

    http://www.rfc-editor.org/rfc/pdfrfc/rfc6520.txt.pdf

    The Heartbleed Bug

    http://heartbleed.com/

    转载请注明:爱开源 » HeartBleed漏洞对安卓客户端的影响