在laravel中,默认的Auth使用的密码加密方式是Bcrypt散列密码,然后很多时候,我们想使用自己定义的加密方式,出于作业要求,我这次是要使用SHA256加密,当然,我们完全可以不使用Auth来自己实现,不过这里为了方便,我还是想直接使用Auth。

既然要使用Auth,那么我们就要对Auth模块进行扩展,在laravel的官方文档上,感觉讲的并不是很清楚,各种麻烦,于是最后自己查着资料研究了半天,得到了如下解决方案:

首先是官网上说的最详细的,也是最麻烦的方法,我们可以写一个自己的实现了UserProviderInterface接口的UserProvider类,然后通过Auth::extend来注册我们的驱动,最后到app/config/auth.php修改配置即可,具体可以看官网,这个方法还是说的比较详细的。

然后第二个方案是我正在采用的方案,通过研究laravel的框架代码发现,在AuthManager的createEloquentProvider中,我们可以发现如下代码:

/**
	 * Create an instance of the Eloquent user provider.
	 *
	 * @return IlluminateAuthEloquentUserProvider
	 */
	protected function createEloquentProvider()
	{
		$model = $this->app['config']['auth.model'];

		return new EloquentUserProvider($this->app['hash'], $model);
	}

很显然,就是在这里选择了hash算法,那么我们将最后这句话的参数替换成我们想要的hash算法就可以了。

这里的app['hash']里面是一个BcryptHasher,于是我们可以仿照BcryptHasher写出一个Sha256Hasher:

use IlluminateHashingHasherInterface;

class Sha256Hasher implements HasherInterface {

    /**
     * Hash the given value.
     *
     * @param  string  $value
     * @param  array   $options
     * @return string
     *
     * @throws RuntimeException
     */
    public function make($value, array $options = array())
    {
        $hash = hash('sha256', $value);

        return $hash;
    }

    /**
     * Check the given plain value against a hash.
     *
     * @param  string  $value
     * @param  string  $hashedValue
     * @param  array   $options
     * @return bool
     */
    public function check($value, $hashedValue, array $options = array())
    {
        return $this->make($value) === $hashedValue;
    }

    /**
     * Check if the given hash has been hashed using the given options.
     *
     * @param  string  $hashedValue
     * @param  array   $options
     * @return bool
     */
    public function needsRehash($hashedValue, array $options = array())
    {
        return false;
    }

}

然后我们还是采用extend的方式添加自己的驱动:

Auth::extend('sha256', function() {
    return new IlluminateAuthEloquentUserProvider(new ExtensionsHashingSha256Hasher(), 'SecurityModelsUser');
});

或者也可以不用Eloquent,直接使用database:

Auth::extend('sha256', function() {

    $connection = DB::connection();

    // When using the basic database user provider, we need to inject the table we
    // want to use, since this is not an Eloquent model we will have no way to
    // know without telling the provider, so we'll inject the config value.
    $table = Config::get('auth.table');

    return new IlluminateAuthDatabaseUserProvider($connection, new ExtensionsHashingSha256Hasher(), $table);
});

最后,我们还是在app/config/auth.php修改配置为sha256即可。

第三种方案应该算是官方文档中的基于IoC的扩展,和第二个有点类似,都是要实现一个Sha256Hasher,不过接下来要做的是就完全不一样了。

首先再添加一个Sha256HashServiceProvider,继承IlluminateSupportServiceProvider,以将Sha256Hasher注册为hash组件,也就是将上面所提到的app['hash']直接给替换掉,Sha256HashServiceProvider可以仿照HashServiceProvider来实现,代码如下:

use IlluminateSupportServiceProvider;

class Sha256HashServiceProvider extends ServiceProvider {

	/**
	 * Indicates if loading of the provider is deferred.
	 *
	 * @var bool
	 */
	protected $defer = true;

	/**
	 * Register the service provider.
	 *
	 * @return void
	 */
	public function register()
	{
		$this->app->bindShared('hash', function() { return new Sha256Hasher; });
	}

	/**
	 * Get the services provided by the provider.
	 *
	 * @return array
	 */
	public function provides()
	{
		return array('hash');
	}

}

最后,我们修改app/config/app.php中的providers,将其中下面一行删除:

'IlluminateHashingHashServiceProvider',

