对数字证书的理解

最简单的情况下,服务器掌握私钥,任何人都可以使用公钥。因此只要用公钥解密就能确认数据是否来源于服务器。

但还有子证书的概念。用户操作系统里存放的根证书是有限的,因此https网站的证书是一级一级签发的。

根证书管理机构(CA)用自己的私钥签发下一级证书的公钥,而下一级证书的私钥用来加密又下一级的公钥。假定一共就3级,那么用户浏览网站时,首先收到第1级私钥加密的第2级公钥、第2级私钥加密的第三级公钥,然后用本地的第1级证书解密第2级公钥,用第2级证书解密第三级公钥,如此即可信任3级公钥。最后再用第3级公钥验证网站用3级私钥加密的内容。

基于LFS做一个Linux系统

一、搭建Linux环境

曾经有LFSliveCD用于编译LFS,但前者不维护了,可以使用其他Linux发行版。

安装debian,发现使用中文安装会乱码【问题1】,并且未设置root密码,使用su root切换却提示密码错误【问题2】,遂重装英文版本。

debian的安装过程有点长,即使下载了xfced版本,选择不从网络下载软件包,也得等数分钟。

http://www.linuxfromscratch.org/lfs/view/stable/chapter02/hostreqs.html

原本想安装VirtualBox提供的工具,方便从主机剪贴板获取命令,没想到即使以root运行挂载盘下的runasroot.sh,也提示Permission Denied【问题3】。

和午后的困倦拼了一会儿,结果我把挂载的内容复制了,就可以运行。其间我第一次复制得到的文件夹是空的【问题4】,不知道啥原因,由于弹出后挂载镜像又需要手动挂载,我就干脆重启。

虽然没干啥事,但已经过去了几个小时。。工欲善其事,必先利其器嘛。

然后运行官方提供的脚本检查所需软件是否已安装,发现报错一大堆。比如g++就没有安装,所以我apt intall g++,发现报错Unable to correct problems, you have held broken packages.【问题5】

网上说update+upgrade升级源和软件就好了,然而我试了也没用,只好手动添加源再运行以上命令,发现仍然不行。

我再观察报错,发现它都提示有依赖没有安装,难道不是自动安装依赖吗??如下提示,但如果我安装libc6-dev,那么它又会提示我安装别的,如此循环下去。

 libstdc++-6-dev : Depends: libc6-dev (>= 2.13-5) but it is not going to be installed

最后问了dalao,原来是我添加的软件源版本错了,debian9和10的版本号代称我不了解,因此没有注意。我估计以上报错原因是,debian9的源没有高版本软件,因此检查依赖就过不了,但其实有的依赖根本就已经安装好了,只是源中找不到。

更新了源之后,软件可以正常安装,其中makeinfo命令not found,对应的软件包叫texinfo。

LFS官方提供了检查软件安装的脚本,它还提示我,/bin/sh没有指向/usr/bin/bash,我目前指向的是dash。dash是debian启用的新终端,但lfs手册既然说要指向bash,那就只好改了。

现在我要了解一下,软链接( ln -s source target )和硬链接( ln source target )是什么。以前学操作系统的时候提到过,硬链接的引用是在文件系统层面上,控制i-node块的指向实现的,而软链接相当于快捷方式,本身是另一个文件。那么这里就用软链接吧,如果是快捷方式,被误删也没关系。

接近12点,检查过所有软件包都安装后进入下一步。

http://www.linuxfromscratch.org/lfs/view/stable/chapter02/stages.html

这个页面告诉我,root用户需要设置LFS环境变量。从第5章开始,/mnt/lfs必须挂载,必须创建lfs用户,5.3是关键,如果有问题重新解压包。第6章开始,使用chroot要设置LFS环境变量,必须挂载虚拟文件系统(要在chroot命令外)。

我在百度云买了一台云主机,下载软件比较快,而且可以用不同的设备操作,因此实验环境在百度云上了。

