区块链

我了解区块链还是2017年底那一波BTC大涨带来的广泛性影响,彼时刚刚工作不久,手里没什么钱,那时候简直路边的野狗都在讨论区块链,数字黄金等等。

区块链实际就是一个点对点加密的分布式数据库(也就是所谓的去中心化,和迅雷,快播的p2p文件传输类似),完全不依赖第三方或者政府机构的监管,是一个可以自行运行的系统,由中本聪(真名不详)在2008年底提出并发布第一版区块链协议代码,并且挖出了第一个创世区块。

区块链解决的是信任共识问题,避免权威化的中心机构因为一些不可抗力或者人为的因素,而作出损害系统的行为,区块链不仅仅可以运用于金融机构,在其他许多场景下都有不同的涉猎,比如物流跟踪(淘宝),捐赠记录(筹款/捐款明细),历史变更记录(百度百科)等方面。

区块链通常不是免费的,例如比特币,交易记录需要全部节点来维持,节点需要付出电力,带宽,机房,硬件,人员维护等成本,如果没有激励机制,则会产生因为节点过少(用爱发电只是口头说说),出现无法维持整个系统运行的情况。可交易的区块就是奖励,一次转账记录打包并不是所有节点都可以,某次通常只有一个节点可以成功,并获得奖励,如何成功取决于一个计算机算数题,第一个解出答案并被其他节点承认,则奖励就会归属于该计算机绑定的钱包地址,这个题难解但是容易验证,解题难度也会随全网算力自动调整,很明显,自己算力/全网总算力*100%,就是挖出一个比特币的概率

下一篇,说说币圈这个群魔乱舞的世界。

白嫖免费的通配符ssl域名证书

let’encrypt 免费的ssl通配符证书提供服务商,致力于推广网络安全。

众所周知,没有ssl证书的网站,所有数据传输在网上都是明文的,相当于裸奔,容易被一些恶意的程序劫持,比如连接了不安全的wifi,从而造成个人密码,信息的泄漏。

如果从事后端开发,或者运维服务的同学都知道,一般云服务商都提供一些代理ssl证书购买服务,实际的证书提供商如赛门铁克,dc等等,证书从单域名到通配符域名,价格不等,通常单域名证书稍微便宜些,大约一年几百上千的样子,通配符证书一年要上万左右,而且通常需要技术人员手动一年一换,并不方便。同时,也有一些会提免费一年的单域名证书,但是出了问题的话并不理赔(与付费证书的区别)

首先说明单域名证书与通配符证书的区别:

单域名证书,顾名思义,只适用于单个二级域名,假设通常我们购买域名为:abc.com(姑且称其为一级域名,但并不准确,此处不讨论),然后通过域名解析,将blog.abc.com解析到了服务器地址,则blog.abc.com就是一个单域名,同理shop.abc.com也是一个单域名,都是二级域名,而所有的二级域名合在一起,就用*(通配符)来代替,blog和shop都属于*,所以单域名证书只对申请时指定的那个域名有效,而通配符证书,则对所有的二级域名生效。

举个例子:现在我申请了一个单域名证书blog.abc.com,那么,这个证书只能配置给blog.abc.com用,而如果给shop.abc.com使用,则浏览器会提示不安全,证书无效。但是,如果我申请了*.abc.com这个通配符证书,则都可以给blog.abc.com,shop.abc.com使用,一个搞定所有(能不能给123.blog.abc.com使用?似乎不行,这已经是三级域名了,域名分级以.为分隔,*只能管到下一级,不能管下下级,如果想管,同理申请*.blog.abc.com,反正阿里云的证书是这样)

安装 git clone https://github.com/letsencrypt/letsencrypt

下载完后,最好将此文件夹移到类似/usr/local/bin之类的系统文件夹,并给与高级权限,否则下面的命令可能无法执行成功

./certbot-auto certonly  -d *.你的域名 --manual --preferred-challenges dns --server https://acme-v02.api.letsencrypt.org/directory

如果上面报错

OSError: Command /opt/eff.org/certbot/venv/bin/python2.7 - setuptools pip wheel failed with error code 1

解决问题

卸载virtualenv: pip  uninstall  virtualenv

再安装virtualenv : pip  install  virtualenv==15.1.0

输出