替换成自己刚实现的Sha256HashServiceProvider,即完成了散列方式的切换,这种方法意味着,laravel中所有使用到app['hash']来进行散列的全都会被替换成我们自己的散列方式,包括使用Hash::的时候。

code200这道题应该算是一道送分的大水题吧,题目意思很明确,不需要去猜,然后做法也很简单。

题目描述如下:

在218.2.197.248:10007运行了一个ATM程序,但是这个ATM有一个特点,每次只能存2^i(i为偶数)元或者取2^i(i为奇数)元,0<=i<99,且每个i只能使用一次。给出任意一定的金额(正数代表取出,负数代表存进),怎样操作这个ATM才能满足给定的金额?
eg:
13
4 3 2 0
983
10 5 3 1 0

话说这道题,赛后看样例的时候,突然发现,题目写反了,4 3 2 0 对应的应该是 – 2^4 + 2^3 – 2^2 – 2^0 = -13。

不要在意这些细节,我们继续看……

很显然,我们可以先考虑正数的情况,负数就是类似的做即可。

拿到的第一想法肯定是对要求的金额进行二进制分解,这样得到的东西,显然是不能直接过的,其中2^i(i为奇数)的情况是只能取,不能存。那么,我们就要将2^i(i为奇数)替换成存2^(i+1)并取2^i,然后这样一路向上传递上去,便得以解决。

得到程序如下:

#!/usr/bin/env python
#encoding:utf-8

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('218.2.197.248', 10007))

round_count = 1
while True:
    aim = s.recv(100)
    # aim = '-809'
    while aim == '':
        aim += s.recv(100)
    aim = int(aim)
    print aim

    sign = int(aim < 0)
    aim = abs(aim)
    bin_aim = bin(aim)[2:][::-1]
    bin_aim_len = len(bin_aim)
    bin_aim = [bool(c == '1') for c in (bin_aim + '0' * (100 - len(bin_aim)))]
    # print bin_aim

    ans = []
    for i in xrange(99):
        if bin_aim[i]:
            if i % 2 == sign:
                pass
            else:
                j = i + 1
                while bin_aim[j]:
                    bin_aim[j] = False
                    j += 1
                bin_aim[j] = True

    ans = ''
    for i in xrange(99, -1, -1):
        if bin_aim[i]:
            ans += str(i) + ' '
    print ans

    s.send(ans[:-1])
    # break
    round_count += 1
    print 'Round', round_count

今天为了做作业,又新建了一个laravel的项目,但是运行的时候却发现自己写在models中的类没有自动加载,很是郁闷。

检查了半天,和之前的那个laravel的项目对比了下,没发现什么config有问题。

于是乎,在网上查,找到了这个

由于Laravel是使用composer加载类的,如果不是使用命令创建的类是需要更新autoload的,正如@kankana说的 :composer dump-autoload,推荐看下这里http://segmentfault.com/a/1190000000355928

创建项目我是使用的是:

composer create-project laravel/laravel --prefer-dist

不知道这个是否满足上面所说的条件,但是,既然没有自动加载,那我们就还是上面说的,运行一下:

composer dump-autoload

或者是使用:

omposer dump-autoload --optimize

运行完之后再尝试,便会发现程序可以自动加载各个类了,至少表面上看起来,问题是得以解决了。

这次SCTF准确点说应该是第二次参加CTF的比赛,上一次就是HCTF了,本来上次也是准备写个小记和WriteUp的,但是出于时间的问题,就一直没有动手。

这次比赛的成绩应该说还算满意,或者甚至可以说是远超预期了吧,虽然说已知Blue-lotus、0ops、217都不在,压力小了很多,但是也绝对没想过我们能够拿到如此成绩,要知道,上次HCTF可是连纪念品都没能混到。

还记得HCTF的时候,当时题目应该说是比较水的吧,第一天的时候,我们还能混在第一页,第二天就毫不犹豫的各种出不来题,慢慢的就落到了20多名,当时说真的好伤心。尤其是最后看了题解之后,觉得有些题真的就是由于第一次参加,完全没有经验导致的没能做出来,印象最深刻的就是真的能做吗那道题,当时心中就一直认为,比赛中的两道题目直接是不会有关系的,所以根本就没想nc上挂着的就是之前逆向的那个程序,真是可惜了当时逆向的时候还在想,这个逆向的程序咋还有这么明显的buffer overflow问题……然后再就是矩阵那道题,看到它每轮的那个可以变到的数字上限每次游戏都不太一样就傻眼了,根本不知道是神马情况,就没能继续做下去……最后就是jianshu那题,我由于最后没事干,都跑去陪队友XSS了,结果好不容易XSS出来告诉flag不在cookie里,然后队友SQLi的时候还脑残的直接在URL上输入#,没有urlencode,导致明明可以搞定的没搞定。