现在的问题是分区,实验手册中制作lfs系统使用了lfslivecd,宿主系统并没有安装到磁盘上,但我现在是先装好debian,所以要从系统盘给lfs让出一个分区。

发现resize2fs可以调整分区容量,它提示我On-line shrinking not supported,也是哦,系统还在运行呢。【问题6】

遇到了非常恼人的问题,询问多人后发现无简单解决方案。Windows都可以在运行时压缩系统卷,Linux怎么就不行呢?

您好,十分抱歉,系统盘不支持分区,如果您想要进行分区的话,您可以选择购买cds磁盘挂载来充当您的数据盘,请您知悉,谢谢。

好吧,还是只能本地操作。

我下了Ubuntu19.10,打算提前分好区,免得对系统分区操作麻烦。至于百度的云服务器,就拿来给ipad当云电脑用,装个windows。

但是Ubuntu图形化安装界面总是在硬盘分区,新建分区表后卡死,背景变黑。【问题7】

实在无办法,只好先分区。下载了一个ElementaryOS,结果安装的时候没有选项。换openSUSE发行版试试。

openSUSE顺利分区,终于可以开始正式的工作了,此去已几天。

因为要复制脚本,所以用ssh连接,Parallel tools的linux版老是出错。结果能ping通,ssh却连接不上,初步判断防火墙。

幸好opensuse配置防火墙有图形化界面,配置还方便一点。

但是opensuse软件源又得配置,算了还是ubuntu省心,这次给ubuntu分配了更多的内存和显存,就没有卡死了。

ubuntu安装ssh server后继续操作,分区在安装时就设置好,进入系统后用'mount 设备 文件夹'命令挂载分区(挂载前先格式化成ext4)。

之后,我下载了所有需要的软件源码放在了$LFS/sources中。

二、制作工具链

为了避免错误操作造成的影响,这一步新建了lfs账户。

按部就班地解压文件,编译源码,这一步虽然要重复很多次而且很耗时,但只要跟着LFS手册就不会有问题。

但临时系统需要的软件编译完成后,就得准备编译内核和系统软件了,这个时候需要从lfs用户切换回root用户。切换操作只能exit,su会提示认证失败。【问题8】

三、构建内核

这一步开始需要chroot(改变根目录),进行最终的编译。

这一步只要根据LFS来就好了,但是却花了我两周的空闲时间。

温馨提示工具链+为lfs系统编译的软件总数100个左右,而且没有自动化脚本。(有的话lfs就没有diy的感觉了)

四、启动配置

System V是一个经典的启动程序,它包含init,Linux的启动level就是来自这里。

0 — halt
1 — Single user mode
2 — Multiuser, without networking
3 — Full multiuser mode
4 — User definable
5 — Full multiuser mode with display manager
6 — reboot

这里需要安装lfs-bootscript。

由于想改一下Ubuntu的启动配置,又新开了一个ssh窗口,没想到用户名密码都正确却提示Permission denied。【问题9】

我擦,原来是ssh的配置文件里有一句permitRootLogin prohibit-password,也就是只能用密钥。。赶紧注释掉。

出现了error : unknown filesystem错误。

我现在寄希望于boot-repair,在半个月后,问题仍然存在。我从liveCD启动后,添加ppa软件源和安装boot-repair出现了问题,提示依赖关系有问题,不能安装该软件。

于是在StackExchange上找到了答案,遗憾的是,我找不到Synaptic。

原来是liveCD默认不包含某些工具。https://askubuntu.com/questions/874854/package-synaptic-is-not-available-in-ubuntu-16-04

勾选了软件源列表后成功安装boot-repair,但是运行又出了问题。

五、GUI应用

六、窗口管理系统

待更新

DDOS

了解一下DDOS。

定义

一切使用合法方式使服务器不能提供服务不能正常提供服务的攻击,DOS即Denial of Service。一般DDOS的手段是通过大量请求耗尽服务器资源或宽带。

