目录
AES加密
编码
异或XOR
加解密步骤
AES加密
从中可以知道对称加密和公钥密码只能保护机密性,防止窃听。
如果要完整性防篡改,需要用单向散列函数、消息认证码、数字签名技术。
这里有个关于对称加密的一个问题:
如这个问题:对称加密如AES。如果一串密文。有人稍微修了下。用密钥去解会不会解出乱码,还是解密失败?拿java测。稍微测了几次是解密失败。但会不会有解出乱码的情况?
问了一些大佬,最后总结出2个答案:
①一个钥匙(密钥)只能开一把锁(加密数据)。但这样就有一个问题既然是这样的模式,那么这个AES,不就具备了一部分了签名的能力,就可以防篡改了,比如https,最后是用了AES加密,那么这个数据用AES加密,是否能防篡改,而上图中对称加密只用来保障机密性。所以这个地方就有点矛盾了。
②乱码和失败都是有可能,关键看AES是那种加密模式,是如果修改数据的。如果是使用流模式加密,能解密,但解出来的都是乱码。块加密模式只要不修改最后一块就能解密,解出来是乱码,块加密模式修改最后一个块,填充校验失败,不能解密。
这里我做了个实验如下代码: AES.java
package cn.it1995.tool;
import javax.crypto.*;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
public class AES {
private SecretKey mKey;
public AES(){
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
//创建随机密码,并设置种子
SecureRandom secureRandom = new SecureRandom();
secureRandom.setSeed(System.currentTimeMillis());
//初始化密钥对象
keyGenerator.init(128, secureRandom);
mKey = keyGenerator.generateKey();
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
public byte[] encrypt(String content){
if(mKey == null){
return new byte[]{-1};
}
try {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, mKey);
return cipher.doFinal(content.getBytes());
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
catch (NoSuchPaddingException e) {
e.printStackTrace();
}
catch (BadPaddingException e) {
e.printStackTrace();
}
catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
catch (InvalidKeyException e) {
e.printStackTrace();
}
return new byte[]{-1};
}
public byte[] decrypt(byte[] content){
if(mKey == null){
return new byte[]{-1};
}
try {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, mKey);
return cipher.doFinal(content);
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
catch (NoSuchPaddingException e) {
e.printStackTrace();
}
catch (BadPaddingException e) {
e.printStackTrace();
}
catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
catch (InvalidKeyException e) {
e.printStackTrace();
}
return new byte[]{-1};
}
}
Main.java
package cn.it1995.tool;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
public class Main {
public static void main(String[] args) throws UnsupportedEncodingException {
String content = "helloWorldHelloWorld111111111111111111111111";
AES aes = new AES();
byte[] encrypted = aes.encrypt(new String(content));
byte[] decrypt = aes.decrypt(encrypted);
System.out.println("encrypted:" + new String(encrypted));
System.out.println("decrypt:" + new String(decrypt));
System.out.println("----------------------------------");
ByteBuffer bb = ByteBuffer.wrap(encrypted);
byte[] cipher = new byte[32];
bb.get(cipher, 0, 16);
for(int i = 5; i < 10; i++){
cipher[i] = 1;
}
for(int i = 0; i < 16; i++){
cipher[16 + i] = encrypted[encrypted.length - 16 + i];
}
System.out.println("删除(篡改)保留最后一块:");
System.out.println(new String(aes.decrypt(cipher)));
}
}
运行截图:
?所以AES并不能防篡改。如果要防篡改需要用单向散列函数、消息认证码、数字签名技术。
编码
编码:将现实世界中的东西映射为比特序列的操作。如midnight:
m->0110 1101
i->0110 1001
d->0110 0100
n->0110 1110
i->0110 1001
g->0110 0111
h->0110 1000
t->0111 0100
异或XOR
这个比较有意思以前的理解为相同为0,不同的为1,
这里看到了有人用棋盘翻转的理解方式。
理解:0表示不翻转,1表示翻转。
0 XOR 0 = 0;没有翻转
1 XOR 0 = 1;翻转了1次
0 XOR 1 = 1;翻转了1次
1 XOR 1 = 0;翻转了2次
加解密步骤
A XOR B = C
C XOR B = A
一串数据与密钥运算,得到加密数据,加密数据在与密钥运算获取明文。
如下:
A:0100 1100
B:1010 1010
来计算一下:
A XOR B:
0100 1100
1010 1010
----------
1110 0110
结果 XOR B:
1110 0110
1010 1010
----------
0100 1100
这里有个结论:数据a异或数据b得到数据c,数据c再与数据数据b异或,可以得到数据a。
图像是这样的:
下面使用Qt来实现这个加密。
编程实例
程序运行截图如下:
先是一个蜡笔小新的彩色图:
?下面是《山坡羊·潼关怀古》
?原理就是上面说的,通过这种方式实现图片掩盖。这里水印也可以这么搞。
工程如下:
源码如下:
Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
uchar *m_secPtr;
uchar *m_xorPtr;
uchar *m_retPtr;
};
#endif // WIDGET_H
?Widget.cpp
#include "Widget.h"
#include "ui_Widget.h"
#include <QImage>
#include <QDebug>
#include <QRandomGenerator>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QImage pix(":/tghg.png");
uchar *oriPtr = pix.bits();
int picSize = pix.sizeInBytes();
m_secPtr = new uchar[picSize];
m_xorPtr = new uchar[picSize];
m_retPtr = new uchar[picSize];
//生成密钥图片
for(int i = 0; i < picSize; i++){
m_secPtr[i] = QRandomGenerator::global()->bounded(256);
}
ui->originLabel->setPixmap(QPixmap::fromImage(pix));
ui->secLabel->setPixmap(QPixmap::fromImage(QImage(m_secPtr, pix.width(), pix.height(), pix.format())));
for(int i = 0; i < picSize; i++){
m_xorPtr[i] = oriPtr[i] ^ m_secPtr[i];
}
ui->xorLabel->setPixmap(QPixmap::fromImage(QImage(m_xorPtr, pix.width(), pix.height(), pix.format())));
//还原
for(int i = 0; i < picSize; i++){
m_retPtr[i] = m_xorPtr[i] ^ m_secPtr[i];
}
ui->retLabel->setPixmap(QPixmap::fromImage(QImage(m_retPtr, pix.width(), pix.height(), pix.format())));
}
Widget::~Widget()
{
delete ui;
delete this->m_retPtr;
delete this->m_secPtr;
delete this->m_xorPtr;
}
main.cpp
#include "Widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
源码打包下载地址:
Qt/XorPic at master · fengfanchen/Qt · GitHub
|