这次SCTF总算没有上次那么惨,挤上第一面后就没有掉出去了,虽说这次最开始的时候,觉得比上次难很多,但却怎么也没有想到,做到比赛结束的时候,这次拿的分竟然跟上次差不多……不过有这样的结果还是得归功于这次题目出的太顺我的手的,PWN的题都是常规基础题,对于我这种没有脑洞的人再适合不过了,Code的题那就更不用说了,没啥压力,不过RE倒是让我好伤心,各种搞不定。

这次比赛最激动的时候想来就是最后2个小时了,之前由于脑子各种犯二,在PWN300和Code500上浪费了太多时间,导致最后Code500做出来的时候好像是就剩2个小时左右的时间了,然后本来以为游戏就要这么结束了的,结果看PWN400却惊奇的发现是这么的简单,比前面两个PWN要好做多了,于是乎大概在8点半的时候秒掉,一刷榜,竟然冲上了第二,当时那个激动啊,虽说还没激动几分钟,再刷的时候就被挤到了第三,然后就指望队友把手头那个MISC200搞定,争取绝杀,虽然最后还是未遂,但是最终这个结果其实也早已超过了我们赛前的预期了。

这次比赛,虽然最后结果还挺满意,但却有着一个说不出的痛,一直到最后,我们也没能过掉一道PT,是真的很努力的去做了还没过掉,应该说是除了我之外,其它队友都在玩PT,当然,有一个是MISC和PT都玩的,不然MISC也得惨死了~~~

不过,今天也算是得到了一个好消息,PT的所有题解都被放出来了,虽然没能做出了,但至少还能学习一下了。

然后不过不得不说,好不容易弄到这么个成绩,却还是不能实现去线下赛开次眼的目标,真的好郁闷……

最后,这次比赛的WriteUp决定还是要写一下,不过反正都已经有人放了,我也不急,也就先把Code500放了,其它的等过两天考试过了我再来补上。想想,剩下要写的大概也就我做的PWN200、PWN300、PWN400、Code200,然后再就是我也参合了的Code400、MISC100、RE50了。

code500这道题其实并不难,只是由于自己太错误的执着,导致花了不少时间。

题目描述如下:

女神Alice找回了钱钱,女神非常开心,然后就去洗澡了
这时Mallory已经逃走了!
plusplus7希望抓住Mallory,于是决定开挖掘机去追他。
刚刚坐上挖掘机,结果发现启动挖掘机需要把一些插头按照一定的顺序连接起来...
插头有两面,每个插头的两端都有颜色
如:
绿色和黑色插头 “G:B”
当插头的两个面颜色相同,才能被接在一起
如:
对接成功 “G:B = B:V”
对接失败 “G:B * C:G”
插座也有两面,一面有颜色,一面无颜色以"X"表示
如:
左插座 “X:B”
右插座 “C:X”
插座只有两个,且不可移动
如:
“X:B * C:G = G:E * B:C * E:X”
对挖掘机的siri输入指令,“3 3 1”
“X:B = B:C = C:G = G:E = E:X”
那么,问题来了...
218.2.197.248:7777

第一眼看到题目的时候,确实是被那个指令给搞懵了,但是稍微恢复了下神智之后,到nc上去试,试了半天总算猜对了指令的含义:

a b c 表示将第a到b个插头移动到第c个插头的前面(这里也将插座算入,编号从0开始)

弄明白指令的含义之后,说实在的,对于该怎么做其实还是没有什么好的想法(因为操作有步数限制,要在规定步数内完成才算有效)。

但是观察nc看到的数据发现,似乎所有的字母永远都只出现2次,即都是唯一匹配的,然后当时脑子里很快就有了个猜测:

对于两个相邻的未匹配的插头,假设编号为a – 1和a,left[i]、right[i]分别表示第i个插头的两个面的颜色,那么,一定存在b、c,使得left[a] = right[c]、right[a – 1] = left[b],如果b、c满足b < c且[a – 1, a]与[b, c]不相交,那么我们这一步就将[b, c]这一段移动到a – 1与a中间,即指令为b c a。