常见类型

软件缺陷型

winuke

不了解。

land

伪造源IP源端口与目的IP端口相同的包,使得一些主机陷入循环。

teardrop

IP分组报文重组时不会检查长度过短的情况。因此可以构造两段报文,其中第二段报文偏移量小于第一段报文的长度,这种情况下,第二段报文的偏移+长度减去第一段报文的长度小于0。而长度作为无符号数,小于0就会溢出,变成很大的数,从而导致系统分配很多资源。

协议缺陷型

Synflooding

最常听说的DDOS攻击,利用三次握手完成前服务器要保持状态,不停的发送SYN,不回ACK,导致服务器大量资源空耗。

Smurf

局域网中,攻击者伪造源ip,向广播地址发送包,这样被伪造的主机会收到所有主机的回应,回应过于密集时主机可能崩溃。

计算机安全(2) 分组密码的工作模式

电码本ECB(Electronic codebook mode)

消息独立加解密,每组消息64位。简单有效可并行,误差不传递,但信息块可被替换、重排、删除、重放,且相同明文总是对应相同密文。

密文分组链接CBC(Cipher block chaining)

加密时,上一组的密文和这一组的明文都作为输入,因此相同明文可以对应不同密文,也不易被替换、重排、删除、重放,但有误差传递,一个块的错误影响两个块。CBC加密无已知的并行算法,解密可以。

密文反馈CFB(Cipher feedback)

分组加密的输出与明文进行异或(作为密文),异或的结果又作为下一块的加密输入。第一块消息的加密输入是IV(初始向量)分组加密后输出的最左边s位(明文和密文都是s位时)。

输出反馈OFB(Output feedback)

与CFB的区别在于,CFB将异或的结果作为下一块加密输入,OFB将加密后异或前的值作为下一块加密的输入(Cn的输入与Pn-1无关),因此无误差传递。

计数器CTR(Counter)分组/流

只加密计数器值,不反馈任何值。每一个明文需要使用不同计数器。可并行可预处理。

S-DES

生成密钥

//S-DES 第一部分 生成密钥
let secretKey = "0111111101";
const p10=[2, 4, 1, 6, 3, 9, 0, 8, 7, 5];
const p8 = [5, 2, 6, 3, 7, 4, 9, 8];

const substituteMaker = (translate) => {
    //生成置换函数
    return (key) => {
        let res = [];
        for (let i = 0; i < translate.length; i++) {
            res.push(key[translate[i]]);
        }
        return res.join('');
    }


}

secretKey = substituteMaker(p10)(secretKey);

const l1 = (key,count=1) => {
    let keyArr = key.split('');
    let l = keyArr.slice(0, 5);//不包括end
    let r = keyArr.slice(5, 10);
    for(let i=0;i<count;i++){
        l.push(l.shift());
        r.push(r.shift());
    }
    return l.concat(r).join('');
}

secretKey = l1(secretKey)



const key1 = substituteMaker(p8)(secretKey);
console.log('key1',key1);

secretKey = l1(secretKey,2)

const key2 = substituteMaker(p8)(secretKey);
console.log('key2',key2);

//key1 01011111
//key2 11111100



//S-DES 第二部分 加密
const cipherText="10100010";

加密演示

http://demo.guohere.com/sdes/

最近看了无人生还

瓦格雷夫法官为什么可以装作尸体(不会腐烂 吗)……以及他心理素质这么强大……构思巧妙又一丝不苟……觉得都不真实了。不过看前面的时候,也差点以为小说是没有解释的,像狙击电话亭以及一众悬疑电影都没有解释的(听说希区柯克很知名,他的电影也没有解释吧),有解释就很厉害。

C++读取文件内容

很多题都会要求读取txt作为输入。

头文件

fstream

打开文件

ifstream inputData("/cpp/input.txt");
    if (!inputData.is_open())
    {
        cout << "open failed" << endl;
    }