ackage gcc-4.8.5-36.el7_6.2.x86_64 already installed and latest version
Package augeas-libs-1.4.0-6.el7_6.1.x86_64 already installed and latest version
Package 1:openssl-1.0.2k-16.el7_6.1.x86_64 already installed and latest version
Package 1:openssl-devel-1.0.2k-16.el7_6.1.x86_64 already installed and latest version
Package libffi-devel-3.0.13-18.el7.x86_64 already installed and latest version
Package redhat-rpm-config-9.1.0-87.el7.centos.noarch already installed and latest version
Package ca-certificates-2018.2.22-70.0.el7_5.noarch already installed and latest version
Package python-devel-2.7.5-77.el7_6.x86_64 already installed and latest version
Package python-virtualenv-15.1.0-2.el7.noarch already installed and latest version
Package python-tools-2.7.5-77.el7_6.x86_64 already installed and latest version
Package python2-pip-8.1.2-8.el7.noarch already installed and latest version
Nothing to do
Creating virtual environment...
Installing Python packages...
Installation succeeded.
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): 531833XXX@qq.com

Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory

是否通用协议,选择是,那就是’A’

A

Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.

是否分享你的邮箱,否

N

NOTE: The IP of this machine will be publicly logged as having requested this
certificate. If you're running certbot in manual mode on a machine that is not
your server, please ensure you're okay with that.

询问是否对域名和机器(IP)进行绑定=>需要同意

Y

证书续签

注:证书在到期前30天才会续签成功,但为了确保证书在运行过程中不过期,官方建议每天自动执行续签两次;
使用crontab自动续期
···
crontab -e // 编辑定时任务
0 */12 * * * certbot renew –quiet –renew-hook “/etc/init.d/nginx reload”

(说明:–renew-hook是指,如果更新成功的回调,则重启nginx服务,因为替换证书需要重启服务后才生效,所以,如果使用了像apache,tomcat之类的服务,就改成对应的服务重启命令)

证书保存的路径[配置nginx需要用到的]

/etc/letsencrypt/live/you.cn/fullchain.pem
/etc/letsencrypt/live/you.cn/privkey.pem

取消证书

可以使用一下命令取消刚刚生成的密匙,也就是以上的反操作:

certbot revoke --cert-path /etc/letsencrypt/live/you.cn/cert.pem
certbot delete --cert-name you.cn

如果自动续签失败,可能是无法验证域名所有者的问题,需要添加dns验证记录,但是手动的话很麻烦,以下提供云服务商自动添加的机制

https://github.com/ywdblog/certbot-letencrypt-wildcardcertificates-alydns-au

具体看readme

过了一段时间,证书又没更新= =,然后手动执行,发现爆如下错误:

Couldn’t download https://raw.githubusercontent.com/certbot/certbot/v1.11.0/letsencrypt-auto-source/letsencrypt-auto. <urlopen error [Errno 104] Connection reset by peer>

好吧gfw发威了

修改/etc/hosts

添加 199.232.4.133 raw.githubusercontent.com 即可

2021-1-7 发现,证书虽然是有效的,但是浏览器仍然提示不安全,即使重启nginx,依然不行,包括手机端访问也是如此,淦!

然后提示

Your system is not supported by certbot-auto anymore.

Certbot will no longer receive updates.

Please visit https://certbot.eff.org/ to check for other alternatives.

Saving debug log to /var/log/letsencrypt/letsencrypt.log

好吧,下载的certbot-auto已经不再支持我的系统(Ubuntu18.04 lts),让我访问https://certbot.eff.org/这个网站解决问题

第一步选择自己的webserver和系统,我选的nginx和Ubuntu18.04 lts

第二步,往下翻,按步骤的命令一步一步往下安装

即可

记一次java和php使用aes加密/解密打通

对接了个第三方公司的服务,需要集成到公司的产品页,由于对方的技术栈是java,并且对接接口需要将数据加密传输,对方发来了java版的加解密方法,如下:

package kd.common.codec;

import java.security.MessageDigest;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

/**
 *  AES 128 加密,与Nodejs 保持一致
 * @author daiyang
 *
 */
public class AesUtil {
	
	private static final String secret = "pater";
	
	public static final String DEFAULT_CODING = "utf-8";