这个猜想很好理解,至于这样做是不是就是最好的抉择没太想清楚怎么证明,但感觉是可行的。

然后,对于不满足这个猜想的条件的,我最开始是采取随便取一个的方式,结果花了很多时间也没能解决,经常会比要求的步数多1步,最多好像是能跑到30多轮吧,但是看了下code200的数据组数,50组,于是觉得这题也是这个数,于是就对跑不出来的特例暴搜得到答案后硬编码判断,然后就成功的跑到了68轮,感觉已经无法再爱了……

最后,无奈,重写了一下搜索的代码,加上上面的那个猜测作为A*,同时限制了一下,保证每次移动都不会将两个已经配对的插头分开,然后就很轻松就秒了,最后结果有92轮……

#!/usr/bin/env python
#encoding:utf-8

import socket

def get_one(s):
    try:
        ret = s.recv(100)
        while ret.find(":Xn") == -1 and ret.find("SCTF") == -1:
            ret += s.recv(100)
        print ret
        return ret[:-1]
    except Exception, e:
        print 'Fail', ret
        return ret

def check(now, size):
    for i in xrange(1, size):
        if now[i - 1][2] != now[i][0]:
            return False
    return True

def swap(now, i, j, k, deep):
    history[deep] = [i, j, k]
    if k < i:
        return now[:k] + now[i:j + 1] + now[k:i] + now[j + 1:]
    elif k > j:
        return now[:i] + now[j + 1:k] + now[i:j + 1] + now[k:]
    return now

def dfs(now, deep, max_deep, size):
    if deep == max_deep:
        if check(now, size):
            return True
        return False
    for i in xrange(1, size):
        if now[i - 1][2] != now[i][0]:
            for j in xrange(1, size):
                if now[i - 1][2] == now[j][0]:
                    start = j
                    break
            for j in xrange(0, size - 1):
                if now[i][0] == now[j][2]:
                    stop = j
                    break
            if start <= stop and (start > i or stop < i - 1):
                return dfs(swap(now, start, stop, i, deep), deep + 1, max_deep, size)
    for i in xrange(1, size - 1):
        if now[i - 1][2] == now[i][0]:
            continue
        for j in xrange(i, size - 1):
            if now[j][2] == now[j + 1][0]:
                continue
            for k in xrange(1, i):
                if now[k - 1][2] != now[k][0] and dfs(swap(now, i, j, k, deep), deep + 1, max_deep, size):
                    return True
            for k in xrange(j + 2, size):
                if now[k - 1][2] != now[k][0] and dfs(swap(now, i, j, k, deep), deep + 1, max_deep, size):
                    return True
    return False

def get_answer(now, max_step):
    now = now.replace(' ', '').replace('=', '*').split('*')
    size = len(now)
    for i in xrange(1, max_step + 1):
        if dfs(now, 0, i, size):
            print history
            return i

history = [[] for i in range(10)]

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('218.2.197.248', 7777))
round_count = 1
ret = get_one(s)[len("Let's start this game!n"):]
while True:
    step = get_answer(ret, 7)
    for i in xrange(step):
        s.send("%d %d %d" % (history[i][0], history[i][1], history[i][2]))
        ret = get_one(s)
    print 'Round', round_count, 'finsh.n'
    round_count += 1
    ret = get_one(s)[len('Round pass...n'):]

重写代码的时间比之前小调优化的时间少了不知道多少倍,感觉大好的时光就这么浪费了,还好最后pwn400轻松秒了,不然估计真得为这个事情后悔好久~~~


关于代码写的丑的问题,各位看官就不用吐槽了,毕竟当时大脑已经是一半浆糊了,只要能跑出来,哪还有心情管代码美不美,直接在暴力上面改的,所以DFS都没有改成BFS,强行枚举了max deep,我也是服了我自己的……

这道题最后比赛结束之后竟然还被打电话challenge了,被质疑速度,看起来似乎标答应该不是搜索的样子,也不知道有没有哪位会愿意把标准解法分享一下了~~~

不得不说,这次的lab绝对是已经做过的几个lab中最麻烦的一个,为了做lab5,我们需要做的除了将lab5的代码补全,还需要修改lab1-lab4中的部分代码。

然后,还有个坑爹的事情就是,GitHub里面自带的答案make grade竟然只能得91分(满分150),我也是给亮瞎了,然后这题错了其实又不太好调,然后还听有同学说它同一段代码,曾经能得满分,后来好像是重装了还是怎么滴,就突然成no output了。

做的时候,还有个比较大的坑就是set_links这个函数中竟然将proc插入到了proc_list中,然后还把nr_process加了1,需要在do_fork中删除之前写的这两个操作,真不知道写的人是怎么想的,这两个步骤是怎么会想到加在set_links里面的,还好被同学提醒了,不然真不知道得被这个坑多久。

最后需要注意的大概就是check的时候需要把lab1中加的print_ticks功能,不然可能会导致make grade的时候个别需要运行时间比较长的check过不了。

还是老规矩,附上lab5的实验报告和修改的代码。

lab5.zip

由于作业需要,要求搭建一个SSL加密的安全的FTP服务器,在网上随便找了一下,因为比较偏好Python,便决定用pyftpdlib。

首先还是使用pip进行安装:

sudo pip install pyftpdlib

安装很顺利,没有遇到什么问题。

接下来是使用了,首先架设一个没有使用SSL的FTP服务器:

#!/usr/bin/env python
#encoding:utf-8

from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer


def main():
    # Instantiate a dummy authorizer for managing 'virtual' users
    authorizer = DummyAuthorizer()
    # Define a new user having full r/w permissions
    authorizer.add_user('Jayvic', '******', './ftp_home', perm='elradfmwM')
    # Define a read-only anonymous user
    authorizer.add_anonymous('./ftp_home')

    # Instantiate FTP handler class
    handler = FTPHandler
    handler.authorizer = authorizer

    # Define a customized banner (string returned when client connects)
    handler.banner = "Welcome to Jayvic's FTP."

    # Instantiate FTP server class and listen on 127.0.0.1:21
    address = ('127.0.0.1', 21)
    server = FTPServer(address, handler)

    # set a limit for connections
    server.max_cons = 256
    server.max_cons_per_ip = 5

    # start ftp server
    server.serve_forever()


if __name__ == '__main__':
    main()

由于此处我们使用的是21号端口,所以需要使用sudo来运行,否则会报如下错误:

Traceback (most recent call last):
  File "./ftp_server.py", line 37, in <module>
    main()
  File "./ftp_server.py", line 26, in main
    server = FTPServer(address, handler)
  File "/Library/Python/2.7/site-packages/pyftpdlib/servers.py", line 145, in __init__
    self._af = self.bind_af_unspecified(address_or_socket)
  File "/Library/Python/2.7/site-packages/pyftpdlib/ioloop.py", line 774, in bind_af_unspecified
    raise socket.error(err)
socket.error: [Errno 13] Permission denied

如果不想使用sudo权限,可以选择换一个用户级别的端口。

确认FTP可以正常使用后,接下来,我们将代码换成支持SSL的版本:

#!/usr/bin/env python
#encoding:utf-8

from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import TLS_FTPHandler
from pyftpdlib.servers import FTPServer


def main():
    # Instantiate a dummy authorizer for managing 'virtual' users
    authorizer = DummyAuthorizer()
    # Define a new user having full r/w permissions
    authorizer.add_user('Jayvic', '******', './ftp_home', perm='elradfmwM')
    # Define a read-only anonymous user
    authorizer.add_anonymous('./ftp_home')

    # Instantiate TLS FTP handler class
    handler = TLS_FTPHandler
    handler.authorizer = authorizer
    handler.certfile = './server.crt'
    handler.keyfile = './server.key'

    # Define a customized banner (string returned when client connects)
    handler.banner = "Welcome to Jayvic's FTPS."

    # Instantiate FTP server class and listen on 127.0.0.1:21
    address = ('127.0.0.1', 21)
    server = FTPServer(address, handler)

    # set a limit for connections
    server.max_cons = 256
    server.max_cons_per_ip = 5

    # start ftp server
    server.serve_forever()


if __name__ == '__main__':
    main()

证书的制作可见此文,如果使用最后的方法生成的证书,可以不指定keyfile。

做这题的时候,脑残了一下,还把FTPS和SFTP给弄混了,竟然在架设好了FTPS的服务器后,使用SFTP命令来连接,真是脑残的不轻,想之前只有在windows下SSH的时候想传文件还是用的FTP软件开SFTP传的,这几天不用竟然就忘了。

