作者归档:wingyue

如何利用Wireshark解密SSL和TLS流量

英语版本:CTX116557 /   创建日期:2008年3月14日   /   更新日期:2012年11月29日

概要

本文介绍在Wireshark网络协议分析仪中如果解密SSL和TLS流量

要求

  • 以下基本知识:

• 网络追踪

• 网络,TCP/IP和SSL/TLS协议

• 证书和公私钥的使用

• Wireshark网络协议分析仪

  • Wireshark 软件支持SSL解密
  • 服务器或设备的私钥,私钥格式为PKCS#8 PEM

背景

在Wireshark中,SSL解析器功能完整,且支持高级特性,如提供加密私钥时的SSL解密。这对于使用SSL或TLS加密的思杰产品的排错有很大的帮助。

步骤

Wireshark 设置

  • 在Wireshark中,SSL解析器功能完整,且支持高级特性,如提供加密私钥时的SSL解密。这对于使用SSL或TLS加密的思杰产品的排错有很大的帮助。

  • 从菜单中选择Edit > Preferences.

  • 打开Preferences 窗口,展开Protocols.

  • 下拉选择 SSL.

  • 在RSA keys list后的空白处,提供以下信息<ip>,<port>,<protocol>,<key_file_name> (如上图所示)
    其中: 
    <ip>
    是具有私钥的服务器或设备的IP地址
    <port>
     是SSL/TLS端口号,通常是443
    <protocol>
     通常是HTTP
    <key_file_name>
     是私钥的名称和路径is the location and file name of the private key
    Note:
     逗号间没有符号。而且,使用分号间隔用于不同条目。
    “<ip>,<port>,<protocol>,<key_file_name>;<ip>,<port>,<protocol>,<key_file_name>;<ip>,<port>,<protocol>,<key_file_name>”.
  • SSL debug file后的空白处填入排错文件的路径和文件名
  • 点击 OK.
  • SSL则被解密(解密的SSL如下图所示)

私钥格式

Wireshark只要有私钥就可以解密SSL流量。私钥需要时decrypted PKCS#8 PEM format (RSA)格式。你可以打开私钥看它的内容。如果是二进制,则是DER格式,不能用于Wireshark解密。

你可以使用OpenSSL转换密钥格式。例如,可以将PKCS#8 DER格式的密钥转化成decrypted PKCS#8 PEM format (RSA)格式。在$提示符后输入如下指令:

openssl pkcs8 -nocrypt -in der.key -informat DER -out pem.key -outformat PEM

其中:

der.key 是DER密钥文件的文件名和路径

pem.key是pem文件的文件名和路径

解密后的decrypted PKCS#8 PEM format (RSA)格式如下:

注意密钥开头为:

—–BEGIN RSA PRIVATE KEY—–

如果开头为:

—–BEGIN ENCRYPTED PRIVATE KEY—–

则这个密钥需要用适当的方法解密。OpenSSL可以实现。

  • At the $ prompt, enter the command: 在$提示符,输入命令:
    openssl rsa

    If you enter this command without arguments, you are prompted as follows: 如果输入指令不带参数,则出现以下字样:
    read RSA key
  • 输入解密的私钥文件名

你可以在openssl rsa后加上参数,假如你知道私钥和解密的PEM的文件名。例如,私钥的文件名是myprivkey.pvk和解密的文件名问keyout.pem,命令如下:
openssl rsa –in myprivkeypvk -out keyout.pem

附加信息

http://www.wireshark.org/

http://wiki.wireshark.org/SSL

http://www.wireshark.org/docs/dfref/s/ssl.html

http://www.openssl.org/docs/apps/rsa.html#EXAMPLES

http://sourceforge.net/project/showfiles.php?group_id=23617&release_id=4880

手动获取我们所感兴趣网站X.509证书的一般方法

怎样手动获取我们所感兴趣网站的公钥证书?

一、通过抓包从网络协议数据流中获取

因为不管怎么说,在SSL协议的握手阶段,服务器必然会向客户端/浏览器发送自己的证书,所以我要详细说的这第一种获取证书的方法就是通过抓包从网络协议数据流中获取,这是一种通用性非常好的方法。以利用Wireshark 1.10(当然你若是有其他习惯的抓包工具也可以,只要支持SSL协议解析即可)获取account.test01.com的服务器证书例,方法如下:

(1)使用Wireshark 1.10把登录account.test01.com过程的HTTPS会话数据抓取下来(抓包的过程就不多说了,不会问度娘),为了后面定位数据包方便,抓包要尽量干净、完整;

(2)为了防止你还是不小心抓住了其他HTTPS会话的数据,你可以在Windows的命令提示符下用nslookup命令查询account.test01.com的IP地址,假如是192.168.1.57;

(3)使用 Ctrl+F 组合键打开查找对话框,By(查找类型)选择String,Search In(查找区域)选择Packet list,过滤条件写 Certificate ,设定完毕点 Find。如下图:

查找对话框

图1-1  查找对话框设置

(4)查看所查找到的数据包的源IP地址,是否为第(2)步中所获取的192.168.1.57,若是,则成功定位所需数据包;否则使用 Ctrl+N 组合键查看下一个符合查找条件的数据包,直到找到所需数据包为止;

证书所在数据包1

(a)证书和Sever Hello消息分开发

证书所在数据包2

(b)证书和Sever Hello消息在一个包里

图1-2 定位证书所在数据包的两种情况

(5)在上步定位好的数据包中找到证书链所在,如下图靛青色框所标注,

证书和证书链

图1-3 证书和证书链在数据包中所处的位置

        如上图,很多时候服务器发送的不止是自己的应用证书,而是会带上完整的证书链,其中第一个是本应用(即account.test01.com)的证书,图中黑色框标注。【应用证书的持有者,很多时候会使用通配符,例如本例中,account.test01.com所发送证书的持有者是*.test01.com,如果有类似于prox.test01.com,photo.test01.com之类的域名,他们也可以使用这个证书】

另外,图中灰色框标注了给该应用签名的子CA【即DigiCert High Assurance CA-3】的公钥证书,浅灰色框标注了上级CA【即DigiCert High Assurance EV Root CA】的公钥证书。

(6)Wireshark 提供了比较方便地方法从数据包中拷贝出证书,在上图中标注的证书所在位置右键,依次选择”copy”–>”Bytes”–>”Hex Stream”,获取16进制流形式的证书文件。如下图:

证书拷贝

图1-4  证书信息的拷贝

(7)找一款支持16进制流拷贝的工具,比如010Editor,新建一个文件,把上一步得到的十六进制流复制到本文件(一定注意不能使用简单的文本复制,必须基于16进制流格式粘贴,010Editor中使用 Ctrl+Shift+V 组合键即可达到此效果),文件另存为account.test01.com.der。这就是我们要的证书,可以直接双击打开。

 

二、通过浏览器自带的证书复制/导出功能

我要介绍的第二种方法,有一定局限性,但是操作更为简单。我们平时用浏览器访问HTTPS网站时,如果你有细心观察的话,应该能看到浏览器地址栏会有个小锁(不同浏览器显示的位置不太一致),对于搜狗浏览器,如图2-1。

浏览器地址栏

图2-1  搜狗浏览器访问https站点时的地址栏

       点击这个小锁,弹出如下提示:

提示信息

图2-2  “查看证书信息”

       单击“查看证书信息”弹出证书,在“详细信息”选项卡下有个“复制到文件”按钮,如图2-3,点击后按照提示即可成功获取证书文件。

证书复制

图2-3  证书复制

       这个方法的局限性在于只适用于可用浏览器访问的网站,对于那些只用客户端(或者手机应用)访问的网站就无能为力,因为人家的客户端或应用往往都不提供证书复制或导出功能;即使是可用浏览器访问的网站,也只适用于直接使用https协议的网站(比如Gmail),不适用于临时从http跳转到https又跳回http的情况(比如12306)。

使用下面的方法可以从wireshark抓的SSL报文里面得到DER格式的服务器证书。

http://www.wireshark.org/lists/wireshark-users/201003/msg00080.html

Is it possible to extract the x509 ssl certificate from a pcap file? I’m trying to compare a ssl certificate that I have with the one captured in a traffic capture. Searching the archives (and google) have only provided discussions on decrypting ssl traffic which is more than I need.

 

Yes, that’s possible. open the tracefile

1) make sure the setting “Allow subdissector to reassemble TCP streams” is on in the TCP protocol preferences
2) Then go to the packet which contains the SSL handshake message “Certificate”
3) In the packet detail pane, expand the SSL protocol
4) Expand the “Certificate” TLS record
5) Expand the “certificate” handshake protocol
6) Expand the list of certificates. There is now a list of certificate length and certificates (the list could be only 1 certificate). The first certificate is the server certificate, the second it’s signing CA, the third the CA that signed the CA, etc.
7) Now rightclick on the certificate that you want to export
8) Choose “Export selected packet bytes…”
9) Choose a filename and click on save

You now have a file containing the certificate in DER format. You can use openssl to process the certificate as needed.

在Wifi网络中嗅探明文密码(HTTP POST请求、POP等)

全世界,现在大约50%的网站没有使用SSL加密,天朝尤其多。

我们都知道通过HTTP发送的数据都是明文,没有使用任何加密,即使是在数据敏感的登录页面。

本文的目的是:如果你在不熟悉的网络环境中,要注意提高警惕。

没有人希望自己的密码暴露给他人,so,不要嗅探(狗)他人的密码。

想象有一个攻击者坐在某个咖啡馆里,桌子上放着他的笔记本电脑并连着咖啡馆的免费wifi。这个家伙就可以非常容易的获得网络里通信信息。如果密码是明文,连破解都省了。

系统要求:

  • Wireshark
  • ARP欺骗攻击(ARP spoof attack)
  • 无线网卡

无线网卡监听模式和混杂模式有什么不同:

  • 监听模式允许网卡不用连接wifi就可以抓取特性频道的数据,就是在空气中抓取某个波段的数据。可以用在破解wifi密码
  • 混杂模式(连接wifi)就是接收所有经过网卡的数据包,包括不是发给本机的包,即不验证MAC地址
  • 普通模式下网卡只接收发给本机的包

现在的无线路由器都是和主机直接通信,如果你直接使用嗅探工具,只会得到广播信息和自己的连接信息。为了得到网络中所有设备发送的的数据,攻击者必须想办法充当网关的角色,ARP欺骗攻击就干了这样一件事。如果有路由器控制权的话就省事了。

集线器可以直接嗅探,因为所有数据的发送形式是广播。看看这:集线器、交换机和路由器有什么不同

ARP欺骗攻击(ARP spoof attack):


下载安装Wireshark

它也支持Windows、Mac OSX;下载地址:https://www.wireshark.org/download.html

Kali Linux自带了这个工具。

Wireshark

ARP欺骗攻击

我选择使用ettercap,如果你不知道怎么使用,看,Kali Linux ettercap的使用

攻击同一wifi网络中的其他主机:

ettercap

使用Wireshark抓包

现在wifi网络中所有计算机发送的数据包都经过攻击者计算机,攻击者需要抓取这些数据包。

Wireshark

等待其他人使用明文密码。

例如,要找HTTP POST请求,过滤:

http.request.method == "POST"

Wireshark

查看明文密码:

Wireshark

ettercap也可以抓包,它直接提取了明文密码:

ettercap

POP抓取的是我163的邮箱账户。我手机安装了邮件客户端,如果连接网络它会定时收取邮件,在认证的过程中就把密码暴露了。

常用的其他未加密服务:FTP、Telnet等


总结:

  • 不要使用公共网络访问HTTP、POP等不安全服务,尽量使用HTTPS类型的网站。
  • 使用VPN加密连接;顺带还能翻墙。实际上也不是100%可靠,SSH服务器和目标之间还可以做手脚。
  • 密码要勤换,千万不要万年不变。
  • 感觉网络无安全,既要防黑、还要防朝廷。
Written on April 18, 2016

标准ES-POS命令打印,固定IP或蓝牙打印,支持黑白图片打印

Icon

ESC-POS命令打印辅助库

源码地址:ProjectX

介绍

标准ESC-POS命令打印,固定IP或蓝牙打印,支持黑白图片打印。 其中PrintCommands类列出了基本所有的ESC-POS打印命令。 打印指令参考自Commande ESCPOS.pdf文档。

截图

Screenshotsxample

先决条件

  • minSdkVersion 5
  • <uses-permission android:name="android.permission.INTERNET" />
  • <uses-permission android:name="android.permission.BLUETOOTH" />

入门

引用:

dependencies {
    ...
    implementation 'am.util:printer:2.1.0'
    ...
}

添加权限:

添加蓝牙权限<uses-permission android:name="android.permission.BLUETOOTH" />或者网络请求权限<uses-permission android:name="android.permission.INTERNET" />

实现接口:

实现PrintDataMaker接口,完成具体打印任务:

public class TestPrintDataMaker implements PrintDataMaker {

    private Context context;
    private String qr;
    private int width;
    private int height;

    public TestPrintDataMaker(Context context, String qr, int width, int height) {
        this.context = context;
        this.qr = qr;
        this.width = width;
        this.height = height;
    }

    @Override
    public List<byte[]> getPrintData(int type) {
        ArrayList<byte[]> data = new ArrayList<>();
        try {
            PrinterWriter printer;
            printer = type == PrinterWriter58mm.TYPE_58 ? new PrinterWriter58mm(height, width) : new PrinterWriter80mm(height, width);
            printer.setAlignCenter();
            data.add(printer.getDataAndReset());

            ArrayList<byte[]> image1 = printer.getImageByte(context.getResources(), R.drawable.ic_printer_logo);
            data.addAll(image1);

            printer.setAlignLeft();
            printer.printLine();
            printer.printLineFeed();

            printer.printLineFeed();
            printer.setAlignCenter();
            printer.setEmphasizedOn();
            printer.setFontSize(1);
            printer.print("我的餐厅");
            printer.printLineFeed();
            printer.setFontSize(0);
            printer.setEmphasizedOff();
            printer.printLineFeed();

            printer.print("最时尚的明星餐厅");
            printer.printLineFeed();
            printer.print("客服电话:400-8008800");
            printer.printLineFeed();

            printer.setAlignLeft();
            printer.printLineFeed();

            printer.print("订单号:88888888888888888");
            printer.printLineFeed();

            printer.print("预计送达:" +
                    new SimpleDateFormat("yyyy/MM/dd HH:mm", Locale.getDefault())
                            .format(new Date(System.currentTimeMillis())));
            printer.printLineFeed();

            printer.setEmphasizedOn();
            printer.print("#8(已付款)");
            printer.printLineFeed();
            printer.print("××区××路×××大厦××楼×××室");
            printer.printLineFeed();
            printer.setEmphasizedOff();
            printer.print("13843211234");
            printer.print("(张某某)");
            printer.printLineFeed();
            printer.print("备注:多加点辣椒,多加点香菜,多加点酸萝卜,多送点一次性手套");
            printer.printLineFeed();

            printer.printLine();
            printer.printLineFeed();

            printer.printInOneLine("星级美食(豪华套餐)×1", "¥88.88", 0);
            printer.printLineFeed();
            printer.printInOneLine("星级美食(限量套餐)×1", "¥888.88", 0);
            printer.printLineFeed();
            printer.printInOneLine("餐具×1", "¥0.00", 0);
            printer.printLineFeed();
            printer.printInOneLine("配送费", "免费", 0);
            printer.printLineFeed();

            printer.printLine();
            printer.printLineFeed();

            printer.setAlignRight();
            printer.print("合计:977.76");
            printer.printLineFeed();
            printer.printLineFeed();

            printer.setAlignCenter();

            data.add(printer.getDataAndReset());

            String bitmapPath = FileUtils.getExternalFilesDir(context, "Temp") + "tmp_qr.jpg";
            if (QRCodeUtil.createQRImage(qr, 380, 380, null, bitmapPath)) {
                ArrayList<byte[]> image2 = printer.getImageByte(bitmapPath);
                data.addAll(image2);
            } else {
                ArrayList<byte[]> image2 = printer
                        .getImageByte(context.getResources(), R.drawable.ic_printer_qr);
                data.addAll(image2);
            }

            printer.printLineFeed();
            printer.print("扫一扫,查看详情");
            printer.printLineFeed();
            printer.printLineFeed();
            printer.printLineFeed();
            printer.printLineFeed();
            printer.printLineFeed();

            printer.feedPaperCutPartial();

            data.add(printer.getDataAndClose());
            return data;
        } catch (Exception e) {
            return new ArrayList<>();
        }
    }
}

准备打印:

创建打印执行者:

PrintExecutor executor = new PrintExecutor(String ip, int port, int type);
PrintExecutor executor = new PrintExecutor(BluetoothDevice device, int type);

设置执行者状态监听(非必须):

executor.setOnStateChangedListener(new PrintSocketHolder.OnStateChangedListener() {
    @Override
    public void onStateChanged(int state) {
        switch (state) {
            case PrintSocketHolder.STATE_0:
                //生成打印页面数据...
                break;
            case PrintSocketHolder.STATE_1:
                //生成数据成功,开始创建Socket连接...
                break;
            case PrintSocketHolder.STATE_2:
                //创建Socket成功,开始获取输出流...
                break;
            case PrintSocketHolder.STATE_3:
                //获取输出流成功,开始写入打印页面数据...
                break;
            case PrintSocketHolder.STATE_4:
                //写入打印页面数据成功,正在完成打印...
                break;
        }
    }
});

设置执行者回调监听(非必须):

executor.setOnPrintResultListener(new PrintExecutor.OnPrintResultListener() {
    @Override
    public void onResult(int errorCode) {
        switch (errorCode) {
            case PrintSocketHolder.ERROR_0:
                //打印成功完成!
                break;
            case PrintSocketHolder.ERROR_1:
                //生成打印页面数据失败!
                break;
            case PrintSocketHolder.ERROR_2:
                //创建Socket失败!
                break;
            case PrintSocketHolder.ERROR_3:
                //获取输出流失败!
                break;
            case PrintSocketHolder.ERROR_4:
                //写入打印页面数据失败!
                break;
            case PrintSocketHolder.ERROR_5:
                //必要的参数不能为空!
                break;
            case PrintSocketHolder.ERROR_6:
                //关闭Socket出错
                break;
            case PrintSocketHolder.ERROR_100:
                //打印失败
                break;
        }
    }
});

执行打印:

int result = executor.doPrinterRequest(PrintDataMaker maker);//同步
executor.doPrinterRequestAsync(PrintDataMaker maker);//异步

自定义

如果你要实现自己的打印机PrinterWriter,那么你需要继承PrinterWriter,并实现其 必须的方法。示例:

public class PrinterWriter80mm extends PrinterWriter {

    public static final int TYPE_80 = 80;// 纸宽80mm
    public int width = 500;

    public PrinterWriter80mm() throws IOException {
    }

    public PrinterWriter80mm(int parting) throws IOException {
        super(parting);
    }

    public PrinterWriter80mm(int parting, int width) throws IOException {
        super(parting);
        this.width = width;
    }

    @Override
    protected int getLineWidth() {
        //一行能够放下多少个“-”
        return 24;
    }

    @Override
    protected int getLineStringWidth(int textSize) {
        //根据字体的大小,一行可以放下多少个英文字符
        switch (textSize) {
            default:
            case 0:
                return 47;
            case 1:
                return 23;
        }
    }

    @Override
    protected int getDrawableMaxWidth() {
        //图片能够全部打印在纸上的最大宽度
        return width;
    }
}

注意

  • 打印图片出现乱码或者打印不出,大部分原因是打印机缓存较小导致,可调小PrinterWriter80mm的parting参数,设置其图片高度分割值(0~255),调小了还不行的话,缩小图片尺寸或者调整输出流的写入方式。
  • 仅提供建立蓝牙连接打印,不包括蓝牙搜索及配对功能
  • 不包含二维码生成功能
  • PrintCommands包含了大量打印指令,但是并非全部经过测试,而且并非所有打印机都支持。

支持

如果发现错误,请在此处提出: https://github.com/AlexMofer/ProjectX/issues

许可

Copyright (C) 2015 AlexMofer

Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Android——蓝牙连接打印机以及打印格式

我的第一个工作Android项目,刚刚完成使用手机连接打印机然后打印小票的功能,单位买了一个类似车载的打印机,非常小巧,打印机的卖家附送了开发使用的手机连接打印机的代码,非常方便。

代码已经分享到我的git代码库,

https://github.com/hejiawang/PrintDemo

下载地址:

https://codeload.github.com/hejiawang/PrintDemo/zip/master

下载下来基本就能直接用到项目中了,当然,要根据具体业务修改一下了。。。

 

 

里面还有关于打印格式的工具类,能够直接使用,不过使用的时候要注意  /n  符号,不然打印不出格式,比如这个工具类的第二个方法,

Java代码
  1. /**
  2.      * 排版居中内容(以’:’对齐)
  3.      * 
  4.      * 例:姓名:李白
  5.      *     病区:5A病区
  6.      *   住院号:11111
  7.      * 
  8.      * @param msg
  9.      * @return
  10.      */
  11.     public static String printMiddleMsg(LinkedHashMap<String, String> middleMsgMap) {
  12.         sb.delete(0, sb.length());
  13.         String separated = “:”;
  14.         int leftLength = (LINE_BYTE_SIZE – getBytesLength(separated)) / 2;
  15.         for (Entry<String, String> middleEntry : middleMsgMap.entrySet()) {
  16.             for (int i = 0; i < (leftLength – getBytesLength(middleEntry.getKey())); i++) {
  17.                 sb.append(” “);
  18.             }
  19.             sb.append(middleEntry.getKey() + “:” + middleEntry.getValue());
  20.         }
  21.         return sb.toString();
  22.     }

 

在构建map时,map的值一定要以  \n  结尾,才会打印出相应的格式,、

 

Java代码
  1. LinkedHashMap<String, String> middleMsgMap = new LinkedHashMap<String, String>();
  2.         middleMsgMap.put(“日期  “”  “ + timeData + “\n”);
  3.         middleMsgMap.put(“时间  “”  “ + timeL + “\n”);
  4.         middleMsgMap.put(“里程  “”  “ + mileage + “\n”);
  5.         middleMsgMap.put(“金额  “”  “ + money + “\n”);
  6.         middleMsgMap.put(“余额  “”  “ + balance + “\n”);
  7.         String content = BluetoothPrintFormatUtil.printMiddleMsg(middleMsgMap);
  8.         mService.sendMessage(content + “\n”“GBK”);

八大免费SSL证书-给你的网站免费添加Https安全加密

八大免费SSL证书-给你的网站免费添加Https安全加密

SSL证书,用于加密HTTP协议,也就是HTTPS。随着淘宝、百度等网站纷纷实现全站Https加密访问,搜索引擎对于Https更加友好,加上互联网上越来越多的人重视隐私安全,站长们给网站添加SSL证书似乎成为了一种趋势。

给自己的网站添加SSL证书其实并不复杂,但是关键一点就是首先要拥有一个SSL证书。由于SSL证书价格不菲,很多个人站长会选择放弃使用Https。但是,自从开源、免费的Let’s Encrypt证书出现后,我觉得SSL也是我们草根站长可以玩的了。

如果你还在纠结要不要将自己的网站切换到Https,可以看看之前我的分析:我是如何将网站全站启用Https的?本文就来为大家梳理一下当前可供大家免费使用的SSL证书,关于SSL证书申请和安装方法部落之前都有介绍,大家可以直接点击进去查看详情。

作为站长,除了要保护网站数据传输安全,还要保护好域名、服务器等安全:

  • 1、域名防盗:增强域名注册账户和DNS管理账户安全-防止域名被盗和域名恶意解析
  • 2、VPS安全:Linux VPS主机安全-禁止Root登录,密钥授权,Fail2ban,DDoS deflate
  • 3、CDN安全:DNSPOD分布式解析+安全宝和Incapsula对搜索引擎分别CDN加速

八大免费SSL证书-给你的网站免费添加Https安全加密

一、Let’s Encrypt

八大免费SSL证书-Let's Encrypt

1、Let’s Encrypt是国外一个公共的免费SSL项目,由 Linux 基金会托管,它的来头不小,由Mozilla、思科、Akamai、IdenTrust和EFF等组织发起,目的就是向网站自动签发和管理免费证书,以便加速互联网由HTTP过渡到HTTPS。

2、Let’s Encrypt安装部署简单、方便,目前Cpanel、Oneinstack等面板都已经集成了Let’s Encrypt一键申请安装,网上也有不少的利用Let’s Encrypt开源的特性制作的在线免费SSL证书申请网站,总之Let’s Encrypt得到大家的认可。

  • 1、免费SSL证书Let’s Encrypt安装使用教程:Apache和Nginx配置SSL

二、StartSSL

八大免费SSL证书-StartSSL

1、StartSSL是StartCom公司旗下的SSL证书,应该算是免费SSL证书中的“鼻祖”,最早提供完全免费的SSL证书并且被各大浏览器所支持的恐怕就只有StartSSL证书了。任何个人都可以从StartSSL中申请到免费一年的SSL证书。

2、首次申请StartSSL免费SSL证书是免费一年,但是你可以在第二年继续续期。之前StartSSL管理SSL证书只有本地浏览器安装数字证书一种,所以一旦本地的数字证书丢失的话就无法获取到自己之前申请的证书了,不过现在已经增加了邮箱登录了。

3、如果你有看新闻,也许已经知道了“Mozilla正式提议将停止信任 WoSign 和 StartCom 签发的新证书”,对于StartSSL请观察事态发展后再谨慎使用。

  • 1、新StartSSL免费SSL证书申请使用:Apache和Ngnix安装配置SSL证书

三、COMODO PositiveSSL

八大免费SSL证书-COMODO PositiveSSL

1、COMODO官网只有免费90天的SSL证书试用申请,这个COMODO PositiveSSL证书来自UK2公司,VPS.net等就是UK2公司旗下的产品。目前获取UK2提供的免费COMODO PositiveSSL不需要额外的操作,只需要你将域名的IP地址解析到指定的IP即可。

2、先把域名解析到UK2公司的服务器上,然后在网页上获取SSL证书并下载,最后你就可以解除域名解析,同时将下载的域名证书文件上传到服务器配置SSL即可。不过由于是UK2提供的COMODO PositiveSSL免费证书,如果你没有用他们的主机总归不知道哪一天会出问题的。

  • 1、申请和安装COMODO PositiveSSL免费证书及利用VPS自己签发SSL证书

四、

八大免费SSL证书-CloudFlare SSL

1、CloudFlare提供的免费SSL证书是UniversalSSL,即通用SSL,用户无需向证书发放机构申请和配置证书就可以使用的SSL证书,CloudFlare向所有用户(包括免费用户)提供SSL加密功能。

2、不过Universal SSL的服务对免费用户有限制,CloudFlare只支持扩展支持Server Name Indication(SNI)协议的现代浏览器,这意味着它不支持IE6及之前版本、运行Android 2.2或更旧版本的Android浏览器。

  • 1、免费SSL证书:CloudFlare SSL和Wosign沃通SSL申请开通和安装使用

五、Wosign沃通SSL

八大免费SSL证书-Wosign沃通SSL

1、Wosign沃通是国内一家提供SSL证书服务的网站,其免费的SSL证书申请比较简单,在线开通,一个SSL证书只能对应一个域名,支持证书状态在线查询协议(OCSP)。

2、由于Wosign沃通SSL是一家国内的SSL服务商,所以SSL证书申请和管理都比较简单,并且网站使用的是中文有问题还可以联系客服。不过,受“Mozilla正式提议将停止信任 WoSign 和 StartCom 签发的新证书”的影响,请观察后再决定是否使用。

  • 1、免费SSL证书:CloudFlare SSL和Wosign沃通SSL申请开通和安装使用

六、腾讯云DV SSL 证书

八大免费SSL证书-腾讯云DV SSL 证书

1、腾讯云DV SSL 域名型证书由赛门铁克提供自动审核认证,快速签发,支持自动 CSR 生成、域名身份 DNS 自动验证,一步提交申请,审核签发流程全自动。可以一键部署到腾讯云资源,轻松获得数据安全。

  • 1、免费DV SSL证书和AlphaSSL Wildcard SSL证书申请-腾讯云SSL和loovit.net AlphaSSL

七、loovit.net AlphaSSL

八大免费SSL证书-loovit.net AlphaSSL

1、loovit.net 是国外一个网站,背景不详,只是从今年四月份开始陆续有朋友告诉我这个网站提供了免费AlphaSSL证书申请,部落自己试了一下发现申请容易,但是成功率并不是100%。

  • 1、免费DV SSL证书和AlphaSSL Wildcard SSL证书申请-腾讯云SSL和loovit.net AlphaSSL

八、360网站卫士、百度云加速免费SSL

360网站卫士、百度云加速免费SSL

1、360网站卫士、百度云加速与Symantec等合作推出了免费的SSL证书,其实类似于上面的腾讯云DV SSL 证书,只不过360网站卫士如果要使用SSL证书必须得实名认证而且还得使用他们家的CDN。而百度云加速则只能使用百度云服务器才可以申请免费SSL证书。

  • 1、360网站卫士免费DNS和CDN申请使用及CDN缓存无法切换移动主题
  • 2、百度云加速国内免费CDN加速服务使用-百度蜘蛛DNS同步功能

免费SSL证书小结

1、记得几年前使用SSL证书的网站仅限于一些电子商务类的网站,但是现在各大搜索引擎、各类行业网站都纷纷上马了Https,而SSL证书价格也是越来越低,免费的SSL证书也越来越多了,可供大家的选择也是越来越多了。

2、上面介绍的八大免费SSL证书,要说最让人放心的当属Let’s Encrypt了,效果也可以参考部落网站。其它七个免费SSL证书,建议大家谨慎使用,对于一些重要的网站还是建议你直接购买SSL证书:Namecheap SSL一年就十美元。

毛子开发的神器:手机浏览器可用桌面Chrome插件

Chrome之所以被誉为最强的PC浏览器,一大原因就是可以安装各种各样的扩展(国内俗称“插件”,下文就随大家习惯说插件吧),Chrome可以借助插件,实现很多神奇的功能。但是非常遗憾的是,手机上的Chrome,就不支持插件,这让手机版Chrome一下子成为功能最弱鸡的浏览器之一。但是手机上的浏览器,是不是真的就完全和插件无缘了呢?俄罗斯人不答应!来自俄罗斯的手机浏览器Yandex,就可以使用桌面版的Chrome插件,堪称神器!

Yandex浏览器
  • 软件版本:16.11.0.649
  • 软件大小:35.18MB
  • 软件授权:免费
  • 适用平台:Android
立即下载

Yandex这个名字,相信关注互联网的朋友会比较熟悉,这是俄罗斯最大的搜索引擎,地位相当于我朝的百度。Yandex网站是不支持中文的,但Yandex手机浏览器却支持中文语言。Yandex浏览器安卓版不仅支持中文语言,而且首页还会显示中国网站入口等本土化内容,可惜搜索引擎默认却是Google,本土化并不彻底。

手机Chrome插件 手机Chrome扩展手机Chrome插件 手机Chrome扩展
Yandex浏览器主界面,支持中文语言

Yandex浏览器的功能很简单,无非就是常见的支持多标签开启网页,支持翻译啊分享啊等等功能,和其他手机浏览器并没有太大区别。不过,Yandex浏览器的卖点不在于本身功能,而在于可以添加扩展插件!在Yandex浏览器的菜单中,可以看到“扩展插件”的功能入口,点进去就可以安装各种已经为Yandex手机浏览器适配过的插件了。根据页面的描述,Yandex的插件中心有超过1500款插件。

手机Chrome插件 手机Chrome扩展手机Chrome插件 手机Chrome扩展
功能看似平平无奇,重点是“扩展插件”!

手机Chrome插件 手机Chrome扩展手机Chrome插件 手机Chrome扩展
Yandex自带扩展插件中心,但数量和Chrome商店的插件还是没得比

不过,Yandex插件中心所提供的插件,还是偏少的。毕竟这些是特供插件,和电脑上Chrome数以万计的插件相比,还是显得稀少。但是,Yandex插件中心只是个表象,Yandex真正强大的地方,在于可以安装桌面版Yandex的插件,这点是手机版Chrome都无法做到的!

Yandex手机浏览器默认并不开启安装桌面Chrome插件的选项,我们需要一些方法才能使用这功能。

首先,在地址栏输入“Chrome://extensions”,进入到Yandex的扩展管理中心。从这里可以看到,其实Yandex手机浏览器,使用了和Chrome相同的内核,所以Chrome的命令也可以生效。

手机Chrome插件 手机Chrome扩展手机Chrome插件 手机Chrome扩展
进入到扩展插件界面,开启“开发模式”

接着,找到页面上方的“开发模式”,勾选开启。这样,就可以从Yandex手机浏览器安装Chrome插件了。

但是还没完!Yandex并不能直接安装后缀名为“crx”的Chrome插件,我们需要动一些手脚。Chrome插件的后缀名从“crx”改成“zip”,然后用WinRAR等压缩软件解压到一个文件夹中。

手机Chrome插件 手机Chrome扩展
把Chrome插件的文件后缀名改成“zip”

然后,把文件夹中的“_metadata”目录改名或者删掉,不然会安装失败。

手机Chrome插件 手机Chrome扩展
解压压缩包到一个文件夹,删掉里面的“_metadata”目录

最后,就可以用Yandex手机浏览器安装Chrome扩展了。在Yandex中点击“加载已解压的扩展程序”,然后通过文件管理器开启解压了的插件的目录,任意选择一个JS文件,就可以安装了!

手机Chrome插件 手机Chrome扩展手机Chrome插件 手机Chrome扩展
在Yandex中开启插件的文件夹,打开任意JS文件

手机Chrome插件 手机Chrome扩展手机Chrome插件 手机Chrome扩展
安装成功和安装失败的界面,如果没有删除“_metadata”,会安装失败

经过笔者实测,Yandex手机浏览器的确可以成功安装Chrome插件,并成功运行生效。例如笔者安装的BiliBili助手,就能够在Yandex中顺利运行!

手机Chrome插件 手机Chrome扩展手机Chrome插件 手机Chrome扩展
的确可以成功运行Chrome插件!

Yandex手机浏览器安装Chrome插件需要用到本地文件,而不能通过Chrome商店下载。那么Chrome插件可以到哪里下载呢?其实在搜索引擎随便一搜,到处都有。如果可以科学上网,可以这个网站(点此进入)下载Chrome商店的插件,Chrome商店可以点此进入

总的来说,Yandex手机浏览器本身功能平平,但支持Chrome插件这点,令其成为了手机上当之无愧的神器!如果你想在手机上也体验Chrome插件的强大,这款Yandex浏览器值得一试!

国外免费BT离线下载服务收集

#收集控#国外免费BT离线下载服务收集

名称 价格 介绍
OffCloud 免费 每个月只能下载三次,做测试用
Streamza 免费 单文件 1G 限制,队列 1,无法下载回本地,可在线
BytesLoader 免费 单文件无限制,队列 2
FURK 免费 需要邀请码,单文件 2G 限制,队列 2,可在线
DirectTorrents 免费 总文件 10G 限制 | 队列 3
Zbigz 免费 单文件 1G 限制,队列 2,限速 150K,7 天保存
FileStream 免费 单文件 1G 限制,队列 2,3 天保存
FilesLoop 免费 单文件 1G 限制,队列 1
BitPort 免费 单文件 1G 限制,队列 1
Seedr 免费 单文件 2G 限制,队列 1
ByteBX 免费 单文件 100M 限制,队列不限,总空间 2.5G
TorrentSafe 免费 单文件无限制,月队列 3,总空间 10G
2Giga 免费 单文件 2G 限制,队列 1
SonicSeedbox 免费 单文件无限制,天队列 2,总空间 4G
HiperDown 免费 单文件无限制,队列 2,总空间 5G
HiperDown 免费 单文件无限制,队列 2,总空间 5G

Android 蓝牙连接 ESC/POS 热敏打印机打印(ESC/POS指令篇)

上一篇 主要介绍了如何通过蓝牙连接到打印机。这一篇,我们就介绍如何向打印机发送打印指令,来打印字符和图片。

=====================2017.05.09 更新====================

终于抽时间整了一个可以运行的demo出来,实现了以下功能:

  • 检测蓝牙开启状态
  • 显示已配对设备
  • 连接打印机
  • 打印测试,包括打印标题,打印两列三列文字,打印图片等

最终demo及打印的小票示例:

Demo界面
打印小票示例
========================以下是原文=======================

1. 构造输出流

首先要明确一点,就是蓝牙连接打印机这种场景下,手机是 Client 端,打印机是 Server 端。

在上一篇的最后,我们从 BluetoothSocket 得到了一个OutputStream。这里我们做一层包装,得到一个OutputStreamWriter 对象:

OutputStreamWriter writer = new OutputStreamWriter(outputStream, "GBK");

这样做主要是为了后面可以直接输出字符串,不然只能输出 int 或 byte 数据;

2. 常用打印指令

手机通过蓝牙向打印机发送的都是纯字节流,那么打印机如何知道该打印的是一个文本,还是条形码,还是图片数据呢?这里就要介绍 ESC/POS 打印控制命令

  • 初始化打印机
    初始化打印机指令

在每次打印开始之前要调用该指令对打印机进行初始化。向打印机发送这条指令对应的代码就是:

  protected void initPrinter() throws IOException {  
        writer.write(0x1B);  
        writer.write(0x40);  
        writer.flush();  
  }
  • 打印文本
    没有对应指令,直接输出
protected void printText(String text) throws IOException {  
        writer.write(text);
        writer.flush();
    } 
  • 设置文本对齐方式
文本对齐方式指令

对应的发送指令的代码:

    /* 设置文本对齐方式
     * @param align 打印位置  0:居左(默认) 1:居中 2:居右 
     * @throws IOException 
     */  
    protected void setAlignPosition(int align) throws IOException {  
        writer.write(0x1B);  
        writer.write(0x61);  
        writer.write(align);  
        writer.flush();  
    }

与初始化指令不同的是,这条指令带有一个参数n。

  • 换行和制表符
    直接输出对应的字符:
    protected void nextLine() throws IOException {  
        writer.write("\n");  
        writer.flush();  
    }

    protected void printTab(int length) throws IOException {  
        for (int i = 0; i < length; i++) {  
            writer.write("\t");  
        }  
        writer.flush();  
    }  

这两个指令在打印订单详情的时候使用最多。尤其是制表符,可以让每一列的文字对齐。

  • 设置行间距
设置行间距指令

n表示行间距为n个像素点,最大值256

protected void setLineGap(int gap) throws IOException {  
        writer.write(0x1B);  
        writer.write(0x33);  
        writer.write(gap);  
        writer.flush();  
}

这个指令在后面打印图片的时候会用到。

3. 打印图片

很多小票上面都会附上一个二维码,用户扫描之后,可以获得更多的信息。因为热敏打印机只能打印黑白两色,所以首先把图片转成纯黑白的,再调用图片打印指令进行打印。

3.1 打印图片指令

打印图片指令

这个指令的参数很多,一个一个来说:

  • m:取值十进制 0、1、32、33。设置打印精度,0、1对应每行8个点,32、33对应每行24个点,对应最高的打印精度(其实这里也没太搞清楚取值0、1或者取值32、33的区别,只要记住取值33,对应每行24个点,后面还有用)
  • n1, n2 : 表示图片的宽度,为什么有两个?其实只是分成了高位和低位两部分,因为每部分只有8bit,最大表示256。所以 n1 = 图片宽度 % 256,n2 = 图片宽度 / 256。假设图片宽300,那么n1=1,n2=44
  • d1 d2 ... dk 这部分就是转换成字节流的图像数据了

3.2 图片分辨率调整

如果分辨率过大,超过了打印机可打印的最大宽度,那么超出的部分将无法打印。我试验的这台最大宽度是 384 个像素点,超过这个宽度的数据无法被打印出来。所以在开始打印之前,我们需要调整图片的分辨率。代码如下:

    /**
     * 对图片进行压缩(去除透明度)
     *
     * @param bitmapOrg
     */
    public static Bitmap compressPic(Bitmap bitmap) {
        // 获取这个图片的宽和高
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        // 指定调整后的宽度和高度
        int newWidth = 240;
        int newHeight = 240;
        Bitmap targetBmp = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888);
        Canvas targetCanvas = new Canvas(targetBmp);
        targetCanvas.drawColor(0xffffffff);
        targetCanvas.drawBitmap(bitmap, new Rect(0, 0, width, height), new Rect(0, 0, newWidth, newHeight), null);
        return targetBmp;
    }

3.2 图片黑白化处理

因为能够打印的图像只有黑白两色,所以需要先做黑白化的处理。这一部分其实又细分为彩色图片->灰度图片,灰度图片->黑白图片两步。直接上代码:

    /**
     * 灰度图片黑白化,黑色是1,白色是0
     *
     * @param x   横坐标
     * @param y   纵坐标
     * @param bit 位图
     * @return
     */
    public static byte px2Byte(int x, int y, Bitmap bit) {
        if (x < bit.getWidth() && y < bit.getHeight()) {
            byte b;
            int pixel = bit.getPixel(x, y);
            int red = (pixel & 0x00ff0000) >> 16; // 取高两位
            int green = (pixel & 0x0000ff00) >> 8; // 取中两位
            int blue = pixel & 0x000000ff; // 取低两位
            int gray = RGB2Gray(red, green, blue);
            if (gray < 128) {
                b = 1;
            } else {
                b = 0;
            }
            return b;
        }
        return 0;
    }

    /**
     * 图片灰度的转化
     */
    private static int RGB2Gray(int r, int g, int b) {
        int gray = (int) (0.29900 * r + 0.58700 * g + 0.11400 * b);  //灰度转化公式
        return gray;
    }

其中的灰度化转换公式是一个广为流传的公式,具体原理不明。我们直接看灰度转化为黑白的函数 px2Byte(int x, int y, Bitmap bit)。对于一个 Bitmap 中的任意一个坐标点,取出其 RGB 三色信息后做灰度化处理,然后对于灰度小于128的,用黑色表示,灰度大于128的,用白色表示。

3.3 逐行打印图片

其实打印图片和打印文本是一样的,也是一行一行的打印。直接上代码吧,注释已经尽量详细了。

    /*************************************************************************
     * 假设一个240*240的图片,分辨率设为24, 共分10行打印
     * 每一行,是一个 240*24 的点阵, 每一列有24个点,存储在3个byte里面。
     * 每个byte存储8个像素点信息。因为只有黑白两色,所以对应为1的位是黑色,对应为0的位是白色
     **************************************************************************/
    /**
     * 把一张Bitmap图片转化为打印机可以打印的字节流
     *
     * @param bmp
     * @return
     */
    public static byte[] draw2PxPoint(Bitmap bmp) {
        //用来存储转换后的 bitmap 数据。为什么要再加1000,这是为了应对当图片高度无法      
        //整除24时的情况。比如bitmap 分辨率为 240 * 250,占用 7500 byte,
        //但是实际上要存储11行数据,每一行需要 24 * 240 / 8 =720byte 的空间。再加上一些指令存储的开销,
        //所以多申请 1000byte 的空间是稳妥的,不然运行时会抛出数组访问越界的异常。
        int size = bmp.getWidth() * bmp.getHeight() / 8 + 1000;
        byte[] data = new byte[size];
        int k = 0;
        //设置行距为0的指令
        data[k++] = 0x1B;
        data[k++] = 0x33;
        data[k++] = 0x00;
        // 逐行打印
        for (int j = 0; j < bmp.getHeight() / 24f; j++) {
            //打印图片的指令
            data[k++] = 0x1B;
            data[k++] = 0x2A;
            data[k++] = 33; 
            data[k++] = (byte) (bmp.getWidth() % 256); //nL
            data[k++] = (byte) (bmp.getWidth() / 256); //nH
            //对于每一行,逐列打印
            for (int i = 0; i < bmp.getWidth(); i++) {
                //每一列24个像素点,分为3个字节存储
                for (int m = 0; m < 3; m++) {
                    //每个字节表示8个像素点,0表示白色,1表示黑色
                    for (int n = 0; n < 8; n++) {
                        byte b = px2Byte(i, j * 24 + m * 8 + n, bmp);
                        data[k] += data[k] + b;
                    }
                    k++;
                }
            }
            data[k++] = 10;//换行
        }
        return data;
    }

4. 总结

用两篇介绍了一个比较冷门的应用,纯粹是因为自己花了很多时间去搞懂原理,所以希望记录下来。尤其是图片打印部分,废了好多纸啊哈哈哈,一个字节操作错误,打印出来就是一堆乱码。感觉和 java 的 .class 文件很像,每一个指令占用多少位,每一位表示什么都是严格规定好的,不能超出也不能缺少。
最后希望能帮到需要的人吧,感觉网上这部分资料还是比较少的。

作者:VitaminChen
链接:https://www.jianshu.com/p/c0b6d1a4823b
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。