	/**
	 * 解密
	 * @param encrypted
	 * @return
	 */
	public static String decrypt(String encrypted){
		return decrypt(encrypted, secret);
	}
	/**
	 * 解密
	 * @param encrypted
	 * @param secret
	 * @return
	 * @throws Exception
	 */
	public static String decrypt(String encrypted, String secret){
		try{
			byte[] keyb = secret.getBytes(DEFAULT_CODING);
			MessageDigest md = MessageDigest.getInstance("MD5");
			byte[] thedigest = md.digest(keyb);
			SecretKeySpec skey = new SecretKeySpec(thedigest, "AES");
			Cipher dcipher = Cipher.getInstance("AES");
			dcipher.init(Cipher.DECRYPT_MODE, skey);

			byte[] clearbyte = dcipher.doFinal(toByte(encrypted));
			return new String(clearbyte);
		}catch(Exception e){
			e.printStackTrace();
			return "";
		}
		
	}

	/**
	 * 加密
	 * @param content
	 * @return
	 */
	public static String encrypt(String content)  {
		return encrypt(content, secret);
	}
	/**
	 * 加密
	 * @param content
	 * @param secret
	 * @return
	 * @throws Exception
	 */
	public static String encrypt(String content, String secret)  {
		try{
			byte[] input = content.getBytes(DEFAULT_CODING);

			MessageDigest md = MessageDigest.getInstance("MD5");
			byte[] thedigest = md.digest(secret.getBytes(DEFAULT_CODING));
			SecretKeySpec skc = new SecretKeySpec(thedigest, "AES");
			Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
			cipher.init(Cipher.ENCRYPT_MODE, skc);

			byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
			int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
			ctLength += cipher.doFinal(cipherText, ctLength);

			return parseByte2HexStr(cipherText);
		}catch(Exception e){
			e.printStackTrace();
			return "";
		}
	}

	/**
	 * 字符串转字节数组
	 * @param hexString
	 * @return
	 */
	private static byte[] toByte(String hexString) {
		int len = hexString.length() / 2;
		byte[] result = new byte[len];
		for (int i = 0; i < len; i++) {
			result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2), 16).byteValue();
		}
		return result;
	}

	/**
	 * 字节转16进制数组
	 * @param buf
	 * @return
	 */
	private static String parseByte2HexStr(byte buf[]) {
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < buf.length; i++) {
			String hex = Integer.toHexString(buf[i] & 0xFF);
			if (hex.length() == 1) {
				hex = '0' + hex;
			}
			sb.append(hex);
		}
		return sb.toString();
	}

}

然后我发现,这完全就是百度的一个加密方法- -,其中,有nodejs版的互通方法:

/**
 * aes加密
 * @param data
 * @param secretKey
 */
encryptUtils.aesEncrypt = function(data, secretKey) {
	var cipher = crypto.createCipher('aes-128-ecb',secretKey);
	return cipher.update(data,'utf8','hex') + cipher.final('hex');
}
 
/**
 * aes解密
 * @param data
 * @param secretKey
 * @returns {*}
 */
encryptUtils.aesDecrypt = function(data, secretKey) {
	var cipher = crypto.createDecipher('aes-128-ecb',secretKey);
	return cipher.update(data,'hex','utf8') + cipher.final('utf8');

下面,我提供php版的加/解密方法,如下

/**
* 加密
* @param $str-需要加密的字符串
* @param $key-密钥
* @return string
/
function getEncryptContent($str,$key)
{
    return bin2hex(openssl_encrypt($str,'aes-128-ecb', openssl_digest($key, 'md5', true), OPENSSL_RAW_DATA));
}

/**
* 获取解秘内容
* @param $decode_str-需要解密的字符串
* @param $key-密钥
* @return string
*/
function getDecryptContent($decode_str,$key)
{
    return openssl_decrypt(hex2bin($decode_str), 'aes-128-ecb', openssl_digest($key, 'md5', true), OPENSSL_RAW_DATA);
}

写在即将的而立之年

不知不觉,大学毕业都5年多了,也经历领职场摸爬滚打(社会的毒打),好似失去了青少年时的激情,勇气,自信,无畏的精神,变得越发的安静,不愿远行,不愿没心没肺的说话,需要直面所遇到的一切,无论是自己愿意还是不愿意。

记得曾经和一个工友聊过,那时他刚结婚,我问他结婚和没结婚,有什么区别,他是这样回答的:结婚之后,感觉时间都不是自己的了,白天既要赚钱,晚上回家又要处理家里的琐事,应付亲朋好友,唯一可能不被人打扰的时候,大约就是一个人呆在厕所吧。

年少的时的情感,是建立在几乎没有任何需要自己直面的现实的情况下,才可以自由且无忧的思考,追逐,当成年后,才明白,父母为自己抵挡了,承担了许多事情,以后,都需要自己去面对了。

想想这些年,自己到底干了些啥呢,普普通通的过着每一天,每个小时,每一秒,日复一日,年复一年,除了有了点积蓄,让自己心里不是特别慌张,稍有点底气,解决了自己一个人的衣食住行问题。其实,也没啥好自豪的。

去年10月初,捡到一只刚断奶的狸花猫,不知不觉,都快养一年了,在上海这个诺大的城市里,小小的心有了一丝回家的期盼,有个小生命在期待我回家(虽然它最近似乎十分嫌弃我- -)。每次我回家,打开门,看到它迎接我,朝我喵喵叫,我心里都会默默说一句:我打猎回来啦,别担心喔。

今年虽然疫情比较严重,行情不太好,但是似乎解决了不少事情,一个一年都没工作的室友,在疫情爆发前找到了工作,不用我接济。去医院拔了第一颗蛀坏的智齿

今天就写到这吧,开会去了

linux环境下的小游戏(非桌面版)

1)bsdgames