今天倒腾了下OpenSSL自制证书,这里做个记录。

我是使用的Mac OS,系统自带OpenSSL,OpenSSL使用的默认配置/System/Library/OpenSSL/openssl.cnf。

首先由于是默认配置,先按照默认配置的要求建立目录结构:

mkdir -p ./demoCA/{private,newcerts}
touch ./demoCA/index.txt
echo 01 > ./demoCA/serial

其中serail存放下一个证书的序列号,index.txt存放证书信息数据库。

然后生成CA证书的RSA密钥对:

openssl genrsa -des3 -out ./demoCA/private/cakey.pem 2048

参数解释:

genrsa:用于生成 RSA 密钥对的 OpenSSL 命令。

-des3:使用3-DES对称加密算法加密密钥对,该参数需要用户在密钥生成过程中输入一个口令用于加密。今后使用该密钥对时,需要输入相应的口令。如果不加该选项,则不对密钥进行加密。

-out ./demoCA/private/cakey.pem:令生成的密钥对保存到文件./demoCA/private/cakey.pem。

2048:RSA模数位数,在一定程度上表征了密钥强度。

接着生成CA证书请求:

为了获取一个 CA 根证书,我们需要先制作一份证书请求。先前生成的CA密钥对被用于对证书请求签名。

openssl req -new -days 365 -key ./demoCA/private/cakey.pem -out careq.pem

参数解释:

req:用于生成证书请求的OpenSSL命令。

-new:生成一个新的证书请求。该参数将令OpenSSL在证书请求生成过程中要求用户填写一些相应的字段。

-days 365:从生成之时算起,证书时效为365天。

-key ./demoCA/private/cakey.pem:指定./demoCA/private/cakey.pem为证书所使用的密钥对文件。

-out careq.pem:令生成的证书请求保存到文件 careq.pem。

最后通过对CA证书请求进行签名得到CA的证书:

在实际应用中,用户可以通过向知名CA递交证书请求来申请证书。但是在这里,我们需要建立的是一个根CA ,只能由我们自己来对证书请求进行签名。

openssl ca -selfsign -in careq.pem -out ./demoCA/cacert.pem

ca:用于执行CA相关操作的OpenSSL命令。

-selfsign:使用对证书请求进行签名的密钥对来签发证书,即自签名。

-in careq.pem:指定careq.pem为证书请求文件。

-out ./demoCA/cacert.pem:指定./demoCA/cacert.pem为输出的证书。

至此,我们已经生成了一个根证书./demoCA/cacert.pem以及其对应的RSA密钥对的私钥./demoCA/private/cakey.pem,接下来无论是FTPS、HTTPS还是其它什么需要使用证书的地方,都是可以直接使用这个证书的。

除了直接使用根证书,我们也可以选择用根证书签发新的证书来使用。

首先是生成新证书的RSA密钥对:

openssl genrsa -out newkey.pem 2048

此处因为我们架设HTTPS服务器需要使用其私钥,所以这里没有使用3-DES加密。

然后是生成新证书请求:

openssl req -new -days 365 -key newkey.pem -out newreq.pem

新证书请求使用先前生成的新密钥进行签名。

最后使用CA对新证书请求进行签名得到新证书:

openssl ca -in newreq.pem -out newcert.pem

注意此处由于签名需要使用的CA的私钥,而CA的私钥我们使用了3-DES加密,所以我们需要输入CA的私钥来完成此操作。

自此,我们就用我们自制的根证书签发了一个新的证书,通过这样的方式,我们还可以继续用根证书签发的二级证书再签发三级证书,完成证书的管理。

最后在写几个可能需要是用的命令:

查看证书:

openssl x509 -noout -text -in cert.pem

验证证书:

openssl verify -CAfile ./demoCA/cacert.pem hostcert.pem

对加密的私钥进行解密(Apache需要解密后的私钥):

openssl rsa -in encrypted_key.pem -out decrypted_key.pem

一步生成同时包含私钥和证书的文件:

openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout cert.pem

这个文件中由于已经包含了私钥,所以在使用的时候,可以只指定证书文件为此文件,而不用再同时指定私钥文件。

刚倒腾了一下HTTPS,使用的是Apache服务器,由于不想开虚拟机,所以是直接在OS X中进行的设置。