...
inputData.close();

读取一行的内容

string temp;
getline(inputData, temp);

分隔一行的内容(split)

char a[65];
strcpy(a, temp.c_str());//temp是string类型,需要转换成const char*
char *w = strtok(a, " ");//第一个参数char*,第二个参数是分隔符
char *h = strtok(NULL, " ");//第一次以后,第一个参数传NULL即可获取下一段字符串

//如果内容是数字,atoi(w)与atoi(h)可将char*转换成int

用js写了一个批量发短信的脚本

可能的问题

Axios的POST请求主体默认是json,但api可能只接受x-www-form-urlencoded编码

一方面,header要添加content-type信息,另一方面,需要引入qs库,qs。stringify与json.stringify类似,但转换结果是queryString。

代码

let Axios = require('axios')
let Qs = require('qs')

function sendSMS(name, time, location, time2, phone) {
    let data = {
        "apikey": "密钥",
        "mobile": phone,
        "text": `短信模板`
    };
    let headers = {
        "Content-Type": "application/x-www-form-urlencoded"
    };
    Axios.post('https://sms.yunpian.com/v2/sms/batch_send.json',
        Qs.stringify(data), { headers }
    ).then(
        res => {
            console.log(res.data)
        }
    ).catch(
        err => {
            console.error(err)
        })
}



let fs= require('fs')
fs.readFile('/Users/guo/Documents/x.csv',function(err,data){
    if(err){
        console.error(err);
    }else{
        data.toString().split('\n').forEach(function(val){
            let [name,phone]=val.split(",").map(val=>val.trim())
            sendSMS(name,'13月31日15:20', 'DXa101', '13月30日12:00', phone)
        });
       
    }
})

数据格式

csv 可以从Excel导出,每一行格式 XX,XXX

寻路(待完成)

用C++实现寻路的几种方法。

地图

思前想后我决定用链表来存储地图,也就是用vector<int>按顺序存储地图的节点,由于地图一般是矩形的,知道高度与宽度后我们无需再存储位置信息,每个节点的内容可以是地形高度。用数组也是可以的,但栈中的数组需要确定大小,动态数组很好,但为了方便删除元素还是用效率低的vector容器吧。

points存储每个节点的高度,target存储目标节点的序号,landing存储登陆点的序号,width与length用于根据序号推算节点位置,height是寻路对象能够跨越的最大高度,track记录路径,find标记是否已到达所有终点。除了track与find,其余成员变量都要初始化。

class Graph{
 private:
  vector<int> points;
  vector<int> target;
  int landing;
  int width,length;
  int height;
  vector<int> track;
  bool find=false;
}

DFS

先写一个启动DFS的函数,用于未找到路径时输出结果。

    void startDFS()
    {
        DFS(this->landing);
        if (this->find == false)
        {
            cout << "未找到路径!" << endl;
        }

    }

DFS主函数检查周围的点是否已访问或符合规则,然后访问。访问后节点height被标记为-1,checkEnd用于判断是否已到达全部终点并修改find的值,checkNeighbor用于把将要访问的点放入neighbor。

    bool DFS(int start)
    {
        vector<int> neighbor;
        //如果已经找到那么结束
        if (this->find == true)
        {
            return false;
        }

        //如果已经访问过那么返回
        if (points[start]==-1)
        {
            return false;
        }
        else
        {
            points[start]=-1;
            track.push_back(start);
        }

        checkEnd(start);

        checkNeighbor(start, &neighbor);

        for (int i = 0; i < neighbor.size(); i++)
        {
            DFS(neighbor[i]);
        }
        track.pop_back(); //返回上级时弹出
    }

checkEnd函数:

    void checkEnd(int start)
    {
        for (int k = 0; k < target.size(); k++)
        {
            if (start == target[k])
            {
                cout << "经过了" << start << endl;
                target[k] = -1; //标记已到达
                bool visitedAll = true;
                for (int i = 0; i < target.size(); i++)
                {
                    if (target[i] != -1)
                    {
                        visitedAll = false;
                    }
                }
                if (visitedAll == true)
                {
                    cout << "已全部到达!" << endl;
                    this->find = true;
                    for (int i = 0; i < track.size(); i++)
                    {
                        cout <<"("<< track[i]/width <<","<<track[i]%width<<")"<< endl;
                    }
                }
            }
        }
    }

checkNeighbor函数,通过有关计算确定一个位置的上/下/左/右等位置是否存在节点。

    void checkNeighbor(int start, vector<int> *neighbor)
    {
        int leftTop = -1, top = -1, rightTop = -1, left = -1, right = -1, bottom = -1, leftBottom = -1, rightBottom = -1;
        bool hasLeft = false, hasRight = false, hasTop = false, hasBottom = false;

        if (start % width != 0)
        {
            hasLeft = true;
        }
        if ((start + 1) % width != 0)
        {
            hasRight = true;
        }
        if (start - width >= 0)
        {
            hasTop = true;
        }
        if (start + width < width * height)
        {
            hasBottom = true;
        }
        //8个方向的数组序号
        if (hasTop && hasLeft)
        {
            leftTop = start - width - 1;

            if (abs(points[start] - points[leftTop]) >= maxZ)
            {
                leftTop = -1;
            }
        }
        if (hasTop)
        {
            top = start - width;
            if (abs(points[start] - points[top]) >= maxZ)
            {
                top = -1;
            }
        }
        if (hasRight && hasTop)
        {
            rightTop = start - width + 1;
            if (abs(points[start] - points[rightTop]) >= maxZ)
            {
                rightTop = -1;
            }
        }
        if (hasLeft)
        {
            left = start - 1;
            if (abs(points[start] - points[left]) >= maxZ)
            {
                left = -1;
            }
        }
        if (hasRight)
        {
            right = start + 1;
            if (abs(points[start] - points[right]) >= maxZ)
            {
                right = -1;
            }
        }
        if (hasLeft && hasBottom)
        {
            leftBottom = start + width - 1;
            if (abs(points[start] - points[leftBottom]) >= maxZ)
            {
                leftBottom = -1;
            }
        }
        if (hasRight && hasBottom)
        {
            rightBottom = start + width + 1;
            if (abs(points[start] - points[rightBottom]) >= maxZ)
            {
                rightBottom = -1;
            }
        }
        if (hasBottom)
        {
            bottom = start + width;

            if (abs(points[start] - points[bottom]) >= maxZ)
            {
                
                bottom = -1;
            }
        }
        //添加到neighbor
        if (leftTop != -1)
        {
            //neighbor.push_back(&points[leftTop]);
            neighbor->push_back(leftTop);
        }
        if (top != -1)
        {
            //neighbor.push_back(&points[top]);
            neighbor->push_back(top);
        }
        if (rightTop != -1)
        {
           
            neighbor->push_back(rightTop);
        }
        if (left != -1)
        {
            
            neighbor->push_back(left);
        }
        if (right != -1)
        {
           
            neighbor->push_back(right);
        }
        if (leftBottom != -1)
        {
           
            neighbor->push_back(leftBottom);
        }
        if (bottom != -1)
        {
   
            neighbor->push_back(bottom);
        }
        if (rightBottom != -1)
        {
      
            neighbor->push_back(rightBottom);
        }
    }

BFS

虽然我把BFS遍历写出来了,但BFS不方便记录轨迹,因为访问元素是依次加入队列的,之间没有父子关系。如果要记录路径的话,需要知道路径上每个节点的父节点,也就是要形成一颗BFS树:每个节点都记录自己的父节点。

那么BFS记录轨迹的方法就是,将邻居节点加入队列时,节点信息要包括本节点,收集从队列中弹出的节点(收集访问过的节点,可以组织成一棵树),当找到终点时,反向寻找出发点。

A*