bsdgames是一个小游戏包,使用ubuntu系统下载,sudo apt-get install bsdgames

安装后,进入/usr/games

随便输入一个命令,如wargames

敲下回车键

==============================

computer:想要玩一场比赛吗?

我:是的

computer:一个奇怪的游戏,唯一能够胜利的方式就是:别玩!

over

==============================

是不是觉得被耍了呢?哈哈,但是不是所有游戏都是这么整蛊,自己可以试试别的,但是一般都没有说明,也有一些常见的游戏复刻版,比如俄罗斯方块,吃豆人,贪吃蛇等等,上班无聊摸鱼时可以试试

apache设置默认跳转域名

apache配置webserver。如果想让所有未解析二级域名,如abc.domain.com 统一跳转到某一个固定域名下。

<VirtualHost *>

        ServerAlias *.domain.com

        #DocumentRoot /data/webapp/empty/

        <Location />

            RewriteEngine On

            RewriteCond %{SERVER_PORT} 80

            RewriteRule ^(.*)$ https://www.domain.com/$1 [R,L]

        </Location>

</VirtualHost>

ubuntu 安装 shadowsocks

由于众所周知的原因,我们无法访问外网。so,当我们需要查阅一些资料(比如访问github,stackoverflow)时,就需要一些梯子

解决方法

一)使用第三方的工具

比如蓝灯什么的,啥也不需要操心,只要氪金就行了,缺点就是可能有流量限制,需要充值,偶尔有些不太靠谱的第三方服务随时可能不可用,甚至跑路。

二)做一个手艺人

去买个vps(一般不需要翻墙,最好选择支持支付宝和微信支付的),比如搬瓦工,vultr等

然后去控制台,选择安装ubuntu的linux发行版

ssh连接上以后

sudo apt-get install shadowsocks

vim /etc/shadowsocks/config.json

配置文件怎么弄网上一大堆

/etc/init.d/shadowsocks restart 启动服务

最后自己弄个什么客户端,比如andr,ios,macOs的客户端配置好,连接即可

最后。国外的vps实在是太容易被封了,也别买包年的,封了不划算的(别问我为啥这么说,痛的领悟你懂的),可以尝试买买阿里云的香港服务器,包月。

三)新一代FQ工具v2ray

首先得有一台vps,最好还有一个域名(无所谓备不备案,可以拯救已经被墙的vps)。

详细教程:https://iketao.cn/2020/02/06/1301/

ImageMagick 文字写入图片时,变问号的解决方法

使用ImageMagick做海报时,如果需要写入用户微信昵称,比如 ‘张三👏’,图片内的文字会变成 ‘张三?’,因为本质上,emoji不是文字,在常规入Pingfang字体中,是不存在对应关系的。查遍24史(github和stackoverflow),都说ImageMagick并不支持emoji表情,php自带的gd库就更不支持了。

说明

首先,emoji对应的unicode码在不同平台解析出的样式不同。所以即使mysql使用字符集UTF8mb4格式将emoji存储起来,拿出来后,发给前端(andr,ios,浏览器),同一个unicode的图并不一样。

思路

所以,想让‘张三👏’完美的在海报上写出来。需要转换思考的方式,将‘张三👏’拆分成‘张三’+‘👏’,前者当成文字,后者当成图片。所以,能找到对应unicode对应的图片素材。就能使用ImageMagick的写入文字和合并图片的方法分治处理了。