要想开启HTTPS,第一步当然还是准备好证书,关于自制证书可以参见这里

证书准备好后,就要着手开始配置Apache了。

首先是开启Apache的ssl模块并启用ssl和vhosts的配置文件(去掉/etc/apache2/httpd.conf中下面三行前面的#):

LoadModule ssl_module libexec/apache2/mod_ssl.so
Include /private/etc/apache2/extra/httpd-ssl.conf
Include /private/etc/apache2/extra/httpd-vhosts.conf

接下来是配置SSL的证书位置,编辑/etc/apache2/extra/httpd-ssl.conf,修改下面两行,分别指向证书文件和私钥文件:

SSLCertificateFile "/private/etc/apache2/server.crt"
SSLCertificateKeyFile "/private/etc/apache2/server.key"

如果说是证书和私钥在同一个文件中的,可以不用设置上面的第二行,注释掉即可。

最后是配置虚拟主机,修改/etc/apache2/extra/httpd-vhosts.conf,在文件的最后加入如下几行(其它几个默认的vhosts如果不用可以注释掉):

<VirtualHost *:443>
    SSLEngine on
    SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
    SSLCertificateFile /private/etc/apache2/certs/server.crt
    SSLCertificateKeyFile /private/etc/apache2/certs/server.key
    ServerName localhost
    DocumentRoot "/Users/Jayvic/Sites"
</VirtualHost>

其中SSLCertificateFile和SSLCertificateKeyFile也是同上一样指向证书和私钥。

至此,配置已经完成,可以使用下面的命令来检查Apache的配置:

apachectl configtest

如果发现有没有开启的模块就开启一下即可,确认配置无误后,使用下面的命令重启Apache即可:

sudo apachectl restart

PS:在配置的时候感觉/private/etc与/etc有点混乱了,就查了下,发现Mac OS X下是用/private/etc,而为了兼容Unix,所以/etc通过链接指向/private/etc,所以基本可以认为使用两个是一样的。


今天偶然看到一篇nginx https的文章,感觉不错,mark一下。

由于作业需要做个给图像嵌入信息的程序,需要全自动,所以便开始倒腾PIL了(之前都是手动转BMP然后按照格式把其中的像素点信息取出来修改的)。

首先是安装,我是用的Mac OS,直接使用pip安装:

sudo pip install PIL --allow-external PIL --allow-unverified PIL

如果不加–allow-external PIL –allow-unverified PIL,会出现下面两个报错:

Some externally hosted files were ignored (use --allow-external PIL to allow).

Some insecure and unverifiable files were ignored (use --allow-unverified PIL to allow).

安装好后就是使用了~~~

从文件中读取图像:

import Image
im = Image.open("test.jpg")

fp = open("test.jpg", "rb")
im = Image.open(fp)

import StringIO
im = Image.open(StringIO.StringIO(buffer))

import TarIO
fp = TarIO.TarIO("Imaging.tar", "Imaging/test/test.jpg")
im = Image.open(fp)

查看图片格式等信息:

print im.format, im.size, im.mode

其中format为图片原格式,size为图片大小,mode为图片模式(L为灰度图,RGB为像素图……)。

查看图片:

im.show()

保存图片:

im.save('out.jpg')
im.save('out.thumbnail', 'JPEG')

裁剪图片:

box = (100, 100, 400, 400)
region = im.crop(box)

处理图片的一部分:

region = region.transpose(Image.ROTATE_180)
im.paste(region, box)

将图片分解与合成:

r, g, b = im.split()
im = Image.merge("RGB", (b, g, r))

需要注意的是要将图片先转换为RGB模式才能分解。

改变图片大小:

out = im.resize((128, 128))

图片旋转:

out = im.rotate(45)
out = im.transpose(Image.ROTATE_90)
out = im.transpose(Image.ROTATE_180)
out = im.transpose(Image.ROTATE_270)

图片翻转:

out = im.transpose(Image.FLIP_LEFT_RIGHT)
out = im.transpose(Image.FLIP_TOP_BOTTOM)

图片模式转换:

out = im.convert("L")

接下来是两个我最需要用到的函数:

获取字符串形式的图片中的像素信息:

im.tostring()

使用字符串形式的图片中的像素信息生成一张图片:

out = Image.fromstring('RGBA', im.size, im.convert('RGBA').tostring()

再附个PIL的教程