emoji素材

https://www.unicode.org/emoji/charts/full-emojilist.html

以上链接为unicode对应的emoji在各大平台的图像映射关系

分析一下页面源代码,发现,除了绿框内的。其他的都是base64的格式发出的图片数据流,如下:

所以,直接control + s 保存该页面,重命名为emoji.html,同时mkdir 建立emojis文件夹,编写处理图片的php代码,如下:

<?php

$str = file_get_contents('./emoji.html');
$regx = '/<td class=\"code\">.*?<a href=.*?>(.*?)<\/a>.*?<img.*? class=\"imga\" src=\"(.*?)\">.*?<\/td>/is';
preg_match_all($regx, $str, $matches, PREG_PATTERN_ORDER);
$codes = $matches[1];
$stream = $matches[2];
unset($matches);
function get_img_type($str){
    $regx = '/^(data:\s*image\/(\w+);base64,)/';
    preg_match($regx, $str, $result);
    return $result;
}
foreach($codes as $k => $code){
    $code = str_replace(' ', '', $code);
    $code = str_replace('U+', '', $code);
    $info = get_img_type($stream[$k]);
    $mime = $info[2];
    $prefix = $info[1];
    $streamInfo = base64_decode(str_replace($prefix, '', $stream[$k]));
    file_put_contents('./emojis/'.$code.'.'.$mime, $streamInfo);
    unset($stream[$k]);
}

运行后,拿出所有图片,如下,素材已经取出来了

那么,如何找到‘?’对应的unicode码呢

如下php函数

function utf8ToUnicode($utf8) 
{
    $i = 0;
    $l = strlen($utf8);
    $out = '';
    while ($i < $l) {
        if ((ord($utf8[$i]) & 0x80) === 0x00) {
            $n = ord($utf8[$i++]);
        } elseif ((ord($utf8[$i]) & 0xE0) === 0xC0) {
            $n =
                ((ord($utf8[$i++]) & 0x1F) <<  6) |
                ((ord($utf8[$i++]) & 0x3F) <<  0);
        } elseif ((ord($utf8[$i]) & 0xF0) === 0xE0) {
            $n =
                ((ord($utf8[$i++]) & 0x0F) << 12) |
                ((ord($utf8[$i++]) & 0x3F) <<  6) |
                ((ord($utf8[$i++]) & 0x3F) <<  0)
            ;
        } elseif ((ord($utf8[$i]) & 0xF8) === 0xF0) {
            $n =
                ((ord($utf8[$i++]) & 0x07) << 18) |
                ((ord($utf8[$i++]) & 0x3F) << 12) |
                ((ord($utf8[$i++]) & 0x3F) <<  6) |
                ((ord($utf8[$i++]) & 0x3F) <<  0)
            ;
        } elseif ((ord($utf8[$i]) & 0xFC) === 0xF8) {
            $n =
                ((ord($utf8[$i++]) & 0x03) << 24) |
                ((ord($utf8[$i++]) & 0x3F) << 18) |
                ((ord($utf8[$i++]) & 0x3F) << 12) |
                ((ord($utf8[$i++]) & 0x3F) <<  6) |
                ((ord($utf8[$i++]) & 0x3F) <<  0);
        } elseif ((ord($utf8[$i]) & 0xFE) === 0xFC) {
            $n =
                ((ord($utf8[$i++]) & 0x01) << 30) |
                ((ord($utf8[$i++]) & 0x3F) << 24) |
                ((ord($utf8[$i++]) & 0x3F) << 18) |
                ((ord($utf8[$i++]) & 0x3F) << 12) |
                ((ord($utf8[$i++]) & 0x3F) <<  6) |
                ((ord($utf8[$i++]) & 0x3F) <<  0);
        } else {
            throw new \Exception('Invalid utf-8 code point');
        }
        $n = strtoupper(dechex($n));
        $pad = strlen($n) <= 4 ? strlen($n) + strlen($n) %2 : 0;
        $n = str_pad($n, $pad, "0", STR_PAD_LEFT);

        $out .= $n;
        //$out .= sprintf("\u%s", $n);

    }
    return $out;
}

echo utf8ToUnicode('👏');   //输出1F44F

从emojis文件夹中可以找到1F44F.png,就是对应的素材。

最后,怎么用ImageMagick插入图片到另一张图片上,这就不在此多做赘述了。

效果图如下: