Chapter 1
密码学发展史
古典密码学
古代就已经开始使用密码,目的:就是希望保护信息。
替换法
替换法很好理解,就是用固定的信息将原文替换成无法直接阅读的密文信息。例如将 b 替换成 w ,e 替换成p ,这样bee
单词就变换成了wpp
,不知道替换规则的人就无法阅读出原文的含义。
替换法有单表替换和多表替换两种形式。单表替换即只有一张原文密文对照表单,发送者和接收者用这张表单来加密解密。在上述例子中,表单即为:a b c d e - s w t r p
。
多表替换即有多张原文密文对照表单,不同字母可以用不同表单的内容替换。
例如约定好表单为:表单 1:abcde-swtrp
、表单2:abcde-chfhk
、表单 3:abcde-jftou
。
规定第一个字母用第三张表单,第二个字母用第一张表单,第三个字母用第二张表单,这时bee
单词就变成了(312)fpk
,破解难度更高,其中 312 又叫做密钥,密钥可以事先约定好,也可以在传输过程中标记出来。
移位法
移位法就是将原文中的所有字母都在字母表上向后(或向前)按照一个固定数目进行偏移后得出密文,典型的移位法应用有 “ 恺撒密码 ”。
例如约定好向后移动2位(abcde - cdefg
),这样 bee
单词就变换成了dgg
。
同理替换法,移位法也可以采用多表移位的方式,典型的多表案例是“维尼吉亚密码”(又译维热纳尔密码),属于多表密码的一种形式。
基于ASCII码的凯撒密码Java实现
public class CaesarCode {
public static void main(String[] args) {
String orignal = "Hello world";
// 往右边偏移三位
int key = 3;
// 选中我即将抽取的代码,按快捷键Ctrl + Alt + M
String encryptCaesar = encryptCaesar(orignal,key);
System.out.println("加密:" + encryptCaesar);
String decryptCaesar = decryptCaesar(encryptCaesar,key);
System.out.println("解密:" + decryptCaesar);
}
/**
* 使用凯撒加密方式解密数据
*
* @param encryptedData :密文
* @param key :密钥
* @return : 源数据
*/
public static String decryptCaesar(String encryptedData, int key) {
// 将字符串转为字符数组
char[] chars = encryptedData.toCharArray();
StringBuilder sb = new StringBuilder();
for (char aChar : chars) {
// 获取字符的ASCII编码
int asciiCode = aChar;
// 偏移数据
asciiCode -= key;
// 将偏移后的数据转为字符
char result = (char) asciiCode;
// 拼接数据
sb.append(result);
}
return sb.toString();
}
/**
* 使用凯撒加密方式加密数据
*
* @param orignal :原文
* @param key :密钥
* @return :加密后的数据
*/
public static String encryptCaesar(String orignal, int key) {
// 将字符串转为字符数组
char[] chars = orignal.toCharArray();
StringBuilder sb = new StringBuilder();
for (char aChar : chars) {
// 获取字符的ascii编码
int asciiCode = aChar;
// 偏移数据
asciiCode += key;
// 将偏移后的数据转为字符
char result = (char) asciiCode;
// 拼接数据
sb.append(result);
}
return sb.toString();
}
}
附录拉丁字母共23个字母
https://www.aulafacil.com/cursos/latin/i/el-alfabeto-latino-l24462
El alfabeto latino lo forman las siguientes letras:
A B C D E F G H I J K L M N O P Q R S T V X Y Z
A b c d e f g h i j k l m n o p q r s t u x y z
El alfabeto latino tiene 23 letras.
Tiene una única letra para la U y la V.
Hoy se diferencia la U de la V en la mayoría de los textos latinos, pero pueden encontrarse ediciones dónde no se diferencian estas dos letras (en este curso sí las diferenciaremos).
Las letras K, Y y Z se utilizaban poco.
古典密码破解方式及Java实现
古典密码虽然很简单,但是在密码史上是使用的最久的加密方式,直到“概率论”的数学方法被发现,古典密码就被破解了。
英文单词中字母出现的频率是不同的,e以12.702%的百分比占比最高,z 只占到0.074%,感兴趣的可以去百科查字母频率详细统计数据。如果密文数量足够大,仅仅采用频度分析法就可以破解单表的替换法或移位法。
多表的替换法或移位法虽然难度高一些,但如果数据量足够大的话,也是可以破解的。以维尼吉亚密码算法为例,破解方法就是先找出密文中完全相同的字母串,猜测密钥长度,得到密钥长度后再把同组的密文放在一起,使用频率分析法破解。
提示:直接运行下面的代码不一定会成功,这取决于文件路径设置是否正确。
FrequencyAnalysis.java
package code;
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
/**
* 频率分析法破解凯撒密码
*/
public class FrequencyAnalysis {
//英文里出现次数最多的字符
private static final char MAGIC_CHAR = 'e';
//破解生成的最大文件数
private static final int DE_MAX_FILE = 4;
public static void main(String[] args) throws Exception {
//测试1,统计字符个数
printCharCount("article_en.txt");
//加密文件
// int key = 3;
// encryptFile("src/code/article.txt", "src/code/article_en.txt", key);
//读取加密后的文件
String artile = Util.file2String("article_en.txt");
//解密(会生成多个备选文件)
decryptCaesarCode(artile, "article_de.txt");
}
public static void printCharCount(String path) throws IOException {
String data = Util.file2String(path);
List<Map.Entry<Character, Integer>> mapList = getMaxCountChar(data);
for (Map.Entry<Character, Integer> entry : mapList) {
//输出前几位的统计信息
System.out.println("字符'" + entry.getKey() + "'出现" + entry.getValue() + "次");
}
}
public static void encryptFile(String srcFile, String destFile, int key) throws IOException {
String artile = Util.file2String(srcFile);
//加密文件
String encryptData = CaesarCode.encryptCaesar(artile, key);
//保存加密后的文件
Util.string2File(encryptData, destFile);
}
/**
* 破解凯撒密码
* @param input 数据源
* @return 返回解密后的数据
*/
public static void decryptCaesarCode(String input, String destPath) {
int deCount = 0;//当前解密生成的备选文件数
//获取出现频率最高的字符信息(出现次数越多越靠前)
List<Entry<Character, Integer>> mapList = getMaxCountChar(input);
for (Entry<Character, Integer> entry : mapList) {
//限制解密文件备选数
if (deCount >= DE_MAX_FILE) {
break;
}
//输出前几位的统计信息
System.out.println("字符'" + entry.getKey() + "'出现" + entry.getValue() + "次");
++deCount;
//出现次数最高的字符跟MAGIC_CHAR的偏移量即为秘钥
int key = entry.getKey() - MAGIC_CHAR;
System.out.println("猜测key = " + key + ", 解密生成第" + deCount + "个备选文件" + "\n");
String decrypt = CaesarCode.decryptCaesar(input, key);
String fileName = "de_" + deCount + destPath;
Util.string2File(decrypt, fileName);
}
}
//统计String里出现最多的字符
public static List<Entry<Character, Integer>> getMaxCountChar(String data) {
Map<Character, Integer> map = new HashMap<Character, Integer>();
char[] array = data.toCharArray();
for (char c : array) {
if(!map.containsKey(c)) {
map.put(c, 1);
}else{
Integer count = map.get(c);
map.put(c, count + 1);
}
}
//输出统计信息
/*for (Entry<Character, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + "出现" + entry.getValue() + "次");
}*/
//获取获取最大值
int maxCount = 0;
for (Entry<Character, Integer> entry : map.entrySet()) {
//不统计空格
if (/*entry.getKey() != ' ' && */entry.getValue() > maxCount) {
maxCount = entry.getValue();
}
}
//map转换成list便于排序
List<Map.Entry<Character, Integer>> mapList = new ArrayList<Map.Entry<Character,Integer>>(map.entrySet());
//根据字符出现次数排序
Collections.sort(mapList, new Comparator<Map.Entry<Character, Integer>>(){
public int compare(Map.Entry<Character, Integer> o1,
Entry<Character, Integer> o2) {
return o2.getValue().compareTo(o1.getValue());
}
});
return mapList;
}
}
CaesarCode.java
package code;
public class CaesarCode {
public static void main(String[] args) {
String orignal = "Hello world";
// 往右边偏移三位
int key = 3;
// 选中我即将抽取的代码,按快捷键Ctrl + Alt + M
String encryptCaesar = encryptCaesar(orignal,key);
System.out.println("加密:" + encryptCaesar);
String decryptCaesar = decryptCaesar(encryptCaesar,key);
System.out.println("解密:" + decryptCaesar);
}
/**
* 使用凯撒加密方式解密数据
*
* @param encryptedData :密文
* @param key :密钥
* @return : 源数据
*/
public static String decryptCaesar(String encryptedData, int key) {
// 将字符串转为字符数组
char[] chars = encryptedData.toCharArray();
StringBuilder sb = new StringBuilder();
for (char aChar : chars) {
// 获取字符的ASCII编码
int asciiCode = aChar;
// 偏移数据
asciiCode -= key;
// 将偏移后的数据转为字符
char result = (char) asciiCode;
// 拼接数据
sb.append(result);
}
return sb.toString();
}
/**
* 使用凯撒加密方式加密数据
*
* @param orignal :原文
* @param key :密钥
* @return :加密后的数据
*/
public static String encryptCaesar(String orignal, int key) {
// 将字符串转为字符数组
char[] chars = orignal.toCharArray();
StringBuilder sb = new StringBuilder();
for (char aChar : chars) {
// 获取字符的ascii编码
int asciiCode = aChar;
// 偏移数据
asciiCode += key;
// 将偏移后的数据转为字符
char result = (char) asciiCode;
// 拼接数据
sb.append(result);
}
return sb.toString();
}
}
Util.java
package code;
import java.io.*;
import java.nio.charset.StandardCharsets;
public class Util {
public static void print(byte[] bytes) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
sb.append(bytes[i]).append(" ");
}
System.out.println(sb);
}
public static String file2String(String path) throws IOException {
FileReader reader = new FileReader(new File(path));
char[] buffer = new char[1024];
int len = -1;
StringBuffer sb = new StringBuffer();
while ((len = reader.read(buffer)) != -1) {
sb.append(buffer, 0, len);
}
return sb.toString();
}
public static void string2File(String data, String path){
FileWriter writer = null;
try {
writer = new FileWriter(new File(path));
writer.write(data);
} catch (Exception e) {
e.printStackTrace();
}finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static String inputStream2String(InputStream in) throws IOException {
int len = -1;
byte[] buffer = new byte[1024];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while((len = in.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
baos.close();
return baos.toString(StandardCharsets.UTF_8);
}
}
article.txt
Chief Justice Roberts, President Carter, PresidentClinton, President Bush, President Obama, fellow Americans and people of the world, thank you.
We, the citizens of America, are now joined in a great national effort to rebuild our country and restore its promise for all of our people.
Together, we will determine the course of America and the world for many, many years to come. We will face challenges. We will confront hardships. But we will get the job done.
Every four years we gather on these steps to carry out the orderly and peaceful transfer of power.
And we are grateful to President Obama and first ladyMichelle Obama for their gracious aid throughout this transition.
They have been magnificent.
Thank you.
Today's ceremony, however, has a very special meaning because today we are not merely transferring power from one administration toanother or from one party to another, but we are transferring power fromWashington, D.C., and giving it back to you, the people.
For too long, a small group in our nation's capital has reaped the rewards of government while the people have bore the cost.Washington flourished, but the people did not share in its wealth. Politiciansprospered but the jobs left and the factories closed.
The establishment protected itself, but not the citizens of our country. Their victories have not been your victories. Their triumphs havenot been your triumphs. And while they celebrated in our nation's capital,there was little to celebrate for struggling families all across our land.
That all changes starting right here and right now, because this moment is your moment.
It belongs to you.
It belongs to everyone gathered here today and everyone watching all across America.
This is your day.
This is your celebration.
And this, the United States of America, is your country.
What truly matters is not which party controls our government, but whether our government is controlled by the people.
January 20th, 2017, will be remembered as the day the people became the rulers of this nation again.
The forgotten men and women of our country will be forgotten no longer. Everyone is listening to you now. You came by the tens of millionsto become part of a historic movement, the likes of which the world has neverseen before.
At the center of this movement is a crucial conviction that a nation exists to serve its citizens. Americans want great schools for theirchildren, safe neighborhoods for their families and good jobs for themselves.
These are just and reasonable demands of righteous people and a righteous public.
But for too many of our citizens, a different reality exists.
Mothers and children trapped in poverty in our inner cities,rusted out factories scattered like tombstones across the landscape of ournation.
An education system flush with cash but which leaves our young and beautiful students deprived of all knowledge.
And the crime and the gangs and the drugs that have stolen too many lives and robbed our country of so much unrealized potential. ThisAmerican carnage stops right here and stops right now.
We are one nation, and their pain is our pain.
Their dreams are our dreams, and their success will be our success. We share one heart, one home and one glorious destiny.
The oath of office I take today is an oath of allegiance to all Americans.
For many decades we've enriched foreign industry at the expense of American industry, subsidized the armies of other countries whileallowing for the very sad depletion of our military.
We've defended other nations' borders while refusing to defend our own. And we've spent trillions and trillions of dollars overseaswhile America's infrastructure has fallen into disrepair and decay.
We've made other countries rich while the wealth, strength and confidence of our country has dissipated over the horizon.
One by one, the factories shuttered and left our shores with not even a thought about the millions and millions of American workers thatwere left behind.
The wealth of our middle class has been ripped from their homes and then redistributed all across the world. But that is the past, andnow we are looking only to the future.
We assembled here today are issuing a new decree to be heard in every city, in every foreign capital and in every hall of power. From thisday forward, a new vision will govern our land.
From this day forward, it's going to be only America first,America first. Every decision on trade, on taxes, on immigration, on foreignaffairs will be made to benefit American workers and American families. We mustprotect our borders from the ravages of other countries making our product,stealing our companies and destroying our jobs.
Protection will lead to great prosperity and strength. I will fight for you with every breath in my body, and I will never ever let youdown.
America will start winning again, winning like never before.
We will bring back our jobs.
We will bring back our borders.
We will bring back our wealth, and we will bring back our dreams.
We will build new roads and highways and bridges and airports and tunnels and railways all across our wonderful nation.
We will get our people off of welfare and back to work,rebuilding our country with American hands and American labor.
We will follow two simple rules: Buy American and hireAmerican.
We will seek friendship and goodwill with the nations of the world, but we do so with the understanding that it is the right of all nationsto put their own interests first.
We do not seek to impose our way of life on anyone, but rather to let it shine as an example.
We will shine for everyone to follow.
We will re-enforce old alliances and form new ones and unite the civilized world against radical Islamic terrorism, which we will eradicatecompletely from the face of the earth.
At the bedrock of our politics will be a total allegiance to the United States of America, and through our loyalty to our country we willrediscover our loyalty to each other.
When you open your heart to patriotism, there is no room for prejudice.
The Bible tells us how good and pleasant it is when God's people live together in unity. We must speak our minds openly, debate ourdisagreements honestly, but always pursue solidarity. When America is united,America is totally unstoppable. There should be no fear. We are protected andwe will always be protected. We will be protected by the great men and women ofour military and law enforcement. And most importantly, we will be protected byGod.
Finally, we must think big and dream even bigger. InAmerica, we understand that a nation is only living as long as it is striving.We will no longer accept politicians who are all talk and no action, constantlycomplaining but never doing anything about it.
The time for empty talk is over. Now arrives the hour of action.
Do not allow anyone to tell you that it cannot be done. No challenge can match the heart and fight and spirit of America. We will notfail. Our country will thrive and prosper again.
We stand at the birth of a new millennium, ready to unlock the mysteries of space, to free the earth from the miseries of disease, and toharness the energies, industries and technologies of tomorrow.
A new national pride will stir ourselves, lift our sights and heal our divisions. It's time to remember that old wisdom our soldiers willnever forget, that whether we are black or brown or white, we all bleed thesame red blood of patriots.
We all enjoy the same glorious freedoms and we all salute the same great American flag.
And whether a child is born in the urban sprawl of Detroit or the windswept plains of Nebraska, they look up at the same night sky, theyfill their heart with the same dreams and they are infused with the breath oflife by the same almighty creator.
So to all Americans in every city near and far, small and large, from mountain to mountain, from ocean to ocean, hear these words: Youwill never be ignored again. Your voice, your hopes and your dreams will defineour American destiny. And your courage and goodness and love will forever guideus along the way.
Together we will make America strong again, we will makeAmerica wealthy again, we will make America proud again, we will make America safe again.
And, yes, together we will make America great again.
Thank you.
God bless you.
And God bless America.
破译结果:
字符'#'出现1343次
字符'h'出现817次
字符'r'出现560次
字符'w'出现539次
字符'u'出现510次
字符'd'出现500次
...
字符'#'出现1343次
猜测key = -66, 解密生成第1个备选文件
字符'h'出现817次
猜测key = 3, 解密生成第2个备选文件
字符'r'出现560次
猜测key = 13, 解密生成第3个备选文件
字符'w'出现539次
猜测key = 18, 解密生成第4个备选文件
运行结果 # 出现次数最多, 我们知道在英文当中 e 出现的频率是最高的,我们假设现在 # 号,就是 e ,变形而来的 ,我们可以对照 ascii 编码表 ,我们的凯撒加密当中位移是加了一个 key ,所以我们 猜测 两个值直接相差 -66 ,我们现在就以 -66 进行解密 生成一个文件,我们查看第一个文件发现,根本读不懂,所以解密失败,我们在猜测 h 是 e ,h 和 e 之间相差3 ,所以我们在去看第二个解密文件,发现我们可以读懂,解密成功。
现代密码学
恩尼格玛机
核心原理:替换法,移位法
古典密码的安全性受到了威胁,外加使用便利性较低,到了工业化时代,近现代密码被广泛应用。
恩尼格玛机是二战时期纳粹德国使用的加密机器,后被英国破译,参与破译的人员有被称为计算机科学之父、人工智能之父的图灵。
恩尼格玛机使用的加密方式本质上还是移位和替代,只不过因为密码表种类极多,破解难度高,同时加密解密机器化,使用便捷,因而在二战时期得以使用。
ASCII 编码
ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。它是现今最通用的单字节编码系统,并等同于国际标准ISO/IEC 646。
字符串转换成ascii码 Java实现
public class AsciiDemo {
public static void main(String[] args) {
// char a = 'A';
// int b = a;
// System.out.println(b);
String a = "AaZ";
// 获取ascii码,需要把字符串转成字符 65 97 90
char[] chars = a.toCharArray();
for (char c : chars) {
int asciiCode = c;
System.out.print(asciiCode);
}
}
}
散列函数
散列函数,也见杂凑函数、摘要函数或哈希函数,可将任意长度的消息经过运算,变成固定长度数值,常见的有MD5、SHA-1、SHA256、SHA512,多应用在文件校验,数字签名中。
MD5 可以将任意长度的原文生成一个128位(16字节)的哈希值
SHA-1可以将任意长度的原文生成一个160位(20字节)的哈希值
对称密码
常见的对称密码加密方式是DES、AES。
对称密码应用了相同的加密密钥和解密密钥。对称密码分为:序列密码(流密码),分组密码(块密码)两种。流密码是对信息流中的每一个元素(一个字母或一个比特)作为基本的处理单元进行加密,块密码是先对信息流分块,再对每一块分别加密。
例如原文为1234567890,流加密即先对1进行加密,再对2进行加密,再对3进行加密……最后拼接成密文;块加密先分成不同的块,如1234成块,5678成块,90XX(XX为补位数字)成块,再分别对不同块进行加密,最后拼接成密文。前文提到的古典密码学加密方法,都属于流加密。
非对称密码
对称密码的密钥安全极其重要,加密者和解密者需要提前协商密钥,并各自确保密钥的安全性,一但密钥泄露,即使算法是安全的也无法保障原文信息的私密性。
在实际的使用中,远程的提前协商密钥不容易实现,即使协商好,在远程传输过程中也容易被他人获取,因此非对称密钥此时就凸显出了优势。
非对称密码有两支密钥,公钥(public key)和私钥(private key),加密和解密运算使用的密钥不同。用公钥对原文进行加密后,需要由私钥进行解密;用私钥对原文进行加密后(此时一般称为签名),需要由公钥进行解密(此时一般称为验签)。公钥可以公开的,大家使用公钥对信息进行加密,再发送给私钥的持有者,私钥持有者使用私钥对信息进行解密,获得信息原文。因为私钥只有单一人持有,因此不用担心被他人解密获取信息原文。
如何设置密码才比较安全
- 密码不要太常见,不要使用类似于123456式的常用密码。
- 各应用软件密码建议不同,避免出现一个应用数据库被脱库,全部应用密码崩塌,
- 可在设置密码时增加注册时间、注册地点、应用特性等方法。例如tianjin123456,表示在天津注册的该应用。
可读性算法
Base64
Base64算法是一种用于将二进制数据转换为文本数据的编码方式。它由美国程序员Joe Simons在1973年创立,最初是用来将二进制数据以ASCII编码的形式传输到网络上。Base64 不是加密算法,是可读性算法,目的是提升数据的可读性。
Base64算法的原理是将三个字节的输入数据按照一定的规则划分成四个6位的片段,然后将每个6位的片段转换为对应的Base64字符。最后再在末尾加上1-2个“=”符号,以保证编码后的数据长度为4的倍数。Base64算法的编码表是由大小写字母、数字和“+”、“/”符号组成。
Python 调用
在Python中,可以使用base64模块来实现Base64算法的编码和解码。下面是一个示例代码:
import base64
# 将字符串编码为Base64
text = "Hello, World!"
encoded_text = base64.b64encode(text.encode('utf-8'))
print(encoded_text)
# 将Base64编码的字符串解码
decoded_text = base64.b64decode(encoded_text).decode('utf-8')
print(decoded_text)
以上代码中,首先将字符串“Hello, World!”编码为Base64格式的数据,然后再将其解码为原始字符串。通过使用base64模块,我们可以方便地在Python中进行Base64编码和解码的操作。
Java 调用
要使用Base64编码和解码,在Java中可以使用java.util.Base64类。可以通过以下方法来导入Base64类:
import java.util.Base64;
然后使用Base64编码和解码的例子如下:
public class Base64Example {
public static void main(String[] args) {
String originalString = "Hello, World!";
// 编码
String encodedString = Base64.getEncoder().encodeToString(originalString.getBytes());
System.out.println("Encoded string: " + encodedString);
// 解码
byte[] decodedBytes = Base64.getDecoder().decode(encodedString);
String decodedString = new String(decodedBytes);
System.out.println("Decoded string: " + decodedString);
}
}
以上代码首先将原始字符串"Hello, World!"进行编码,然后再进行解码。最终输出结果如下:
Encoded string: SGVsbG8sIFdvcmxkIQ==
Decoded string: Hello, World!
这样就实现了用Base64和Java对字符串进行加密和解密。
Java中toString()和new String()区别
package code;
import java.util.Base64;
public class TestBase64 {
public static void main(String[] args) {
String str = "TU0jV0xBTiNVYys5bEdiUjZlNU45aHJ0bTdDQStBPT0jNjQ2NDY1Njk4IzM5OTkwMDAwMzAwMA==";
String rlt1 = new String(Base64.getDecoder().decode(str));//MM#WLAN#Uc+9lGbR6e5N9hrtm7CA+A==#646465698#399900003000
String rlt2 = Base64.getDecoder().decode(str).toString();//[B@1540e19d
System.out.println(rlt1);
System.out.println(rlt2);
}
}
str.toString是调用了这个object对象的类的toString方法。一般是返回这么一个String:[class name]@[hashCode]
new String(str)是根据parameter是一个字节数组,使用java虚拟机默认的编码格式,将这个字节数组decode为对应的字符。若虚拟机默认的编码格式是ISO-8859-1,按照ascii编码表即可得到字节对应的字符。
什么时候用什么方法呢?
一般使用字符转码byte[]数组的时候使用new String()
对象打印的时候使用toString()
Base58
Base58算法是一种类似于Base64算法的编码方式,它也用于将二进制数据转换为文本数据。与Base64不同的是,Base58算法不包含容易产生混淆的字符,比如0(数字零)、O(大写字母O)、I(大写字母I)、l(小写字母L),也不包含"+"和"/"符号。
Base58算法通常用于生成易于阅读和书写的短链接、Bitcoin地址等。
Base58算法没有明确的作者和起源,但最为人熟知的是由比特币的创始人中本聪设计并应用于比特币地址的生成。
Base58算法的原理和Base64类似,将输入数据按照一定的规则划分成固定长度的片段,然后对每个片段进行编码转换为Base58字符。Base58算法的编码表通常是由除了易混淆字符外的58个字符组成。
Python实现
在Python中,我们可以使用base58模块来实现Base58算法的编码和解码。以下是一个简单示例代码:
from base58 import b58encode, b58decode
# 将字符串编码为Base58
text = "Hello, World!"
encoded_text = b58encode(text.encode('utf-8'))
print(encoded_text)
# 将Base58编码的字符串解码
decoded_text = b58decode(encoded_text).decode('utf-8')
print(decoded_text)
通过使用base58模块,我们可以轻松地在Python中实现Base58算法的编码和解码操作。
对称加密算法
简介
默认情况下加密模式和填充模式为ECB/PKCS5Padding
使用CBC加密方式,加密、解密都需要创建IV向量。IV向量的长度也必须是特定长度的整数倍。
特点
- 加密速度快, 可以加密大文件
- 密文可逆, 一旦密钥文件泄漏, 就会导致数据暴露
- 加密后编码表找不到对应字符, 出现乱码
- 一般结合Base64使用,解决乱码的问题
DES
DES : Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。
密钥key必须是8位
原理
DES(Data Encryption Standard)是一种对称加密算法,其原理基于置换和替代。下面是DES加密算法的工作原理:
-
密钥生成:DES算法使用固定长度的密钥(56位)进行加密和解密。首先,根据用户提供的密钥,生成16个子密钥,每个子密钥48位长。
-
初始置换(Initial Permutation):明文经过一个固定的初始置换IP(Initial Permutation)置换后得到一个64位的数据块。
-
轮函数(Round Function):DES算法使用16轮替换-置换网络(Feistel Network)来加密数据。每一轮中,明文的左半部分和右半部分会分别经过一系列的替代和置换操作,并且会使用不同的子密钥。
-
替代操作(Substitution):在DES算法中,会使用S盒进行替代操作。S盒是一个4x16的矩阵,将6位输入映射为4位输出。DES算法共有8个不同的S盒。
-
置换操作(Permutation):DES算法中还会进行置换操作,将数据重新排列以增加其随机性。
-
逆初始置换(Final Permutation):经过16轮的替换和置换操作后,得到的加密结果会经过逆初始置换IP^-1,将64位数据块重新排列并输出。
-
密文输出:经过逆初始置换后得到的密文即为加密结果。
总的来说,DES算法通过多轮的替换和置换操作来加密数据,并且通过密钥生成子密钥来增加加密的随机性。虽然DES算法曾经是比较安全的加密算法,但由于密钥长度较短,目前已经不适合用于加密敏感数据,因此已经被更高级的加密算法如AES所取代。
Java调用(DES/ECB/PKCS5Padding)
package code;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class DESDemo {
// DES加密算法,key的大小必须是8个字节
public static void main(String[] args) throws Exception {
String input = "硅谷";
// DES加密算法,比较高级,所以key的大小必须是8个字节
String key = "12345678";
String transformation = "DES/ECB/PKCS5Padding"; // 加密算法/加密模式/填充方式
// 指定获取密钥的算法
String algorithm = "DES";
// 先测试加密,然后在测试解密
String encryptDES = encryptDES(input, key, transformation, algorithm);
System.out.println("加密:" + encryptDES);
String s = decryptDES(encryptDES, key, transformation, algorithm);
System.out.println("解密:" + s);
}
/**
* 使用DES加密数据
*
* @param input : 原文
* @param key : 密钥(DES, 密钥的长度必须是16个字节)
* @param transformation : 获取Cipher对象的算法
* @param algorithm : 获取密钥的算法
* @return : 密文
* @throws Exception
*/
private static String encryptDES(String input, String key, String transformation, String algorithm) throws Exception {
// 获取加密对象
Cipher cipher = Cipher.getInstance(transformation);
// 创建加密规则
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
// ENCRYPT_MODE:加密模式
// DECRYPT_MODE: 解密模式
// 初始化加密模式和算法
cipher.init(Cipher.ENCRYPT_MODE, sks);
// 加密
byte[] bytes = cipher.doFinal(input.getBytes());
// 输出加密后的数据
return Base64.getEncoder().encodeToString(bytes);
}
/**
* 使用DES解密
*
* @param input : 密文
* @param key : 密钥
* @param transformation : 获取Cipher对象的算法
* @param algorithm : 获取密钥的算法
* @return: 原文
* @throws Exception
*/
private static String decryptDES(String input, String key, String transformation, String algorithm) throws Exception {
// 获取Cipher对象
Cipher cipher = Cipher.getInstance(transformation);
// 指定密钥规则
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
cipher.init(Cipher.DECRYPT_MODE, sks);
// 解密
byte[] bytes = cipher.doFinal(Base64.getDecoder().decode(input));
return new String(bytes);
}
}
输出结果如下:
加密:qANksk5lvqM=
解密:硅谷
Python调用
在Python中使用DES加密可以使用pycryptodome
库。以下是一个用DES算法加密字符串“打倒老大哥”并进行解密的示例代码:
from Crypto.Cipher import DES
from Crypto.Random import get_random_bytes
import base64
def encrypt_text(text):
key = b'abcdefgh' # DES密钥必须是8个字节
cipher = DES.new(key, DES.MODE_ECB)
padded_data = text.ljust(8 * (len(text) // 8 + 1), ' ') # 补齐数据长度为8的倍数
ciphertext = cipher.encrypt(padded_data.encode('utf-8'))
encrypted_text = base64.b64encode(ciphertext).decode('utf-8')
return encrypted_text
def decrypt_text(encrypted_text):
key = b'abcdefgh' # DES密钥必须是8个字节
cipher = DES.new(key, DES.MODE_ECB)
ciphertext = base64.b64decode(encrypted_text.encode('utf-8'))
decrypted_data = cipher.decrypt(ciphertext).decode('utf-8').strip()
return decrypted_data
text = "打倒老大哥"
encrypted_text = encrypt_text(text)
print("加密后的字符串:", encrypted_text)
decrypted_text = decrypt_text(encrypted_text)
print("解密后的字符串:", decrypted_text)
在这个示例代码中,我们使用了DES算法,并指定了ECB模式。首先定义了DES加密的密钥key
,并创建一个DES加密对象cipher
。然后对要加密的明文补齐至8的倍数长度,使用密钥对明文进行加密,再将加密后的密文进行Base64编码以便于输出。
运行以上代码,将会先输出加密后的密文,然后再输出解密后的原始明文,即可完成先加密后解密的过程。请注意,在实际应用中,需要注意密文传输的安全性和确保密钥只有合法的用户能够访问。
AES
AES : Advanced Encryption Standard, 高级加密标准 .在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。
密钥key必须是16位
原理
AES(Advanced Encryption Standard)是一种对称加密算法,其原理基于替代和置换。下面是AES加密算法的工作原理:
-
密钥扩展(Key Expansion):AES算法使用固定长度的密钥(128位、192位或256位)来进行加密和解密。在加密和解密过程中,需要生成轮密钥(Round Key)。轮密钥是从初始密钥生成的一系列密钥,每一轮加密或解密过程都使用不同的轮密钥。密钥扩展算法根据初始密钥生成足够的轮密钥。
-
初始轮(Initial Round):AES算法首先使用初始轮密钥对明文进行初始处理。初始轮包括字节替换(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)和轮密钥加(AddRoundKey)。
-
多轮加密(Rounds):AES算法使用多轮加密来保障数据安全。每一轮加密包括字节替换、行移位、列混淆和轮密钥加这四个步骤。加密轮数取决于密钥长度,128位密钥需要10轮,192位密钥需要12轮,256位密钥需要14轮。
-
最终轮(Final Round):在最后一轮加密中,不执行列混淆步骤。最后一轮只包括字节替换、行移位和轮密钥加这三个步骤。
-
密文输出(Cipher Text):经过多轮加密后,明文被转换成密文。最后输出的密文即为加密结果。
总的来说,AES算法是通过多轮的替代和置换操作来保证数据的安全性。密钥长度的不同会导致加密的轮数、轮密钥数量的不同,从而增加了加密的强度和安全性。AES算法是目前最为常用和安全的加密算法之一。
Java实现(AES/ECB/PKCS5Padding)
package code;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class AESDemo {
public static void main(String[] args) throws Exception {
String input = "硅谷";
// AES加密算法,比较高级,所以key的大小必须是16个字节
String key = "1234567812345678";
String transformation = "AES/ECB/PKCS5Padding"; // 加密算法/加密模式/填充方式
// 指定获取密钥的算法
String algorithm = "AES";
// 先测试加密,然后在测试解密
String encryptAES = encryptAES(input, key, transformation, algorithm);
System.out.println("加密:" + encryptAES);
String s = decryptAES(encryptAES, key, transformation, algorithm);
System.out.println("解密:" + s);
}
/**
* 使用AES加密数据
*
* @param input : 原文
* @param key : 密钥(AES, 密钥的长度必须是16个字节)
* @param transformation : 获取Cipher对象的算法
* @param algorithm : 获取密钥的算法
* @return : 密文
* @throws Exception
*/
private static String encryptAES(String input, String key, String transformation, String algorithm) throws Exception {
// 获取加密对象
Cipher cipher = Cipher.getInstance(transformation);
// 创建加密规则
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
// ENCRYPT_MODE:加密模式
// DECRYPT_MODE: 解密模式
// 初始化加密模式和算法
cipher.init(Cipher.ENCRYPT_MODE, sks);
// 加密
byte[] bytes = cipher.doFinal(input.getBytes());
// 输出加密后的数据
return Base64.getEncoder().encodeToString(bytes);
}
/**
* 使用AES解密
*
* @param input : 密文
* @param key : 密钥
* @param transformation : 获取Cipher对象的算法
* @param algorithm : 获取密钥的算法
* @return: 原文
* @throws Exception
*/
private static String decryptAES(String input, String key, String transformation, String algorithm) throws Exception {
// 获取Cipher对象
Cipher cipher = Cipher.getInstance(transformation);
// 指定密钥规则
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
cipher.init(Cipher.DECRYPT_MODE, sks);
// 解密
byte[] bytes = cipher.doFinal(Base64.getDecoder().decode(input));
return new String(bytes);
}
}
输出结果如下:
加密:LIdHYmgk7CfTDY/pvc/SlQ==
解密:硅谷
Python实现
在Python中使用AES加密可以通过pycryptodome
库来实现。
要先对字符串进行加密,然后再对加密后的密文进行解密,可以修改上面的代码稍作调整。下面是一个完整的示例代码:
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
import base64
def encrypt_text(text):
key = b'Sixteen byte key' # AES密钥必须是16个字节(128位)
cipher = AES.new(key, AES.MODE_ECB)
padded_data = text.ljust(16 * (len(text) // 16 + 1), ' ') # 补齐数据长度为16的倍数
ciphertext = cipher.encrypt(padded_data.encode('utf-8'))
encrypted_text = base64.b64encode(ciphertext).decode('utf-8')
return encrypted_text
def decrypt_text(encrypted_text):
key = b'Sixteen byte key' # AES密钥必须是16个字节(128位)
cipher = AES.new(key, AES.MODE_ECB)
ciphertext = base64.b64decode(encrypted_text.encode('utf-8'))
decrypted_data = cipher.decrypt(ciphertext).decode('utf-8').strip()
return decrypted_data
text = "打倒老大哥"
encrypted_text = encrypt_text(text)
print("加密后的字符串:", encrypted_text)
decrypted_text = decrypt_text(encrypted_text)
print("解密后的字符串:", decrypted_text)
在这个示例代码中,我们添加了一个decrypt_text
函数来实现对加密后的密文进行解密。在解密函数中,我们同样需要提供AES加密时使用的密钥,并使用相同的AES对象对密文进行解密操作,最后得到原始的明文。
运行以上代码,将会先输出加密后的密文,然后再输出解密后的原始明文,即可完成先加密后解密的过程。请注意,在实际应用中,需要注意密文传输的安全性和确保密钥只有合法的用户能够访问。
加密方式
前面DES和AES都有key,而加密模式是说怎么用key。
ECB是同key并行,CBC是变key串行
对称加密中的加密模式有很多种,其中比较常见的有 ECB(Electronic Codebook)、CBC(Cipher Block Chaining)、CFB(Cipher Feedback)、OFB(Output Feedback)、CTR(Counter)等。
- ECB(Electronic Codebook)模式:
ECB 是最简单的加密模式,将明文分成固定大小的块,然后每个块独立加密。虽然简单易实现,但由于每个块独立加密,导致相同明文块会得到相同的密文块,存在明显的安全问题。
- CBC(Cipher Block Chaining)模式:
CBC 是一种常见的加密模式,是将前一个加密块的密文和当前明文块进行异或操作,再进行加密。每个块的加密依赖于前一个块的密文,避免了 ECB 模式的问题,并提高了安全性。
- CFB(Cipher Feedback)模式:
CFB 模式将密文作为输入反馈到密码算法中生成密文块,再与明文块异或生成最终的密文。CFB 模式可以将块密码转换为流密码使用,适合于需要实时传输的数据加密。
- OFB(Output Feedback)模式:
OFB 模式将前一个加密块输出作为输入反馈到密码算法中生成密钥流,再与明文异或得到密文。OFB 模式不依赖于前一个块的密文,因此可以提高并行加密的速度。
- CTR(Counter)模式:
CTR 模式通过一个计数器和一个初始化向量生成密钥流,再与明文异或得到密文。CTR 模式避免了与前一个块的依赖关系,并可以实现高效的并行加密。
这些是常见的对称加密模式,每种模式都有自己的特点和适用场景,选择合适的加密模式可以提高数据的安全性和性能。
ECB(Electronic codebook)
电子密码本按照密码块的大小把需要加密的消息分为数个块,然后使用同一个key对每个块分别加密。
优点:可以并行处理数据
缺点:同样的原文生成同样的密文,不能很好地保护数据
同时加密,原文一样则密文也一样。
CBC(Cipher-block chaining)
密码块链接,每个明文块都与前一个密文块进行异或运算后,再使用同一个key进行加密。在这种方法中,每个密文块都依赖于它前面的所有明文块。
优点:同样的原文生成的密文不一样
缺点:串行处理数据,速度较慢
填充模式
对称加密中的填充模式是为了解决明文长度不满足加密算法块大小要求时的处理方式。常见的填充模式有 PKCS#5/PKCS#7 填充、ISO7816-4 填充、Zero 填充和ANSI X.923 填充等。
- PKCS#5/PKCS#7 填充:
PKCS#5 和 PKCS#7 填充是较为常见的填充模式,它们在加密前将明文填充到加密块的大小,填充内容是填充的字节数。PKCS#5 不允许填充长度为块大小,而 PKCS#7 需要填充块大小的长度。在解密时,根据最后一个字节的填充长度可以准确去除填充。
- ISO7816-4 填充:
ISO7816-4 填充是一种简单的填充模式,将剩余需要填充的字节数用 0x80 填充,剩余的位置用 0x00 填充。在解密时,找到第一个 0x80 并移除,后面的 0x00 即为填充部分。
- Zero 填充:
Zero 填充是一种填充模式,将剩余需要填充的字节数用 0x00 填充。在解密时就需要遍历找到非 0 字节,判断是否是填充部分并去除。
- ANSI X.923 填充:
ANSI X.923 填充是一种填充模式,将剩余需要填充的字节数用 0x00 填充,最后一个字节记录填充的长度。在解密时,根据最后一个字节的填充长度可以准确去除填充。
以上是常见的对称加密中的填充模式,选择合适的填充模式可以保证数据在加密后正确性和完整性,并避免数据截断的问题。不同的填充模式适用于不同的场景和系统要求,需要根据具体需求选择合适的填充模式。
NoPadding
不填充。
使用DES加密算法时,要求原文长度必须是8byte的整数倍
使用AES加密算法时,要求原文长度必须是16byte的整数倍
PKCS5Padding
数据块大小为8位,不够就补足。
消息摘要(散列函数)
简介
消息摘要(Message Digest)又称为数字摘要(Digital Digest)。
消息摘要是密码学中一种用于对消息或数据进行摘要和验证的技术。在消息摘要算法中,输入一段数据,经过算法处理后得到一个固定长度的摘要(通常为固定位数的字符串),此摘要就是对原始数据的一种"指纹",不同的数据将会产生不同的摘要。消息摘要的主要作用是确保数据的完整性和验证数据的真实性,防止数据在传输过程中被篡改。
只要输入的消息不同,对其进行摘要以后产生的摘要消息也必不相同;但相同的输入必会产生相同的输出。
消息摘要是单向、不可逆的。
常用算法
常用的消息摘要算法包括MD5(Message Digest Algorithm 5)、SHA系列(Secure Hash Algorithm,包括SHA-1、SHA-256、SHA-512等)、CRC系列(Cyclic Redundancy Check,包括CRC-32)等。其中,MD5是一种多用途的消息摘要算法,SHA-1是较为安全的消息摘要算法,SHA-256和SHA-512具有更高的安全性。
MD5算法 : 摘要结果16个字节, 转16进制后32个字节
SHA1算法 : 摘要结果20个字节, 转16进制后40个字节
SHA256算法 : 摘要结果32个字节, 转16进制后64个字节
SHA512算法 : 摘要结果64个字节, 转16进制后128个字节
消息摘要算法在网络通信、数字签名、数据校验等领域广泛应用,能够有效地验证数据的完整性和真实性,保障信息安全。通过比对接收到的消息摘要和计算得到的消息摘要,可以确认数据的完整性和可靠性,防止恶意篡改而导致数据的不一致性。因此,消息摘要在信息安全领域中具有重要的意义。
MD5
MD5(Message Digest Algorithm 5)是一种广泛使用的消息摘要算法,用于产生数据的哈希值。MD5算法的原理是将输入的任意长度的数据经过一系列复杂的运算转换成一个128位的二进制数字,然后再将这个128位的二进制数字转换成32位的16进制数字,得到最终的MD5摘要。
Python中可以使用 hashlib 模块来调用MD5算法进行摘要计算。以下是一个简单的Python代码示例,将文本”Heil hiter“使用MD5算法进行加密:
import hashlib
# 要加密的文本
text = "Heil hiter"
# 创建一个MD5对象
md5 = hashlib.md5()
# 更新MD5对象的摘要数据
md5.update(text.encode('utf-8'))
# 获取摘要结果
result = md5.hexdigest()
print("MD5加密结果为:", result)
以上代码首先导入 hashlib 模块,然后定义了要加密的文本为"Hello, World!",创建了一个 MD5 对象,更新了文本数据并计算了摘要结果,最后打印出了MD5加密结果。
需要注意的是,MD5算法由于存在已知的安全漏洞,不再被推荐用于安全性要求较高的场景,建议使用更安全的哈希算法如SHA-256等。
SHA系列
SHA算法系列(Secure Hash Algorithm)包括了多个版本,比如SHA-1、SHA-256、SHA-512等,它们都是用于产生数据的哈希值的安全哈希函数。这些算法的原理是将输入的数据进行一系列复杂的转换和运算,最终生成一个固定长度的哈希值。
-
SHA-1:SHA-1算法产生一个160位的哈希值,通常被认为比MD5更安全。不过,由于存在碰撞攻击等问题,SHA-1算法现在也逐渐被废弃。
-
SHA-256:SHA-256算法生成一个256位的哈希值,安全性更高,常用于安全领域。
-
SHA-512:SHA-512算法生成一个512位的哈希值,比SHA-256更安全,适用于对安全性要求更高的场景。
下面是一个使用Python调用SHA-1、SHA-256和SHA-512算法进行加密的示例:
import hashlib
# 要加密的文本
text = "Heil hiter"
# 使用SHA-1算法加密
sha1 = hashlib.sha1()
sha1.update(text.encode('utf-8'))
sha1_result = sha1.hexdigest()
print("SHA-1加密结果为:", sha1_result)
# 使用SHA-256算法加密
sha256 = hashlib.sha256()
sha256.update(text.encode('utf-8'))
sha256_result = sha256.hexdigest()
print("SHA-256加密结果为:", sha256_result)
# 使用SHA-512算法加密
sha512 = hashlib.sha512()
sha512.update(text.encode('utf-8'))
sha512_result = sha512.hexdigest()
print("SHA-512加密结果为:", sha512_result)
以上代码将输入文本"Heil hiter"使用SHA-1、SHA-256和SHA-512算法进行加密,分别得到对应的加密结果并打印出来。这样就可以通过Python调用这些哈希算法来对数据进行加密了。
CRC系列
CRC(Cyclic Redundancy Check)是一种循环冗余校验算法,用于检测数据在传输中是否发生错误或被篡改。CRC算法使用的是多项式除法的方法,通过对数据进行位操作和异或运算生成校验值。
常见的CRC算法包括CRC-32、CRC-16等,其中CRC-32是较为常用的一种,通常用于数据传输校验。
下面是一个使用Python调用CRC-32算法进行校验的示例:
import binascii
# 要加密的文本
text = "Heil hiter"
# 计算CRC-32校验值
crc32 = binascii.crc32(text.encode('utf-8')) & 0xffffffff
print("CRC-32校验结果为:", hex(crc32))
以上代码首先导入了 binascii 模块,然后将文本"Heil hiter"转换为UTF-8编码后,使用 crc32 方法计算CRC-32校验值,最后输出校验结果。需要注意的时,在计算CRC-32校验值时,需要将结果进行位运算与 0xffffffff,以确保结果为一个32位无符号整数。
通过以上示例代码,可以实现对数据进行CRC校验,并得到相应的校验值。CRC算法在数据传输校验、网络通信等领域有着广泛的应用,通过校验值可以快速检测数据在传输中是否发生错误或被篡改。
消息摘要应用(Java实现)
获取字符串消息摘要
package com.atguigu.digest;
import javax.sound.midi.Soundbank;
import java.security.MessageDigest;
public class DigestDemo1 {
public static void main(String[] args) throws Exception{
// 原文
String input = "aa";
// 算法
String algorithm = "MD5";
// 获取数字摘要对象
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
// 获取消息数字摘要的字节数组
byte[] digest = messageDigest.digest(input.getBytes());//原文
System.out.println(new String(digest));
}
}
运行:乱码,所以我们可以使用Base64转一下
1.9.3 base64 编码
package com.atguigu.digest;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import javax.sound.midi.Soundbank;
import java.security.MessageDigest;
public class DigestDemo1 {
public static void main(String[] args) throws Exception{
// 原文
String input = "aa";
// 算法
String algorithm = "MD5";
// 获取数字摘要对象
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
// 消息数字摘要
byte[] digest = messageDigest.digest(input.getBytes());
// System.out.println(new String(digest));
// base64编码
System.out.println(Base64.encode(digest));
}
}
运行:QSS8CpM1wn8IbyS6IHpJEg==
使用在线 md5 加密 ,发现我们生成的值和代码生成的值不一样,那是因为消息摘要不是使用base64进行编码的,所以我们需要把值转成16进制
数字摘要转换成 16 进制:虽然我们不能显示负数,但是我们可以显示字符串,把原来的字节码不用ASCII表示,改用16进制表示即可
// 4124bc0a9335c27f086f24ba207a4912 md5 在线校验
// QSS8CpM1wn8IbyS6IHpJEg== 消息摘要使用的是16进制
代码转成16进制
要注意的是,要有补0的操作,原因在于Integer.toHexString(b & 0xff);这个语句,如果字节是00001111的话,只得到了F
package com.atguigu.digest;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import javax.sound.midi.Soundbank;
import java.security.MessageDigest;
public class DigestDemo1 {
public static void main(String[] args) throws Exception{
// 4124bc0a9335c27f086f24ba207a4912 md5 在线校验
// QSS8CpM1wn8IbyS6IHpJEg== 消息摘要使用的是16进制
// 原文
String input = "aa";
// 算法
String algorithm = "MD5";
// 获取数字摘要对象
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
// 消息数字摘要
byte[] digest = messageDigest.digest(input.getBytes());
// System.out.println(new String(digest));
// base64编码
// System.out.println(Base64.encode(digest));
// 创建对象用来拼接
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
// 转成 16进制
String s = Integer.toHexString(b & 0xff);
//System.out.println(s);
if (s.length() == 1){
// 如果生成的字符只有一个,前面补0
s = "0"+s;
}
sb.append(s);
}
System.out.println(sb.toString());
}
}
运行:4124bc0a9335c27f086f24ba207a4912
其他数字摘要算法
package com.atguigu.digest;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import javax.sound.midi.Soundbank;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class DigestDemo1 {
public static void main(String[] args) throws Exception{
// 4124bc0a9335c27f086f24ba207a4912 md5 在线校验
// QSS8CpM1wn8IbyS6IHpJEg== 消息摘要使用的是16进制
// 原文
String input = "aa";
// 算法
String algorithm = "MD5";
// 获取数字摘要对象
String md5 = getDigest(input, "MD5");
System.out.println(md5);
String sha1 = getDigest(input, "SHA-1");
System.out.println(sha1);
String sha256 = getDigest(input, "SHA-256");
System.out.println(sha256);
String sha512 = getDigest(input, "SHA-512");
System.out.println(sha512);
}
private static String toHex(byte[] digest) throws Exception {
// System.out.println(new String(digest));
// base64编码
// System.out.println(Base64.encode(digest));
// 创建对象用来拼接
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
// 转成 16进制
String s = Integer.toHexString(b & 0xff);
if (s.length() == 1){
// 如果生成的字符只有一个,前面补0
s = "0"+s;
}
sb.append(s);
}
System.out.println("16进制数据的长度:" + sb.toString().getBytes().length);
return sb.toString();
}
private static String getDigest(String input, String algorithm) throws Exception {
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
// 消息数字摘要
byte[] digest = messageDigest.digest(input.getBytes());
System.out.println("密文的字节长度:" + digest.length);
return toHex(digest);
}
}
获取文件消息摘要
package com.atguigu.digest;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import sun.misc.BASE64Decoder;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.security.MessageDigest;
public class DigestDemo {
public static void main(String[] args) throws Exception{
String input = "aa";
String algorithm = "MD5";
// sha1 可以实现秒传功能
String sha1 = getDigestFile("apache-tomcat-9.0.10-windows-x64.zip", "SHA-1");
System.out.println(sha1);
String sha512 = getDigestFile("apache-tomcat-9.0.10-windows-x64.zip", "SHA-512");
System.out.println(sha512);
String md5 = getDigest("aa", "MD5");
System.out.println(md5);
String md51 = getDigest("aa ", "MD5");
System.out.println(md51);
}
private static String getDigestFile(String filePath, String algorithm) throws Exception{
FileInputStream fis = new FileInputStream(filePath);
int len;
byte[] buffer = new byte[1024];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ( (len = fis.read(buffer))!=-1){
baos.write(buffer,0,len);
}
// 获取消息摘要对象
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
// 获取消息摘要
byte[] digest = messageDigest.digest(baos.toByteArray());
System.out.println("密文的字节长度:"+digest.length);
return toHex(digest);
}
private static String getDigest(String input, String algorithm) throws Exception{
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
byte[] digest = messageDigest.digest(input.getBytes());
System.out.println("密文的字节长度:"+digest.length);
return toHex(digest);
}
private static String toHex(byte[] digest) {
// System.out.println(new String(digest));
// 消息摘要进行表示的时候,是用16进制进行表示
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
// 转成16进制
String s = Integer.toHexString(b & 0xff);
// 保持数据的完整性,前面不够的用0补齐
if (s.length()==1){
s="0"+s;
}
sb.append(s);
}
System.out.println("16进制数据的长度:"+ sb.toString().getBytes().length);
return sb.toString();
}
}
运行程序 ,获取 sha-1 和 sha-512 的值
使用 sha-1 算法,可以实现秒传功能,不管咱们如何修改文件的名字,最后得到的值是一样的
如果原文修改了,那么sha-1值 就会不一样
非对称加密算法
简介
① 非对称加密算法又称现代加密算法。
② 非对称加密是计算机通信安全的基石,保证了加密数据不会被破解。
③ 与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(public key) 和私有密钥(private key)
④ 公开密钥和私有密钥是一对
⑤ 如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密。
⑥ 如果用私有密钥对数据进行加密,只有用对应的公开密钥才能解密。
⑦ 因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
特点
- 加密和解密使用不同的密钥
- 如果使用私钥加密, 只能使用公钥解密
- 如果使用公钥加密, 只能使用私钥解密
- 处理数据的速度较慢, 因为安全级别高
常见算法——RSA
RSA算法是一种非对称加密算法,由罗纳德·李维斯特和阿迪·萨莫尔等人发明的。RSA算法的原理基于数论中的大数分解问题,即将一个大数分解为其质因数的乘积。因为大数分解是一个非常困难的问题,使得RSA算法能够提供较高的安全性。
RSA算法的原理是基于两个密钥,分别为公钥和私钥。公钥用于加密数据,私钥用于解密数据。首先,生成一对公钥和私钥,并将公钥发布给他人,而私钥保密。当他人想要向你发送加密的数据时,他们使用你的公钥对数据进行加密,只有持有私钥的你才能解密数据。
RSA算法广泛应用在安全传输和加密通信领域,如HTTPS通信、数字签名、加密文档等。其安全性和可靠性使得RSA成为公认的重要加密算法之一。
原理
假设Alice想要向Bob发送加密的消息,他们采用RSA算法进行加密和解密。
-
生成公钥和私钥: Alice生成公钥(n, e)和私钥(n, d)。其中,n是两个大素数p和q的乘积,e是与(d * e mod φ(n) = 1)互质的数,d是e的模φ(n)的乘法逆元。
-
加密消息: Alice获取Bob的公钥(n, e),并将消息M转换为整数m。然后,使用公钥进行加密,计算密文c = m^e mod n。
-
解密消息: Bob收到密文c后,使用自己的私钥进行解密,计算明文消息m = c^d mod n。
这样,Bob就成功解密了Alice发送的消息。RSA算法通过公钥和私钥的配对来保证数据的安全传输和机密性。
数字示例
首先生成密钥对, 公钥为(5,14), 私钥为(11,14)
现在A希望将原文2发送给B
A使用公钥加密数据, 2的5次方mod 14 = 4 , 将密文4发送给B
B使用私钥解密数据, 4的11次方mod14 = 2, 得到原文2
Python实战RSA
以下是一个使用Python实现RSA算法的示例,并对给定明文进行加密和解密:
RSA算法示例:
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
# 生成RSA密钥对
key = RSA.generate(2048)
public_key = key.publickey()
private_key = key
# 加密和解密操作
cipher = PKCS1_v1_5.new(public_key)
text = "新中国还在黎明中等待复苏"
encrypted = cipher.encrypt(text.encode('utf-8'))
decrypt_cipher = PKCS1_v1_5.new(private_key)
decrypted = decrypt_cipher.decrypt(encrypted, None).decode('utf-8')
print("RSA加密后的密文:", encrypted)
print("RSA解密后的明文:", decrypted)
这两个示例演示了如何使用Python中的RSA和ECC算法对给定的明文进行加密和解密操作。请注意,为了运行这些示例,您可能需要安装相应的密码学库(如pycryptodome和cryptography)。
JavaScript实战RSA
以下是一个使用JavaScript调用RSA算法的示例:
RSA算法示例:
// 使用CryptoJS库进行RSA加密
const publicKey = "<公钥>"; // 公钥需要替换为实际的公钥
const plainText = "新中国还在黎明中等待复苏";
const encrypted = CryptoJS.AES.encrypt(plainText, publicKey).toString();
console.log("RSA加密后的密文:", encrypted);
以上示例中使用了CryptoJS库进行RSA加密,具体的实现细节需要您根据实际情况调整。
常见算法——ECC
椭圆曲线密码学(ECC)是一种基于椭圆曲线理论的密码学技术。ECC的起源可追溯到1985年,由Neal Koblitz和Victor Miller提出。ECC算法在比传统RSA算法更短的密钥长度下,提供了相同甚至更高的安全性,因此在资源受限的环境中广泛应用。
ECC算法的原理是基于椭圆曲线上的点运算。椭圆曲线是由满足特定方程的点构成的集合,具有一些特殊的数学性质。对于给定的椭圆曲线、基点和一条直线,通过多次点运算,可以实现椭圆曲线上的加法、减法、倍乘等操作。ECC算法使用这些点运算来实现加密和签名操作。
ECC算法的应用包括加密通信、数字签名、身份认证等领域。在移动设备、IoT设备和智能卡等资源受限的环境中,ECC算法可以提供更高效的安全解决方案。随着量子计算机等新兴技术的发展,ECC算法也被认为是一种抵御量子计算攻击的有效手段。因此,ECC算法在密码学领域具有重要的地位和广泛的应用前景。
原理
假设Alice和Bob想要使用ECC算法进行加密和解密通信。
-
生成公钥和私钥: Alice和Bob各自生成一对公钥和私钥。每对密钥包括一个椭圆曲线上的点作为公钥,以及一个私钥作为椭圆曲线上的一个整数。
-
加密消息: Alice先将消息m转换为椭圆曲线上的点P,然后使用Bob的公钥点Q对P进行倍乘运算,得到密文点C = k * Q。将C发送给Bob。
-
解密消息: Bob收到密文点C后,使用自己的私钥整数k对C进行倍乘运算,计算得到解密后的点P = k * C。然后将P转换为明文消息m。
这样,Bob成功解密了Alice发送的消息。ECC算法通过椭圆曲线上的点运算来保证数据的安全传输和机密性。
Python实战ECC
以下是一个使用Python实现ECC算法的示例,并对给定明文进行加密和解密:
ECC算法示例:
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.backends import default_backend
# 生成ECC密钥对
private_key = ec.generate_private_key(ec.SECP256R1(), default_backend())
public_key = private_key.public_key()
# 加密和解密操作
encrypted = public_key.encrypt(b"新中国还在黎明中等待复苏", ec.ECIES())
decrypted = private_key.decrypt(encrypted, ec.ECIES())
print("ECC加密后的密文:", encrypted)
print("ECC解密后的明文:", decrypted.decode('utf-8'))
这两个示例演示了如何使用Python中的RSA和ECC算法对给定的明文进行加密和解密操作。请注意,为了运行这些示例,您可能需要安装相应的密码学库(如pycryptodome和cryptography)。
JavaScript实战ECC
以下是一个使用JavaScript调用ECC算法的示例:
ECC算法示例:
// 使用elliptic库进行ECC加密
const EC = require('elliptic').ec;
const ec = new EC('secp256k1'); // 选择椭圆曲线参数
const key = ec.genKeyPair();
const plainText = "新中国还在黎明中等待复苏";
const message = Buffer.from(plainText, 'utf8');
const signature = key.sign(message);
const validSignature = key.verify(message, signature);
console.log("ECC签名:", signature);
console.log("ECC签名验证结果:", validSignature);
以上示例中使用了elliptic库进行ECC签名和验证操作,具体的实现细节需要您根据实际情况调整。请注意,为了运行这些示例,您可能需要安装相应的JavaScript密码学库。
Java-RSA加解密完整实践
生成公钥和私钥
生成RSA的公钥私钥:KeyPairGenerator类
package code.async;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
public class RSADemo {
public static void main(String[] args) throws Exception {
// 加密算法
String algorithm = "RSA";
// 创建密钥对生成器对象
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
// 生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 生成私钥
PrivateKey privateKey = keyPair.getPrivate();
// 生成公钥
PublicKey publicKey = keyPair.getPublic();
// 获取私钥字节数组
byte[] privateKeyEncoded = privateKey.getEncoded();
// 获取公钥字节数组
byte[] publicKeyEncoded = publicKey.getEncoded();
// 对公私钥进行base64编码
String privateKeyString = Base64.getEncoder().encodeToString(privateKeyEncoded);
String publicKeyString = Base64.getEncoder().encodeToString(publicKeyEncoded);
// 打印私钥
System.out.println(privateKeyString);
// 打印公钥
System.out.println(publicKeyString);
}
}
运行程序:先打印的是私钥 , 后面打印的是公钥
私钥加密
package code.async;
import javax.crypto.Cipher;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
public class RSADemo {
public static void main(String[] args) throws Exception {
String plainText = "硅谷没有包子,就像中南海没有小李子一样。没有鸡翅,你吃的什么?";
// 加密算法
String algorithm = "RSA";
// 创建密钥对生成器对象
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
// 生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 生成私钥
PrivateKey privateKey = keyPair.getPrivate();
// 生成公钥
PublicKey publicKey = keyPair.getPublic();
// 获取私钥字节数组
byte[] privateKeyEncoded = privateKey.getEncoded();
// 获取公钥字节数组
byte[] publicKeyEncoded = publicKey.getEncoded();
// 对公私钥进行base64编码
String privateKeyString = Base64.getEncoder().encodeToString(privateKeyEncoded);
String publicKeyString = Base64.getEncoder().encodeToString(publicKeyEncoded);
// 创建加密对象
// 参数表示加密算法
Cipher cipher = Cipher.getInstance(algorithm);
// 初始化加密
// 第一个参数:加密的模式
// 第二个参数:使用私钥进行加密
cipher.init(Cipher.ENCRYPT_MODE,privateKey);
// 私钥加密
byte[] bytes = cipher.doFinal(plainText.getBytes());
System.out.println(Base64.getEncoder().encodeToString(bytes));
}
}
私钥加密私钥解密
package code.async;
import javax.crypto.Cipher;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
public class RSADemo {
public static void main(String[] args) throws Exception {
String plainText = "硅谷没有包子,就像中南海没有小李子一样。没有鸡翅,你吃的什么?";
// 加密算法
String algorithm = "RSA";
// 创建密钥对生成器对象
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
// 生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 生成私钥
PrivateKey privateKey = keyPair.getPrivate();
// 生成公钥
PublicKey publicKey = keyPair.getPublic();
// 获取私钥字节数组
byte[] privateKeyEncoded = privateKey.getEncoded();
// 获取公钥字节数组
byte[] publicKeyEncoded = publicKey.getEncoded();
// 对公私钥进行base64编码
String privateKeyString = Base64.getEncoder().encodeToString(privateKeyEncoded);
String publicKeyString = Base64.getEncoder().encodeToString(publicKeyEncoded);
// 创建加密对象
// 参数表示加密算法
Cipher cipher = Cipher.getInstance(algorithm);
// 初始化加密
// 第一个参数:加密的模式
// 第二个参数:使用私钥进行加密
cipher.init(Cipher.ENCRYPT_MODE,privateKey);
// 私钥加密
byte[] bytes = cipher.doFinal(plainText.getBytes());
System.out.println(Base64.getEncoder().encodeToString(bytes));
// 私钥进行解密
cipher.init(Cipher.DECRYPT_MODE,privateKey);
// 对密文进行解密,不需要使用base64,因为原文不会乱码
byte[] bytes1 = cipher.doFinal(bytes);
System.out.println(new String(bytes1));
}
}
运行程序 ,报错。
原因:私钥加密,只能公钥解密
私钥加密公钥解密
package code.async;
import javax.crypto.Cipher;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
public class RSADemo {
public static void main(String[] args) throws Exception {
String plainText = "硅谷没有包子,就像中南海没有小李子一样。没有鸡翅,你吃的什么?";
// 加密算法
String algorithm = "RSA";
// 创建密钥对生成器对象
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
// 生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 生成私钥
PrivateKey privateKey = keyPair.getPrivate();
// 生成公钥
PublicKey publicKey = keyPair.getPublic();
// 获取私钥字节数组
byte[] privateKeyEncoded = privateKey.getEncoded();
// 获取公钥字节数组
byte[] publicKeyEncoded = publicKey.getEncoded();
// 对公私钥进行base64编码
String privateKeyString = Base64.getEncoder().encodeToString(privateKeyEncoded);
String publicKeyString = Base64.getEncoder().encodeToString(publicKeyEncoded);
// 创建加密对象
// 参数表示加密算法
Cipher cipher = Cipher.getInstance(algorithm);
// 初始化加密
// 第一个参数:加密的模式
// 第二个参数:使用私钥进行加密
cipher.init(Cipher.ENCRYPT_MODE,privateKey);
// 私钥加密
byte[] bytes = cipher.doFinal(plainText.getBytes());
System.out.println(Base64.getEncoder().encodeToString(bytes));
// 私钥进行解密
cipher.init(Cipher.DECRYPT_MODE,publicKey);
// 对密文进行解密,不需要使用base64,因为原文不会乱码
byte[] bytes1 = cipher.doFinal(bytes);
System.out.println(new String(bytes1));
}
}
成功输出明文。
保存公钥和私钥
package com.TianHan;
import javax.crypto.Cipher;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;
import org.apache.commons.io.FileUtils;
public class RSADemo {
public static void main(String[] args) throws Exception {
// 加密算法
String algorithm = "RSA";
//生成密钥对并保存在本地文件中
generateKeyToFile(algorithm, "./code/src/main/resources/a.pub", "./code/src/main/resources/a.pri");
}
/**
* 生成密钥对并保存在本地文件中
*
* @param algorithm : 算法
* @param pubPath : 公钥保存路径
* @param priPath : 私钥保存路径
* @throws Exception
*/
private static void generateKeyToFile(String algorithm, String pubPath, String priPath) throws Exception {
// 创建密钥对生成器对象
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
// 生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 生成私钥
PrivateKey privateKey = keyPair.getPrivate();
// 生成公钥
PublicKey publicKey = keyPair.getPublic();
// 获取私钥字节数组
byte[] privateKeyEncoded = privateKey.getEncoded();
// 获取公钥字节数组
byte[] publicKeyEncoded = publicKey.getEncoded();
// 对公私钥进行base64编码
String privateKeyString = Base64.getEncoder().encodeToString(privateKeyEncoded);
String publicKeyString = Base64.getEncoder().encodeToString(publicKeyEncoded);
// 保存文件
FileUtils.writeStringToFile(new File(pubPath), publicKeyString, StandardCharsets.UTF_8);
FileUtils.writeStringToFile(new File(priPath), privateKeyString, StandardCharsets.UTF_8);
}
/**
* 解密数据
*
* @param algorithm : 算法
* @param encrypted : 密文
* @param key : 密钥
* @return : 原文
* @throws Exception
*/
public static String decryptRSA(String algorithm,Key key,String encrypted) throws Exception{
// 创建加密对象
// 参数表示加密算法
Cipher cipher = Cipher.getInstance(algorithm);
// 私钥进行解密
cipher.init(Cipher.DECRYPT_MODE,key);
// 由于密文进行了Base64编码, 在这里需要进行解码
byte[] decode = Base64.getDecoder().decode(encrypted);
// 对密文进行解密,不需要使用base64,因为原文不会乱码
byte[] bytes1 = cipher.doFinal(decode);
System.out.println(new String(bytes1));
return new String(bytes1);
}
/**
* 使用密钥加密数据
*
* @param algorithm : 算法
* @param input : 原文
* @param key : 密钥
* @return : 密文
* @throws Exception
*/
public static String encryptRSA(String algorithm, Key key, String input) throws Exception{
// 创建加密对象
// 参数表示加密算法
Cipher cipher = Cipher.getInstance(algorithm);
// 初始化加密
// 第一个参数:加密的模式
// 第二个参数:使用私钥进行加密
cipher.init(Cipher.ENCRYPT_MODE, key);
// 私钥加密
byte[] bytes = cipher.doFinal(input.getBytes());
// 对密文进行Base64编码
System.out.println(Base64.getEncoder().encodeToString(bytes));
return Base64.getEncoder().encodeToString(bytes);
}
}
运行在项目目录下生成私钥
运行在项目目录下生成公钥
读取私钥
package com.TianHan;
import java.io.File;
import java.nio.charset.Charset;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import org.apache.commons.io.FileUtils;
public class RSADemo {
public static void main(String[] args) throws Exception {
String path = "./code/src/main/resources/";
// 加密算法
String algorithm = "RSA";
// 从文件读取私钥 ,这个是自定义的方法
PrivateKey privateKey = getPrivateKey(path + "a.pri", algorithm);
System.out.println("privateKey: " + privateKey);
}
public static PrivateKey getPrivateKey(String priPath,String algorithm) throws Exception{
// 将文件内容转为字符串
String privateKeyString = FileUtils.readFileToString(new File(priPath), Charset.defaultCharset());
// 获取密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
// 构建密钥规范 进行Base64解码
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyString));
// 生成私钥
return keyFactory.generatePrivate(spec);
}
}
读取公钥
package com.TianHan;
import java.io.File;
import java.nio.charset.Charset;
import java.security.*;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import org.apache.commons.io.FileUtils;
public class RSADemo {
public static void main(String[] args) throws Exception {
String path = "./code/src/main/resources/";
// 加密算法
String algorithm = "RSA";
PublicKey publicKey = getPublicKey(path + "a.pub", algorithm);
System.out.println(publicKey);
}
public static PublicKey getPublicKey(String publicPath, String algorithm) throws Exception{
// 将文件内容转为字符串
String publicKeyString = FileUtils.readFileToString(new File(publicPath), Charset.defaultCharset());
// 获取密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
// 构建密钥规范 进行Base64解码
X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyString));
// 生成公钥
return keyFactory.generatePublic(spec);
}
}
RSA加解密完整代码
package com.TianHan;
import javax.crypto.Cipher;
import java.io.File;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import org.apache.commons.io.FileUtils;
public class RSADemo {
public static void main(String[] args) throws Exception {
String plainText = "硅谷没有包子,就像中南海没有小李子一样。没有鸡翅,你吃的什么?";
String path = "./code/src/main/resources/";
// 加密算法
String algorithm = "RSA";
// 从文件读取私钥 ,这个是自定义的方法
PrivateKey privateKey = getPrivateKey(path + "a.pri", algorithm);
PublicKey publicKey = getPublicKey(path + "a.pub", algorithm);
String encryptStr = encryptRSA(algorithm, privateKey, plainText);
String decryptStr = decryptRSA(algorithm, publicKey, encryptStr);
System.out.println(decryptStr);
}
public static PublicKey getPublicKey(String publicPath, String algorithm) throws Exception{
// 将文件内容转为字符串
String publicKeyString = FileUtils.readFileToString(new File(publicPath), Charset.defaultCharset());
// 获取密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
// 构建密钥规范 进行Base64解码
X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyString));
// 生成公钥
return keyFactory.generatePublic(spec);
}
public static PrivateKey getPrivateKey(String priPath,String algorithm) throws Exception{
// 将文件内容转为字符串
String privateKeyString = FileUtils.readFileToString(new File(priPath), Charset.defaultCharset());
// 获取密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
// 构建密钥规范 进行Base64解码
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyString));
// 生成私钥
return keyFactory.generatePrivate(spec);
}
/**
* 生成密钥对并保存在本地文件中
*
* @param algorithm : 算法
* @param pubPath : 公钥保存路径
* @param priPath : 私钥保存路径
* @throws Exception
*/
private static void generateKeyToFile(String algorithm, String pubPath, String priPath) throws Exception {
// 创建密钥对生成器对象
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
// 生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 生成私钥
PrivateKey privateKey = keyPair.getPrivate();
// 生成公钥
PublicKey publicKey = keyPair.getPublic();
// 获取私钥字节数组
byte[] privateKeyEncoded = privateKey.getEncoded();
// 获取公钥字节数组
byte[] publicKeyEncoded = publicKey.getEncoded();
// 对公私钥进行base64编码
String privateKeyString = Base64.getEncoder().encodeToString(privateKeyEncoded);
String publicKeyString = Base64.getEncoder().encodeToString(publicKeyEncoded);
// 保存文件
FileUtils.writeStringToFile(new File(pubPath), publicKeyString, StandardCharsets.UTF_8);
FileUtils.writeStringToFile(new File(priPath), privateKeyString, StandardCharsets.UTF_8);
}
/**
* 解密数据
*
* @param algorithm : 算法
* @param encrypted : 密文
* @param key : 密钥
* @return : 原文
* @throws Exception
*/
public static String decryptRSA(String algorithm,Key key,String encrypted) throws Exception{
// 创建加密对象
// 参数表示加密算法
Cipher cipher = Cipher.getInstance(algorithm);
// 私钥进行解密
cipher.init(Cipher.DECRYPT_MODE,key);
// 由于密文进行了Base64编码, 在这里需要进行解码
byte[] decode = Base64.getDecoder().decode(encrypted);
// 对密文进行解密,不需要使用base64,因为原文不会乱码
byte[] bytes1 = cipher.doFinal(decode);
//System.out.println(new String(bytes1));
return new String(bytes1);
}
/**
* 使用密钥加密数据
*
* @param algorithm : 算法
* @param input : 原文
* @param key : 密钥
* @return : 密文
* @throws Exception
*/
public static String encryptRSA(String algorithm, Key key, String input) throws Exception{
// 创建加密对象
// 参数表示加密算法
Cipher cipher = Cipher.getInstance(algorithm);
// 初始化加密
// 第一个参数:加密的模式
// 第二个参数:使用私钥进行加密
cipher.init(Cipher.ENCRYPT_MODE, key);
// 私钥加密
byte[] bytes = cipher.doFinal(input.getBytes());
// 对密文进行Base64编码
//System.out.println(Base64.getEncoder().encodeToString(bytes));
return Base64.getEncoder().encodeToString(bytes);
}
}
数字签名和数字证书
数字签名(公钥数字签名)
起源
数字签名是一种用于确保数字信息的完整性和真实性的加密技术。它起源于1970年代的密码学领域,由Whitfield Diffie 和Martin Hellman首次提出了公钥密码体制。数字签名的原理是使用非对称加密算法生成一对密钥,分别是私钥和公钥。私钥由信息的发送者保管,公钥可以被任何人获取。
数字签名(又称公钥数字签名)是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。它是一种类似写在纸上的普通的物理签名,但是使用了公钥加密领域的技术来实现的,用于鉴别数字信息的方法。一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。
数字签名是非对称密钥加密技术与数字摘要技术的应用。
发展
数字签名在发展过程中,逐渐被广泛应用在各种领域,包括电子支付、电子合同等。同时,为了满足不同应用场景的需求,不断有新的数字签名算法被提出,比如RSA、DSA等。随着互联网和移动通信技术的飞速发展,数字签名在保障信息安全和确保通信可靠性方面发挥着越来越重要的作用。
原理
数字签名的生成过程一般分为两步:首先,信息的发送者使用私钥对信息进行加密,生成一个数字签名;然后,接收者使用发送者的公钥对数字签名进行解密,以验证信息的完整性和真实性。如果数字签名是有效的,接收者就可以确认信息确实是由发送者发送的,并且在传输过程中没有被篡改。
为了理解得清楚,我们通过案例一步一步来讲解。话说张三有俩好哥们A、B。由于工作原因,张三和AB写邮件的时候为了安全都需要加密。于是张三想到了数字签名:
整个思路是这个样子的:
第一步:加密采用非对称加密,张三有三把钥匙,两把公钥,送给朋友。一把私钥留给自己。
第二步:A或者B写邮件给张三:A先用公钥对邮件加密,然后张三收到邮件之后使用私钥解密。
第三步:张三写邮件给A或者B:
(1)张三写完邮件,先用hash函数生成邮件的摘要,附着在文章上面,这就完成了数字签名,然后张三再使用私钥加密。就可以把邮件发出去了。
(2)A或者是B收到邮件之后,先把数字签名取下来,然后使用自己的公钥解密即可。这时候取下来的数字签名中的摘要若和张三的一致,那就认为是张三发来的,再对信件本身使用Hash函数,将得到的结果,与上一步得到的摘要进行对比。如果两者一致,就证明这封信未被修改过。
上面的流程我们使用一张图来演示一下:
服务器把内容(证书明文)经过hash算法得到证书的摘要,再经过私钥加密得到证书的数字签名,然后发送证书和数字签名给浏览器。
浏览器得到了证书和数字签名,先拿证书里的公钥对对数字签名解密得到摘要。再拿证书里的hash算法对证书进行hash得到摘要。对比连个摘要是否相同。如果相同,则代表证书没有被修改过,也就证明了是该服务器发过来的。
但是如果别人伪造了一个证书呢?比如证书里写的颁发给了baidu,而且公钥是第三者的,你发送的内容就被别人拦截下来用第三者的私钥解密,偷看后为了不被发现再用第三者的私钥加密发送给baidu。
为了验证证书没被篡改过,我们可以取认证中心鉴定。
数字证书
数字证书是一种用于在网络通信中进行身份验证和加密数据传输的电子证书。它是CA数字认证中心(Certificate Authority)签发的,用于证明某个实体(个人、组织、服务器等)的身份和公钥的有效性。数字证书通常包含以下信息:
-
主题信息:证书持有者的信息,如姓名、电子邮件地址、组织名称等。
-
公钥:用于加密和解密数据的公钥,公钥是证书持有者的一部分密钥对,另一部分是私钥,只有持有者才知道。
-
证书序列号:标识证书的唯一序列号,用于区分不同的证书。
-
证书有效期:证书的生效日期和过期日期,表示证书的有效期限。
-
数字签名:CA数字认证中心对证书内容的签名,用于验证证书的真实性和完整性。
数字证书的基本原理是通过CA数字认证中心信任链的支持,使用非对称加密算法来确保数字证书的可信性和安全性。一般来说,数字证书的签发流程包括以下步骤:
-
申请证书:证书持有者向CA数字认证中心提交证书申请,包括相关个人或组织的身份认证信息。
-
验证身份:CA数字认证中心会对证书持有者提交的身份信息进行验证,确保其真实性,并签发数字证书。
-
颁发证书:CA数字认证中心在验证通过后,会用自己的私钥对数字证书进行签名,并将数字证书发送给证书持有者。
-
使用数字证书:证书持有者可以将数字证书应用于加密通信、签名验证、身份验证等场景,确保通信的安全性和可信性。
在网页加密中,数字证书扮演着重要角色,用户在访问一个通过HTTPS协议保护的网站时,会验证网站的数字证书,以确认网站的身份和数据传输的安全性。数字证书是构建公钥基础设施(PKI)的核心组成部分,为网络通信提供了必要的安全保障。
网页加密
网页加密的原理主要是通过使用HTTPS协议来保护网页的传输过程中的数据安全。HTTPS是HTTP与SSL/TLS协议的组合,可以确保网页在传输过程中的机密性、完整性和身份验证。
下面是网页加密的原理介绍:
-
建立安全连接:在使用HTTPS的网页上,用户打开网页时,浏览器会向网站请求建立安全连接。网站会发送自己的SSL证书给浏览器,证书包含了网站的公钥等信息。
-
验证证书:浏览器会验证网站的SSL证书,确保证书的有效性和真实性。浏览器会检查证书的签发机构和有效期等信息,以确定当前网站的身份是否可信。
-
生成会话密钥:一旦证书验证成功,浏览器会生成一个随机的会话密钥(对称密钥),用于加密网页内容的传输。
-
加密数据传输:浏览器使用网站的公钥对会话密钥进行加密,并发送给网站。网站利用私钥解密得到会话密钥,之后浏览器和网站之间的通信会使用这个会话密钥对数据进行加密和解密。
-
传输数据:一旦安全连接建立,网页上的数据(包括登录信息、个人数据等)在传输过程中都会被加密,保护数据的机密性。
通过使用HTTPS协议,网页加密可以有效防止数据在传输过程中被窃取、篡改和窥探,提高了网页的安全性和用户的隐私保护。现在越来越多的网站都在推行使用HTTPS协议,以提供更安全可靠的访问环境。
网络监控
虽然HTTPS协议可以加密网页传输过程中的数据,以保护数据的机密性,但外部机构如ISP、学校或公司仍然有能力检测到用户通过浏览器发生的数据。这是因为虽然HTTPS确保了数据传输过程中的加密和安全性,但并不阻止外部机构对一些元数据进行监视和检测。以下是一些原因:
-
域名解析:当用户在浏览器中输入一个网址时,浏览器首先会向DNS服务器发送一个域名解析请求,以获取网站的IP地址。这个请求是明文传输的,即使后续访问是通过HTTPS加密的,ISP仍然可以通过监视DNS请求来知道用户正在访问哪些网站。
-
SNI扩展:在建立HTTPS连接时,客户端向服务器发送Server Name Indication (SNI)扩展,用于指定要访问的域名。这个SNI扩展是明文传输的,可以让网络中的设备(如防火墙、代理服务器)识别出用户正在访问哪个网站。
-
SSL握手过程:在SSL/TLS握手过程中,通信双方会交换加密算法和密钥信息,这些信息中也包括明文的服务器证书。虽然握手过程中的关键数据是加密的,但一些元数据仍然是明文可见的,外部机构可以据此获取有关通信的信息。
-
终端设备:外部机构也可以通过检测终端设备上的浏览历史、缓存数据等信息来掌握用户的网络活动。此外,浏览器的插件或工具也可能会泄露一些用户的浏览数据。
总的来说,虽然HTTPS可以保护数据的机密性和安全性,但并不能完全阻止外部机构对用户网络活动的监视和检测。在一定程度上,外部机构仍然能够通过其他方式获取用户的网络活动信息。因此,用户在网络使用中,仍需保持警惕,注意个人隐私保护。
外部机构可以通过多种方式检测终端设备上的浏览历史、缓存数据等信息来了解用户的网络活动。以下是一些常见的方式:
-
浏览器历史记录:浏览器会保存用户的浏览历史记录,包括访问过的网页和搜索记录。外部机构可以通过访问用户的设备或网络流量分析来获取这些信息。
-
Cookie和网站数据:网站可以在用户的设备上设置Cookie,用于跟踪用户的活动和偏好。外部机构可以访问用户设备上的Cookie数据来获取用户的活动信息。
-
浏览器插件和扩展:一些恶意的浏览器插件和工具可能会窃取用户的浏览历史记录、密码等敏感信息,从而泄露用户的隐私。
-
地址栏和搜索历史:外部机构可以通过访问用户设备上的地址栏和搜索历史记录来了解用户的搜索偏好和访问习惯。
隐私保护
为了防备外部机构通过检测终端设备上的浏览历史、缓存数据等信息来掌握用户的网络活动,用户可以采取一些措施:
-
定期清除浏览历史和缓存:定期清除浏览器的历史记录、缓存数据和Cookie,可以减少外部机构获取用户信息的可能性。
-
使用隐身模式浏览:隐身模式浏览可以防止浏览记录、Cookie等数据被保存在设备上,提高个人隐私保护。
-
谨慎安装浏览器插件:只安装信任的浏览器插件和扩展,避免安装未知来源或官方未审核的插件。
-
使用VPN和加密工具:使用VPN服务和加密工具可以加密网络流量,提高网络通信的隐私性和安全性。
-
定期更新和加强设备安全措施:更新操作系统和软件、安装杀毒软件和防火墙等安全措施,保护设备免受恶意软件和攻击。
除被访问网站和浏览器之外的机构或组织通常不能直接获取用户的浏览器Cookie等信息,因为Cookie数据存储在用户设备上的浏览器中,并受同源策略限制(只有在同一域名下的网站才能访问和修改Cookie)。但有一些情况下,外部机构或组织可能会通过一些方式间接获取用户的浏览器Cookie等信息,例如:
-
网络嗅探:外部机构可以通过网络嗅探技术拦截用户的网络流量,尤其是未加密的HTTP流量。在HTTP请求和响应中,可能会携带用户的Cookie数据,外部机构可以从中获取这些数据。
-
中间人攻击:外部机构可能通过在用户和目标服务器之间插入中间人进行攻击,欺骗用户设备和服务器之间的通信。在这种情况下,中间人可以获取用户的Cookie信息。
-
恶意软件:一些恶意软件和浏览器插件可能会窃取用户设备上存储的Cookie等敏感信息,并将其传送给外部机构。这些恶意软件可能会通过社会工程学手段或漏洞利用等方式感染用户的设备。
防止外部机构通过这些方式获取用户浏览器Cookie等信息可以采取以下措施:
-
使用HTTPS连接:通过使用HTTPS连接访问网站可以加密数据传输,防止中间人窃听,提高数据安全性。
-
避免使用不安全的公共Wi-Fi:尽量避免在不安全的公共Wi-Fi网络上进行敏感操作,以免遭遇网络嗅探等攻击。
-
定期检查设备安全:定期检查设备上的恶意软件,并使用杀毒软件和防火墙等工具保护设备安全。
-
谨慎安装插件:只安装官方可信的浏览器插件和扩展,以避免安全风险。
Python模拟网页加密
是的,可以使用Python模拟网页加密的过程。虽然在实际生产环境中,网页加密是通过浏览器和服务器之间的SSL/TLS协议完成的,但是我们可以使用Python中的加密库和模块来模拟这一过程。
以下是一个简单的Python代码示例,演示了如何使用PyCryptodome库来模拟网页加密的过程:
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
# 模拟网站端
private_key = RSA.generate(2048)
public_key = private_key.publickey()
# 模拟浏览器端
message = b"Hello, world!"
# 模拟浏览器端生成会话密钥
session_key = b"ThisIsASessionKey"
# 浏览器端使用网站的公钥对会话密钥进行加密
cipher = PKCS1_OAEP.new(public_key)
encrypted_session_key = cipher.encrypt(session_key)
# 网站服务端使用私钥解密得到会话密钥
decrypt_cipher = PKCS1_OAEP.new(private_key)
decrypted_session_key = decrypt_cipher.decrypt(encrypted_session_key)
# 利用会话密钥加密数据
data_cipher = PKCS1_OAEP.new(session_key)
encrypted_message = data_cipher.encrypt(message)
# 网站服务端解密数据
decrypted_message = data_cipher.decrypt(encrypted_message)
print("加密后的消息:", encrypted_message)
print("解密后的消息:", decrypted_message)
在上面的代码中,我们模拟了网站端生成RSA密钥对,浏览器端生成会话密钥并使用网站的公钥进行加密,然后网站端使用私钥解密得到会话密钥,并利用会话密钥对消息进行加密和解密的过程。虽然这只是简单的模拟,但可以帮助理解网页加密的基本原理。在实际应用中还需要结合真实的加密库和证书来保护网页传输的安全。
CA认证中心
起源
CA(Certificate Authority)数字认证中心,起源于公钥基础设施(PKI)的概念,最早可以追溯到1990年代。在网络通信中,PKI用于确保通信双方的身份验证、数据加密和签名的过程中。CA数字认证中心作为PKI的核心组成部分,负责颁发数字证书、管理密钥、验证身份等。
发展
随着互联网的快速发展,CA数字认证中心变得越来越重要。各种网站、应用程序、电子商务平台等需要使用数字证书来保证用户数据的安全性和隐私保护。CA数字认证中心经历了不断的发展和完善,确保数字证书的可信性和安全性。
原理
CA数字认证中心的原理是通过数字证书颁发机构来验证个体或组织的身份,并签发相应的数字证书。数字证书包括个体或组织的公钥和相关身份信息,由CA数字认证中心签名保证其可信性。在通信过程中,双方可以通过验证数字证书的有效性来确认对方的身份,并建立安全的通信通道。
典型认证中心
-
VeriSign:作为全球最大的CA数字认证中心之一,VeriSign在互联网安全领域有着较高的声誉。它颁发各种类型的数字证书,包括SSL证书、代码签名证书等。
-
Comodo:Comodo是一家知名的全球CA数字认证中心,拥有庞大的数字证书颁发网络。它提供多种类型的数字证书服务,广泛应用于企业网络、电子商务平台等领域。
-
Symantec:作为全球网络安全领域的领军企业之一,Symantec的CA数字认证中心致力于提供安全可靠的数字证书服务。它的数字证书被广泛应用于各种互联网和移动应用场景中。
数字签名实战
Python
要使用Python实现数字签名,可以使用Python中的cryptography库。以下是一个示例代码,演示如何使用RSA算法对消息进行数字签名和验证:
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes
# 生成RSA密钥对
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
)
public_key = private_key.public_key()
# 消息
message = b"天上地下,唯我独尊!"
# 对消息进行签名
signature = private_key.sign(
message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
# 验证签名
try:
public_key.verify(
signature,
message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
print("验证成功,消息未被篡改!")
except:
print("验证失败,消息已被篡改!")
以上代码生成了一个RSA密钥对,使用私钥对消息进行签名,然后使用公钥验证签名的有效性。在验证过程中,如果签名有效,将输出“验证成功,消息未被篡改!”;如果签名无效,将输出“验证失败,消息已被篡改!”。
Java
生成数字签名并校验
package com.TianHan;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.util.Base64;
import javax.crypto.Cipher;
import java.io.File;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import org.apache.commons.io.FileUtils;
public class DigitalSignatureDemo {
public static void main(String[] args) throws Exception {
String plainText = "时日曷丧,吾与汝皆亡。";
String path = "./code/src/main/resources/";
// 加密算法
String algorithm = "RSA";
// 从文件读取私钥 ,这个是自定义的方法
PrivateKey privateKey = getPrivateKey(path + "a.pri", algorithm);
PublicKey publicKey = getPublicKey(path + "a.pub", algorithm);
// 数字签名
String signatureAlgorithm = "SHA256withRSA";
// Sign the plain text with the private key
String hashedSignature = getHashedSignature(plainText, signatureAlgorithm, privateKey);
//System.out.println("Hashed Signature: " + hashedSignature);
boolean isVerified = verifySignature(plainText, signatureAlgorithm, publicKey, hashedSignature);
System.out.println("Signature verified: " + isVerified);
}
/**
* 生成签名
*
* @param plainText : 原文
* @param algorithm : 算法
* @param privateKey : 私钥
* @return : 签名
*/
private static String getHashedSignature(String plainText, String algorithm, PrivateKey privateKey) throws Exception {
// TODO: Implement the digital signature algorithm
// Use the private key to sign the plain text and return the hashed signature
//获取签名对象
Signature signature = Signature.getInstance(algorithm);
//初始化签名对象
signature.initSign(privateKey);
//更新签名数据
signature.update(plainText.getBytes());
//生成签名
byte[] signedBytes = signature.sign();
//返回签名字符串
return Base64.getEncoder().encodeToString(signedBytes);
}
/**
* 校验签名
*
* @param plainText : 原文
* @param algorithm : 算法
* @param publicKey : 公钥
* @param hashedSignature : 签名
* @return : 数据是否被篡改
*/
private static boolean verifySignature(String plainText, String algorithm, PublicKey publicKey, String hashedSignature)throws Exception {
// 获取签名对象
Signature signature = Signature.getInstance(algorithm);
// 初始化签名
signature.initVerify(publicKey);
// 传入原文
signature.update(plainText.getBytes());
// 校验数据,验证自己活得的签名和传入的签名是否匹配
return signature.verify(Base64.getDecoder().decode(hashedSignature));
}
public static PublicKey getPublicKey(String publicPath, String algorithm) throws Exception {
// 将文件内容转为字符串
String publicKeyString = FileUtils.readFileToString(new File(publicPath), Charset.defaultCharset());
// 获取密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
// 构建密钥规范 进行Base64解码
X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyString));
// 生成公钥
return keyFactory.generatePublic(spec);
}
public static PrivateKey getPrivateKey(String priPath, String algorithm) throws Exception {
// 将文件内容转为字符串
String privateKeyString = FileUtils.readFileToString(new File(priPath), Charset.defaultCharset());
// 获取密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
// 构建密钥规范 进行Base64解码
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyString));
// 生成私钥
return keyFactory.generatePrivate(spec);
}
/**
* 生成密钥对并保存在本地文件中
*
* @param algorithm : 算法
* @param pubPath : 公钥保存路径
* @param priPath : 私钥保存路径
* @throws Exception
*/
private static void generateKeyToFile(String algorithm, String pubPath, String priPath) throws Exception {
// 创建密钥对生成器对象
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
// 生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 生成私钥
PrivateKey privateKey = keyPair.getPrivate();
// 生成公钥
PublicKey publicKey = keyPair.getPublic();
// 获取私钥字节数组
byte[] privateKeyEncoded = privateKey.getEncoded();
// 获取公钥字节数组
byte[] publicKeyEncoded = publicKey.getEncoded();
// 对公私钥进行base64编码
String privateKeyString = Base64.getEncoder().encodeToString(privateKeyEncoded);
String publicKeyString = Base64.getEncoder().encodeToString(publicKeyEncoded);
// 保存文件
FileUtils.writeStringToFile(new File(pubPath), publicKeyString, StandardCharsets.UTF_8);
FileUtils.writeStringToFile(new File(priPath), privateKeyString, StandardCharsets.UTF_8);
}
/**
* 解密数据
*
* @param algorithm : 算法
* @param encrypted : 密文
* @param key : 密钥
* @return : 原文
* @throws Exception
*/
public static String decryptRSA(String algorithm, Key key, String encrypted) throws Exception {
// 创建加密对象
// 参数表示加密算法
Cipher cipher = Cipher.getInstance(algorithm);
// 私钥进行解密
cipher.init(Cipher.DECRYPT_MODE, key);
// 由于密文进行了Base64编码, 在这里需要进行解码
byte[] decode = Base64.getDecoder().decode(encrypted);
// 对密文进行解密,不需要使用base64,因为原文不会乱码
byte[] bytes1 = cipher.doFinal(decode);
//System.out.println(new String(bytes1));
return new String(bytes1);
}
/**
* 使用密钥加密数据
*
* @param algorithm : 算法
* @param input : 原文
* @param key : 密钥
* @return : 密文
* @throws Exception
*/
public static String encryptRSA(String algorithm, Key key, String input) throws Exception {
// 创建加密对象
// 参数表示加密算法
Cipher cipher = Cipher.getInstance(algorithm);
// 初始化加密
// 第一个参数:加密的模式
// 第二个参数:使用私钥进行加密
cipher.init(Cipher.ENCRYPT_MODE, key);
// 私钥加密
byte[] bytes = cipher.doFinal(input.getBytes());
// 对密文进行Base64编码
//System.out.println(Base64.getEncoder().encodeToString(bytes));
return Base64.getEncoder().encodeToString(bytes);
}
}
隐写术
Web3
Web3:面向资产的互联网
Web3是指下一代互联网(Web3.0),它基于区块链技术和去中心化的概念,旨在重新定义互联网的架构和数据所有权。Web3的目标是建立一个去中心化、安全、透明和开放的互联网生态系统,让用户能够更好地掌握和保护自己的数据和隐私。
Web3 关键特定和原则
以下是Web3的一些关键特点和原则:
-
去中心化:Web3采用去中心化的技术和架构,避免了传统互联网中集中式互联网巨头对数据和信息的控制。用户可以直接与其他用户进行去中心化交易和互动,无需依赖第三方中介。
-
用户数据所有权:在Web3中,用户拥有其个人数据的绝对所有权,可以自主管理和控制数据的访问权限和使用方式。个人数据不再被互联网公司滥用,用户可以选择以加密方式存储其数据,确保数据的安全性和隐私性。
-
去信任:通过区块链技术和智能合约,Web3建立了一种去信任的交易和合作模式,不再需要第三方中介机构作为信任的桥梁。智能合约可以自动执行合同条款,确保交易的透明和可靠性。
-
开放协议:Web3基于开放的协议和标准,允许开发者构建各种去中心化应用(DApps)和服务,从而推动互联网应用领域的创新和发展。
-
去监管:Web3的去中心化和加密特性使得政府和监管机构难以干预和控制,用户能够更加自主地管理自己的数据和资产,实现真正的数字主权。
总的来说,Web3代表了一个新的互联网时代,致力于重新构建互联网的基础架构和价值体系,赋予用户更多自主权和隐私权,同时推动区块链技术在互联网领域的广泛应用。
dApp
在Web3中,DApp是指去中心化应用(Decentralized Application),是建立在区块链技术或去中心化网络上的应用程序。DApp与传统的中心化应用程序有所不同,其核心特点包括:
-
去中心化:DApp不依赖于中心化的服务器或机构,其数据存储和运行逻辑通过区块链网络上的节点来管理和执行,具有更高的安全性和透明性。
-
开放性:DApp的源代码通常是开放的,任何人都可以查看、审阅和参与改进,提高了应用的透明度和可信度。
-
没有单点故障:由于DApp的数据存储和逻辑执行分布在多个节点上,没有单一的中心节点,因此不容易受到攻击或发生故障。
-
去中心化的应用生态系统:DApp通常会构建在以太坊、EOS、波场等区块链平台上,并与区块链上的智能合约进行交互,实现各种去中心化的功能和服务,例如数字资产交易、投票治理、预测市场等。
DApp的开发和应用正在不断推动区块链技术的发展和广泛应用,为用户提供更加开放、透明和自主的数字化体验。随着Web3技术的推进,DApp将在未来成为更加重要的一部分,为用户和开发者带来更多创新和可能性。
参考资源
GitHub仓库
https://github.com/ahalfman/blockchain-course-2024
https://github.com/89lixx/BlockChain-BigHW
https://github.com/shiwenwen/BlockChainSimpleDemo
Python and Blockchain: Developing Decentralized Applications with Smart Contracts | Solidity
https://freedium.cfd/https://medium.com/@danielbuilescu/python-and-blockchain-developing-decentralized-applications-with-smart-contracts-solidity-eb8544356428
https://ethereum.org/en/developers/docs/programming-languages/python/
https://snakecharmers.ethereum.org/a-developers-guide-to-ethereum-pt-1/
https://medium.com/@arashtad/how-to-deploy-a-smart-contract-using-python-web3-tools-a-full-coverage-59e6c2ad3f9f
https://blog.arashtad.com/doc/How%20to%20Deploy%20A%20Smart%20Contract%20phyton%20web3.pdf
https://blog.adnansiddiqi.me/develop-and-deploy-your-first-ethereum-smart-contract-with-python/
区块链
何谓区块链?
区块链是一种分布式账本技术,通过去中心化和去信任的方式集体维护一个可靠的数据库。这种技术方案在多个领域中展现出其独特的价值。
从数据的角度来看:区块链是一种几乎不可能被更改的分布式数据库。这里的“分布式”不仅体现为数据的分布式存储,也体现为数据的分布式记录(即由系统参与者共同维护)。这种特性使得数据的安全性和完整性得到了有效保障。
从技术的角度来看:区块链并不是一种单一的技术,而是多种技术整合的结果。这些技术以新的结构组合在一起,形成了一种新的数据记录、存储和表达的方式,具备了高度的灵活性和适应性。
区块链技术起源于2008年,由中本聪在一篇名为《比特币:一种点对点的电子现金系统》的论文中首次提出。这篇论文描述了一种去中心化的数字货币系统,即比特币系统,它基于区块链技术,可以实现在没有中心化机构监管下的安全交易。比特币的诞生标志着区块链技术的诞生。
随后,人们意识到区块链技术的潜力不仅仅局限于加密货币领域,而是可以应用于各种领域,如金融、供应链管理、医疗健康、物联网等。2015年以太坊项目推出了智能合约功能,使得区块链技术更加多样化和灵活化。
区块链技术的核心特点包括去中心化、安全性、透明性和不可篡改性,它可以实现信息的安全传输和存储,确保数据不被篡改和删除。通过区块链技术,参与者可以建立信任,实现价值交换,并实现去中心化的自治组织。
随着区块链技术的不断发展和应用,其在各个领域的影响越来越大,被广泛视为未来的核心技术之一。未来,随着技术的不断完善和应用场景的扩大,区块链技术将起到越来越重要的作用,推动整个社会的数字化转型。
特征
-
开放与共识:任何人都可以参与到区块链网络中,每一台设备都能作为一个节点,并获得一份完整的数据库拷贝。节点间基于一套共识机制,通过竞争计算共同维护整个区块链。即使任一节点失效,其他节点仍能正常工作,确保网络的持续性和稳定性。
-
去中心化与去信任:区块链由众多节点共同构成一个端到端的网络,不存在中心化的设备和管理机构。节点之间的数据交换通过密码技术进行验证,无需互相信任,只要按照系统既定的规则进行,节点之间不能也无法欺骗其他节点,从而增强了系统的安全性。
-
交易透明与双方匿名:区块链的运行规则是公开透明的,所有的数据信息也是公开的,因此每一笔交易都对所有节点可见。由于节点与节点之间是去信任的,身份无需公开,每个参与的节点都是匿名的,这为用户提供了更高的隐私保护。
-
不可篡改与可追溯:单个甚至多个节点对数据库的修改无法影响其他节点的数据库,除非能控制整个网络中超过51%的节点同时修改,这几乎是不可能发生的。区块链中的每一笔交易都通过密码学方法与相邻两个区块串联,因此可以追溯到任何一笔交易的前世今生,确保了信息的完整性。
区块链和密码学
区块链是比特币的底层技术,但其应用远不止于此,广泛适用于金融、供应链、医疗等多个领域。
区块链创造了一个“信任机制”,部署在点对点网络中,以密码学为基础,以共识机制为纲领,采用链式结构存放数据。每个区块都包含了前一区块(父区块)的哈希值(hash),通过“父区块哈希值”字段引用(指向)父区块,从而形成一个不可更改的链条。
区块链的特点包括:分布式(去中心化)、匿名性(无需信任)、开放性(高度透明)、加密安全性(不可篡改)。这些特性使得区块链在数据存储和传输中具有独特的优势。
区块链分类
-
公有链:无官方组织及管理机构,无中心服务器,参与的节点按照系统规则自由接入网络、不受控制,节点间基于共识机制开展工作,确保了网络的开放性和透明性。
-
私有链:建立在某企业内部,系统的运作规则根据企业要求进行设定,修改甚至是读取权限仅限于少数节点,同时仍保留着区块链的真实性和部分去中心化的特性,适合企业内部管理和数据安全。
-
联盟链:由若干机构联合发起,介于公有链和私有链之间,兼具部分去中心化的特性,适用于需要合作的行业,如金融、供应链等。
在区块链领域,公有链(Public Blockchain)、私有链(Private Blockchain)和联盟链(Consortium Blockchain)是区分区块链网络类型的三种主要形式。
-
公有链(Public Blockchain):公有链是一种开放的区块链网络,任何人都可以参与其中而不需要获得许可。公有链中的数据是公开可查的,所有交易记录都可以被浏览和验证。公有链的典型代表是比特币(Bitcoin)和以太坊(Ethereum)等开放的区块链网络。
-
私有链(Private Blockchain):私有链是一种私有的、受控的区块链网络,参与者需要获得许可才能参与其中。私有链的数据通常只有特定参与者能够查看和验证,具有更高的隐私性和控制权。通常用于组织内部或特定企业之间的数据共享和管理。
-
联盟链(Consortium Blockchain):联盟链是一种由多个组织或实体共同管理的区块链网络,在一定程度上介于公有链和私有链之间。参与者必须获得许可才能加入联盟链,但数据的控制和管理由联盟成员共同决定,具有一定的去中心化特性。联盟链常用于跨组织或行业合作、共享数据和资源。
在公有链、私有链和联盟链之间存在以下异同点:
相同点:
- 都基于区块链技术,具有去中心化、不可篡改和可追溯等特点。
- 都使用加密算法确保数据的安全性和隐私性。
- 都支持智能合约等功能的应用开发。
不同点:
- 参与者权限:公有链无需许可即可参与,私有链和联盟链需要获得许可才能加入。
- 数据可见性:公有链数据公开透明,私有链和联盟链数据可见性由参与者控制。
- 去中心化程度:公有链最为去中心化,私有链和联盟链相对中心化程度更高。
- 安全性和可信度:公有链由全网节点共同验证,私有链和联盟链受限于特定的验证节点或参与者。
选择使用公有链、私有链或联盟链取决于具体的应用场景和需求,各种链的形式都有其适用的优势和局限。在实际应用中,通常会根据需求进行选择和部署。
去中心化、安全、高效——不可能三角
区块链技术所追求的目标常常被形容为一个不可能三角,即在去中心化、安全性和高效性之间进行权衡。理想的区块链系统希望能够同时具备这三者,但实际应用中往往需要根据具体场景进行取舍。
-
去中心化:去中心化能够减轻单点故障的风险,提高系统的鲁棒性。但是,去中心化通常导致事务处理速度的降低,因为更多的节点参与共识过程,增加了网络的计算和通信开销。
-
安全性:在区块链中,安全性主要通过密码学(如哈希函数和数字签名)和共识机制来实现。高安全性的系统能有效防止数据篡改和欺诈行为,但往往需要更复杂的算法和机制,从而影响处理效率。
-
高效性:高效性的区块链系统能够快速处理大量交易,保证用户体验。然而,过于追求高效性可能会牺牲去中心化和安全性,导致对中心化机构的依赖或安全漏洞的产生。
这些因素之间的平衡是设计区块链应用时必须考虑的重要课题。
发展
区块链技术的发展经历了几个重要阶段:
-
区块链1.0:以比特币为代表,主要聚焦于虚拟货币的应用。其核心功能是去中心化的数字货币交易,提供了基本的支付手段,但其应用场景有限,无法广泛普及到其他行业。
-
区块链2.0:引入了智能合约的概念,标志着区块链应用的进一步拓展。以以太坊为代表,区块链2.0不仅支持数字货币交易,还允许开发者构建去中心化应用(DApps),实现更加复杂的逻辑和场景。
-
区块链3.0:覆盖金融行业之外的多种应用场景,能够满足更复杂的商业逻辑和需求。区块链3.0被认为是继互联网技术之后的新一代技术创新,能够推动更为广泛的产业改革和效率提升。
2008年,区块链(blockchain)的概念首次被提出,这一技术的成熟将数据分区块存储,每一块包含一定的内容,并记录前一区块的ID(唯一标识),按时间顺序形成链状结构,最终以密码学方式保证数据不可篡改,因此被称为区块链技术。
区块链相关编码
在区块链技术中,编码方式的选择至关重要,它直接影响数据的存储效率、可读性和安全性。常用的编码方式包括BASE64、BASE58和BASE58CHECK,它们各自具有不同的特点和应用场景。
BASE64 编码
简介
BASE64是一种用64个字符来表示任意二进制数据的编码方式。这64个字符包括大小写字母(A-Z, a-z)、数字(0-9)以及两个特殊字符(通常是"+"和"/")。BASE64编码常用于在HTTP环境下传递较长的标识信息,以及在MIME协议中编码二进制数据。
原理
BASE64编码将3个字节(24位)的数据分成4组,每组6位,然后将这6位数据转换为BASE64字符集中的一个字符。如果原始数据长度不是3的倍数,则在编码后会用"="字符进行填充。
特点
- 优点:
- 简单易实现。
- 通用性强,几乎所有编程语言都支持。
- 缺点:
- 编码后的数据长度会增加约33%。
- 不适合人眼直接阅读。
应用
在区块链中,BASE64常用于编码一些不需要用户直接阅读的二进制数据,例如:
- 加密后的数据。
- 某些元数据信息。
示例
假设我们有字符串 "Hello",其ASCII码为 72 101 108 108 111
,对应的二进制为 01001000 01100101 01101100 01101100 01101111
。
-
分组: 将二进制数据分组,每6位一组:
010010 000110 010101 101100 011011 000110 1111
-
转换: 将每组6位二进制数转换为十进制数:
18 6 21 44 27 6 63
-
查表: 根据BASE64编码表,将十进制数转换为对应的BASE64字符:
S G V s b G /
所以,"Hello" 的BASE64编码结果为 SGVsbG8/
。
BASE58 编码
简介
BASE58是一种基于文本的二进制编码格式,用于表示比特币和其他加密货币的地址。它与BASE64类似,但为了避免混淆,移除了一些容易看错的字符,例如数字"0"、大写字母"O"、大写字母"I"和小写字母"l",以及"+"和"/"。因此,BASE58字符集只包含58个字符:数字1-9和大小写字母(不含上述移除的字符)。
原理
BASE58编码是将数字数据转换为BASE58字符集中的字符。其编码过程类似于将十进制数转换为其他进制数,只不过这里是转换为58进制。
特点
- 优点:
- 避免了BASE64中容易混淆的字符,方便人工识别和抄录。
- 编码后的数据长度略大于BASE64,但可读性更好。
- 缺点:
- 编码效率略低于BASE64。
- 不如BASE64通用。
应用
BASE58主要应用于区块链领域,用于编码比特币地址、私钥等信息。
示例
比特币地址的编码过程:
- 添加版本前缀:在比特币地址前添加版本前缀,例如主网地址前缀为
0x00
。 - 计算校验和:对添加前缀后的数据进行两次SHA-256哈希运算,取结果的前4个字节作为校验和。
- 拼接:将版本前缀、地址和校验和拼接在一起。
- BASE58编码:对拼接后的数据进行BASE58编码,得到最终的比特币地址。
BASE58CHECK 编码
简介
BASE58CHECK是在BASE58编码的基础上增加校验和的一种编码方式,用于提高数据的可靠性。通过在原始数据后添加校验和,可以检测数据在传输过程中是否发生错误。
原理
BASE58CHECK编码的原理如下:
- 计算校验和:对原始数据进行两次SHA-256哈希运算,取结果的前4个字节作为校验和。
- 拼接:将原始数据和校验和拼接在一起。
- BASE58编码:对拼接后的数据进行BASE58编码。
特点
- 优点:
- 提高了数据的可靠性,可以检测数据传输过程中的错误。
- 具有BASE58编码的优点,方便人工识别和抄录。
- 缺点:
- 编码过程略复杂。
- 编码后的数据长度略有增加。
应用
BASE58CHECK广泛应用于比特币和其他加密货币中,用于编码地址、私钥等重要信息。通过校验和,可以有效防止因数据错误导致的资金损失。
示例
- 原始数据:假设原始数据为一段16进制字符串。
- 计算校验和:对原始数据进行两次SHA-256运算,取前4字节作为校验和。
- 拼接:将原始数据和校验和拼接。
- BASE58编码:对拼接后的结果进行BASE58编码。
在解码时,需要先进行BASE58解码,然后计算数据的校验和,并与解码后的校验和进行比较,如果一致则说明数据没有错误。
总而言之,BASE64、BASE58和BASE58CHECK是区块链技术中常用的编码方式。BASE64适用于编码二进制数据,BASE58适用于编码需要人工识别的数据,而BASE58CHECK则在BASE58的基础上增加了校验和,提高了数据的可靠性。选择合适的编码方式可以有效地提高数据的存储效率、可读性和安全性。
公钥密码学
公钥密码学是现代密码学的基础,它解决了传统对称密码学中的密钥分发难题,为网络通信和数据安全提供了强大的保障。在区块链技术中,公钥密码学被广泛应用于身份验证、数字签名和数据加密等领域。
密码学发展背景
古典密码学
古典密码学是密码学的早期阶段,主要包括替换密码和置换密码。
- 替换密码:将明文中的字符替换为其他字符。例如,凯撒密码就是一种简单的替换密码,将字母表中的每个字母向后移动固定位数。
- 置换密码:重新排列明文中的字符顺序。例如,栅栏密码就是一种简单的置换密码,将明文按照一定的规则进行排列和组合。
古典密码学的缺点是易于破解,安全性较低,无法满足现代信息安全的需求。
对称密码学
对称密码学使用相同的密钥进行加密和解密。常见的对称加密算法包括DES、AES等。
- 优点:加密速度快,适合处理大量数据。
- 缺点:密钥分发困难,通信双方必须事先共享密钥,一旦密钥泄露,所有加密数据都会被破解。
公钥密码学
公钥密码学使用一对密钥,即公钥和私钥。公钥可以公开给任何人,用于加密数据或验证签名;私钥必须由用户自己保管,用于解密数据或生成签名。
- 优点:解决了密钥分发难题,提高了安全性。
- 缺点:加密速度较慢,不适合处理大量数据。
公钥密码学简介
基本概念
- 公钥:用于加密数据或验证签名,可以公开给任何人。
- 私钥:用于解密数据或生成签名,必须由用户自己保管。
- 加密:使用公钥将明文转换为密文,只有持有对应私钥的人才能解密。
- 解密:使用私钥将密文还原为明文。
- 签名:使用私钥对数据进行签名,任何人都可以使用对应的公钥验证签名的有效性。
- 验证:使用公钥验证签名的有效性,确认数据是否被篡改以及签名者的身份。
工作原理
公钥密码学的核心思想是利用数学上的单向函数,即正向计算容易,反向计算困难的函数。例如,大数分解和离散对数问题都是典型的单向函数。
- 密钥生成:用户生成一对密钥,即公钥和私钥。
- 加密:发送者使用接收者的公钥对消息进行加密,生成密文。
- 解密:接收者使用自己的私钥对密文进行解密,还原为原始消息。
- 签名:发送者使用自己的私钥对消息进行签名,生成数字签名。
- 验证:接收者使用发送者的公钥对数字签名进行验证,确认消息的完整性和发送者的身份。
常见公钥加密算法
- RSA:基于大数分解难题,是最早也是最广泛使用的公钥加密算法。
- ECC:基于椭圆曲线离散对数难题,具有更高的安全性,密钥长度较短,适合移动设备和嵌入式系统。
- DSA:基于离散对数难题,主要用于数字签名。
椭圆曲线密码学 (ECC)
简介
椭圆曲线密码学(Elliptic Curve Cryptography,ECC)是一种基于椭圆曲线数学的公钥密码学算法。与传统的RSA算法相比,ECC具有更高的安全性,可以使用较短的密钥长度达到相同的安全强度。这使得ECC在资源受限的环境中(如移动设备、智能卡等)更具优势。
原理
椭圆曲线密码学基于椭圆曲线上的离散对数问题(Elliptic Curve Discrete Logarithm Problem,ECDLP)。在椭圆曲线上进行点乘运算容易,但反向求解离散对数非常困难。
- 椭圆曲线: 椭圆曲线通常由方程 定义,其中 。
- 点加运算: 在椭圆曲线上定义了一种特殊的加法运算,用于计算曲线上的点。
- 点乘运算: 点乘运算是点加运算的重复应用,即 ,其中 是整数, 是椭圆曲线上的一个点。
- 密钥生成:
- 选择一条椭圆曲线和一个基点 。
- 生成一个私钥 (一个随机整数)。
- 计算公钥 。
特点
- 高安全性: 相比RSA,ECC可以使用较短的密钥长度达到相同的安全强度。例如,256位的ECC密钥相当于3072位的RSA密钥。
- 高效性: ECC的加密和解密速度较快,适合移动设备和嵌入式系统。
- 广泛应用: ECC被广泛应用于数字签名、密钥交换和加密等领域。
应用
- 数字签名:椭圆曲线数字签名算法(ECDSA)是比特币和其他加密货币中常用的数字签名算法。
- 密钥交换:椭圆曲线Diffie-Hellman(ECDH)协议用于在通信双方之间安全地交换密钥。
- 加密:椭圆曲线集成加密方案(ECIES)是一种基于ECC的加密方案,提供机密性和完整性保护。
数字签名
简介
数字签名是一种用于验证数据完整性和发送者身份的密码学技术。数字签名类似于传统的手写签名,但具有更高的安全性和可靠性。
原理
数字签名基于公钥密码学,使用私钥对数据进行签名,使用公钥验证签名的有效性。
-
签名过程:
- 哈希:使用哈希函数对原始数据进行哈希运算,生成固定长度的哈希值。
- 签名:使用私钥对哈希值进行加密,生成数字签名。
-
验证过程:
- 哈希:使用相同的哈希函数对接收到的原始数据进行哈希运算,生成哈希值。
- 解密:使用发送者的公钥对数字签名进行解密,得到哈希值。
- 比较:比较计算出的哈希值和解密得到的哈希值,如果一致则说明签名有效,数据未被篡改。
特点
- 唯一性:只有持有私钥的人才能生成有效的签名。
- 不可伪造性:签名无法被伪造,因为只有私钥持有者才能生成有效的签名。
- 不可抵赖性:签名者无法否认自己对数据进行了签名。
- 完整性:可以验证数据是否被篡改,保证数据的完整性。
数字签名算法
- RSA签名:基于RSA算法的数字签名方案。
- DSA:数字签名算法,是美国国家标准。
- ECDSA:椭圆曲线数字签名算法,是比特币和其他加密货币中常用的数字签名算法。
应用
- 身份验证:验证用户的身份,确保只有授权用户才能访问系统资源。
- 数据完整性:验证数据是否被篡改,保证数据的完整性。
- 交易确认:在区块链中,数字签名用于确认交易的有效性,防止双重支付等攻击。
- 合同签署:用于电子合同的签署,确保合同的法律效力。
总结: 公钥密码学及其相关的椭圆曲线密码学和数字签名技术是现代信息安全的重要组成部分。通过使用公钥和私钥进行加密、解密、签名和验证,可以实现安全的通信和数据保护。这些技术在区块链领域中得到了广泛应用,为区块链系统的安全性和可靠性提供了坚实的基础。
哈希函数
哈希函数是计算机科学中一种非常重要的工具,它将任意长度的输入数据(也称为“消息”)转换为固定长度的输出,这个输出被称为哈希值(或“摘要”)。哈希函数在数据完整性校验、密码学、数据索引等领域都有广泛的应用。
概述
定义
哈希函数(Hash Function)是一种将任意长度的数据映射到固定长度输出的函数。它的作用是将输入数据进行压缩,生成一段较短的、能够代表原始数据的摘要信息。
工作原理
哈希函数通过一系列复杂的数学运算,将输入数据转换为哈希值。理想的哈希函数应该满足以下条件:
- 确定性:相同的输入始终产生相同的输出。
- 快速性:计算哈希值的速度要快。
- 均匀性:输出的哈希值应均匀分布,避免冲突。
示例
例如,一个简单的哈希函数可以将字符串中每个字符的ASCII码相加,然后对一个固定值取模。虽然这个例子很简单,但它展示了哈希函数的基本思想:将任意长度的输入转换为固定长度的输出。
特性
一个好的哈希函数应具备以下关键特性:
-
确定性 (Deterministic):
- 对于相同的输入,哈希函数必须始终产生相同的输出。这是哈希函数最基本的要求。
- 如果相同的输入产生了不同的输出,那么哈希函数的可靠性将大大降低。
-
快速性 (Efficient):
- 哈希函数计算哈希值的速度要快,能够在合理的时间内处理大量数据。
- 在实际应用中,哈希函数的效率直接影响系统的性能。
-
均匀性 (Uniformity):
- 哈希函数的输出应均匀分布,避免出现大量的冲突(即不同的输入产生相同的输出)。
- 均匀性有助于提高哈希表的性能,并减少碰撞攻击的风险。
-
单向性 (One-way):
- 也称为不可逆性,即从哈希值很难(计算上不可行)反推出原始输入。
- 单向性是哈希函数在密码学应用中的重要特性,用于保护数据的安全性。
-
抗碰撞性 (Collision Resistance):
- 弱抗碰撞性:给定一个输入 ,找到另一个输入 ,使得 是困难的。
- 强抗碰撞性:找到任意两个不同的输入 和 ,使得 是困难的。
- 抗碰撞性是保证哈希函数安全性的重要特性,防止攻击者通过构造碰撞来破坏系统的安全性。
-
雪崩效应 (Avalanche Effect):
- 原始输入发生微小变化时,哈希值应发生显著变化。
- 雪崩效应可以增加哈希函数的安全性,使得攻击者难以通过分析输入和输出之间的关系来破解哈希函数。
安全哈希函数
安全哈希函数(Cryptographic Hash Function)是一种特殊的哈希函数,除了具备普通哈希函数的特性外,还必须满足更高的安全要求,以保证在密码学应用中的安全性。
特点
-
单向性 (One-way):
- 从哈希值反推出原始输入在计算上是不可行的。这意味着攻击者无法通过哈希值来还原原始数据。
-
强抗碰撞性 (Strong Collision Resistance):
- 找到任意两个不同的输入 和 ,使得 在计算上是不可行的。
- 强抗碰撞性保证了攻击者无法通过构造碰撞来伪造数据或破坏系统的安全性。
-
抗原像攻击 (Preimage Resistance):
- 给定一个哈希值 ,找到一个输入 ,使得 在计算上是不可行的。
- 抗原像攻击保证了攻击者无法通过已知的哈希值来找到对应的原始输入。
-
抗第二原像攻击 (Second Preimage Resistance):
- 给定一个输入 ,找到另一个不同的输入 ,使得 在计算上是不可行的。
- 抗第二原像攻击保证了攻击者无法在已知一个输入的情况下,找到另一个与其具有相同哈希值的输入。
常见的安全哈希函数
-
MD5 (Message Digest Algorithm 5):
- 曾被广泛使用,但由于安全性问题(已发现碰撞),现在已不推荐在安全要求高的场景中使用。
- 输出哈希值为128位。
-
SHA-1 (Secure Hash Algorithm 1):
- 类似于MD5,也存在安全性问题(已发现碰撞),不推荐使用。
- 输出哈希值为160位。
-
SHA-256 (Secure Hash Algorithm 256-bit):
- SHA-256是SHA-2系列中的一种,输出哈希值为256位。
- 目前被广泛应用于密码学和区块链技术中,如比特币。
-
SHA-512 (Secure Hash Algorithm 512-bit):
- SHA-512是SHA-2系列中的另一种,输出哈希值为512位。
- 提供更高的安全性,适用于对安全性要求更高的场景。
-
SHA-3 (Secure Hash Algorithm 3):
- SHA-3不是SHA-2的扩展,而是一种全新的哈希算法,通过Keccak算法实现。
- 提供与SHA-2不同的设计思路,增加了哈希算法的多样性。
-
RIPEMD-160 (RACE Integrity Primitives Evaluation Message Digest):
- RIPEMD-160是一种160位的哈希函数,由欧洲的RACE计划开发。
- 在一些加密货币和安全协议中使用。
应用
安全哈希函数在以下领域有广泛的应用:
-
数据完整性校验:
- 通过比较数据的哈希值,可以验证数据在传输过程中是否被篡改。
-
密码存储:
- 存储用户密码时,通常只存储密码的哈希值,而不是明文密码,以提高安全性。
-
数字签名:
- 在数字签名过程中,首先对数据进行哈希运算,然后对哈希值进行签名,以提高签名效率和安全性。
-
区块链技术:
- 在区块链中,哈希函数用于构建区块之间的链接,保证区块链的完整性和不可篡改性。
总而言之,哈希函数是一种将任意长度的输入数据转换为固定长度输出的函数。安全哈希函数在普通哈希函数的基础上,增加了单向性、抗碰撞性等安全特性,保证了在密码学应用中的安全性。选择合适的哈希函数对于保护数据的安全至关重要。
哈希指针与默克尔树
哈希指针和默克尔树是区块链技术中非常重要的数据结构,它们在保证数据的完整性和安全性方面起着关键作用。哈希指针是一种带有哈希值的指针,而默克尔树则是一种树状数据结构,用于高效地验证大规模数据的完整性。
哈希指针
简介
哈希指针(Hash Pointer)是一种带有哈希值的指针。传统的指针仅包含指向数据存储位置的地址,而哈希指针不仅包含地址,还包含所指向数据的哈希值。
工作原理
哈希指针的工作原理非常简单:
- 数据存储:
- 将数据存储在某个位置。
- 计算哈希值:
- 使用哈希函数对数据进行哈希运算,生成哈希值。
- 创建哈希指针:
- 创建一个哈希指针,其中包含数据的地址和哈希值。
特点
哈希指针的主要特点是能够验证数据的完整性。通过比较哈希指针中存储的哈希值与实际数据的哈希值,可以判断数据是否被篡改。
- 数据完整性验证:
- 哈希指针可以用于验证数据的完整性。如果数据被篡改,那么重新计算的哈希值将与哈希指针中存储的哈希值不一致,从而检测到数据的篡改。
- 防止篡改:
- 由于哈希指针包含了数据的哈希值,任何对数据的篡改都会导致哈希值发生变化,从而被检测出来。
- 构建链式数据结构:
- 哈希指针可以用于构建链式数据结构,如区块链。在区块链中,每个区块都包含前一个区块的哈希指针,从而形成一个不可篡改的链条。
应用
哈希指针在许多安全应用中都有重要的作用:
- 区块链:
- 在区块链中,哈希指针用于构建区块之间的链接,保证区块链的完整性和不可篡改性。每个区块都包含前一个区块的哈希指针,从而形成一个链式结构。
- 版本控制系统:
- 哈希指针可以用于版本控制系统,以验证文件的完整性和历史版本的一致性。
- 分布式存储系统:
- 在分布式存储系统中,哈希指针可以用于验证存储数据的完整性,防止数据被篡改或损坏。
默克尔树 (Merkle Tree)
简介
默克尔树(Merkle Tree),又称哈希树,是一种树状数据结构,用于高效地验证大规模数据的完整性。默克尔树由叶子节点、中间节点和根节点组成。
工作原理
默克尔树的构建过程如下:
- 叶子节点:
- 每个叶子节点包含数据的哈希值。如果数据块本身存储在叶子节点中,则叶子节点的值为其哈希值。
- 中间节点:
- 每个中间节点包含其子节点哈希值的哈希值。也就是说,中间节点的值是其所有子节点哈希值的哈希值。
- 根节点:
- 根节点(也称为默克尔根)包含整个默克尔树的哈希值。根节点的值是其所有子节点哈希值的哈希值。
特点
默克尔树的主要特点是能够高效地验证大规模数据的完整性。通过验证默克尔根的哈希值,可以确定整个数据集是否被篡改。
- 高效的数据完整性验证:
- 只需要验证默克尔根的哈希值,就可以确定整个数据集是否被篡改。这比验证每个数据块的哈希值要高效得多。
- 局部数据验证:
- 可以验证数据集中的任何一个数据块的完整性,而不需要下载整个数据集。只需要下载与该数据块相关的哈希值路径即可。
- 适用于大规模数据:
- 默克尔树适用于大规模数据的完整性验证,可以在分布式存储系统和区块链等应用中高效地验证数据的完整性。
示例
假设有四个数据块:Data1, Data2, Data3, Data4。构建默克尔树的过程如下:
- 计算叶子节点的哈希值:
- Hash1 = Hash(Data1)
- Hash2 = Hash(Data2)
- Hash3 = Hash(Data3)
- Hash4 = Hash(Data4)
- 计算中间节点的哈希值:
- Hash12 = Hash(Hash1 + Hash2)
- Hash34 = Hash(Hash3 + Hash4)
- 计算根节点的哈希值:
- MerkleRoot = Hash(Hash12 + Hash34)
应用
默克尔树在许多领域都有广泛的应用:
- 区块链:
- 在比特币和其他加密货币中,默克尔树用于汇总区块中的交易信息,并生成默克尔根。默克尔根存储在区块头中,用于验证区块中交易的完整性。
- 分布式存储系统:
- 在分布式存储系统中,默克尔树可以用于验证存储数据的完整性,防止数据被篡改或损坏。
- 版本控制系统:
- 默克尔树可以用于版本控制系统,以验证文件的完整性和历史版本的一致性。
- 数据同步:
- 在数据同步过程中,默克尔树可以用于快速比较两个数据集的差异,只同步发生变化的数据块。
总之,哈希指针和默克尔树是区块链技术中非常重要的数据结构。哈希指针通过包含数据的哈希值来验证数据的完整性,而默克尔树则通过树状结构高效地验证大规模数据的完整性。这些技术在保证数据的安全性和可靠性方面起着关键作用,并在许多其他领域都有广泛的应用。
区块链与AIGC
想象一下:一个人从小学开始的每一年的成绩单、每一个学校的毕业证都可以在区块链上查到。这样的系统不仅增强了教育记录的透明度和可信度,还便于雇主或其它相关方进行审核。
工作状况、婚姻状况、保险状况一一呈现在区块链之上。医生的每一次诊断记录都将以不可修改的方式写入区块,确保医疗数据的准确性和可追溯性。
数字仲裁可以直接从区块链上扣款,公益捐款的每一笔收入和用途都记录在案。这一切都将促进社会信用体系的建立,使人们在数字社会中享有更加透明与可信的互动关系。
未来已来,虽然区块链技术尚未全面流行,但它的发展潜力巨大。我们每一个人都应积极跟进这一波技术潮流,努力推动其在各行各业的应用与落地,实现社会的数字化转型。
国有技术的不断进步与创新,势必将为我们的生活带来巨大的改变。为了迎接区块链时代的到来,我们应保持学习与适应的态度,与时俱进。
区块链与AIGC(人工智能生成内容)的结合在数字版权、数字资产生成、确权和交易等领域展现出巨大的潜力。通过区块链技术,AIGC平台能够为数字内容提供更高效、透明和安全的管理方式,推动数字经济的进一步发展。
AIGC+区块链相关的数字版权
什么是AIGC+区块链数字版权?
AIGC+区块链数字版权是指利用区块链技术对人工智能生成的内容进行版权管理和保护。区块链的分布式账本和不可篡改性使其成为数字版权管理的理想工具。通过区块链,数字内容可以被确权、记录和管理,从而保护创作者和版权所有者的权益,防止盗版和侵权行为。
如何界定AIGC+区块链数字版权?
在AIGC+区块链数字版权平台上,用户可以将作品上传至区块链网络,并生成唯一的数字指纹(如哈希值)。这一数字指纹作为作品的唯一身份标识,确保其版权归属和完整性。区块链记录则用于存储版权信息,确保其不可篡改和永久保存。
具体界定方式包括:
- 数字指纹生成:通过加密算法为作品生成唯一标识。
- 区块链记录:将版权信息和数字指纹记录在区块链上。
- 智能合约管理:通过智能合约自动执行版权管理规则,如授权、转让等。
通过这种方式,AIGC+区块链数字版权平台能够为创作者提供透明、高效和安全的版权保护机制。
AIGC+区块链相关的数字资产生成
什么是AIGC+区块链数字资产生成?
AIGC+区块链数字资产生成是指利用区块链技术将实体资产或权益转化为数字化形式,并在区块链网络上进行记录和管理。这些数字资产可以代表艺术品、不动产、收藏品、知识产权等多种实体资产。
数字资产生成的内容和流程
常见数字资产类型:
- 艺术品数字化资产:将艺术品的所有权和版权信息数字化,便于确权和交易。
- 不动产数字化资产:将房地产、土地等不动产的权益信息转化为数字形式。
- 收藏品数字化资产:将珍稀收藏品、文物的所有权信息数字化,便于鉴定和交易。
- 知识产权数字化资产:将专利、商标、著作权等知识产权转化为数字化资产。
生成流程:
- 资产登记:将实体资产的信息进行数字化处理,包括所有权、权益描述等。
- 资产标识:为数字资产生成唯一标识符(如哈希值)。
- 上链记录:将资产信息和标识符记录在区块链上,确保其安全性和不可篡改性。
- 确权和管理:通过智能合约实现资产确权、转让和交易流程的自动化管理。
通过AIGC+区块链技术,数字资产的生成和管理变得更加高效、透明和安全,为资产所有者提供了全新的数字化管理方式。
AIGC+区块链相关的确权和交易
什么是AIGC+区块链确权和交易?
AIGC+区块链确权和交易是指利用区块链技术对数字资产的所有权进行确认,并在区块链网络上进行交易的过程。区块链的透明性和不可篡改性确保了确权和交易的安全性和可信度。
确权和交易的流程
确权流程:
- 资产登记和标识化:将数字资产信息登记并生成唯一标识符。
- 区块链记录:将资产信息和标识符记录在区块链上。
- 智能合约确权:通过智能合约将资产所有权与特定用户或实体绑定,确保其唯一性和不可篡改性。
交易流程:
- 资产挂牌:资产所有者将数字资产放置在区块链网络上并标明交易意愿。
- 交易发起:交易参与者通过区块链网络查询资产信息并发起交易请求。
- 交易执行:交易双方使用数字货币或其他资产完成交易,智能合约自动执行所有权转移。
- 交易记录:交易完成后,记录被永久保存在区块链上,可供验证和查询。
确权和交易的作用
- 保护资产所有权:确保数字资产的合法性和真实性,防止侵权和盗版。
- 提升交易透明度:降低交易成本和风险,增强交易双方的信任。
- 提高资产流动性:促进数字资产的交易和流通,提升其市场价值。
- 简化交易流程:通过智能合约实现自动化交易,减少中介机构的参与。
AIGC+区块链的确权和交易为数字资产的管理和交易提供了安全、高效和便捷的解决方案,为数字经济的发展奠定了重要基础。
比特币
何为比特币?
比特币(Bitcoin)是世界上首个去中心化的数字货币,也被称为加密货币。它是一种基于密码学原理构建的货币形式,不依赖于任何中央机构或政府,而是由用户和矿工组成的全球性点对点网络运作。
基本特点
- 去中心化:比特币不受中央银行或政府的控制,交易记录在区块链上,每个参与者都可以拥有一份完整的交易记录副本。
- 匿名性:虽然比特币交易是公开的,但交易参与者的身份信息是匿名的,仅通过地址进行识别。
- 有限供应:比特币的总供应量被设定为2100万个,通过复杂的算法逐渐减少以控制通胀。
技术基础
- 区块链:比特币的交易记录在区块链上,这是一种不断增长的数据链,每个区块包含一批交易信息并通过密码学链接。
- 加密算法:比特币使用SHA-256哈希函数来确保数据的完整性和安全性。
- 数字签名:使用椭圆曲线数字签名算法(ECDSA)来验证交易的合法性。
比特币“从何而来?
比特币的诞生源于2008年全球金融危机之后,人们对传统金融体系的不信任。中本聪(Satoshi Nakamoto),比特币的创始人,在2008年10月31日发布了比特币白皮书,提出了一个新的货币系统概念。
发行机制
- 挖矿:比特币通过一个称为“挖矿”的过程产生。矿工通过解决复杂的数学问题(工作量证明),竞争获取记账权,从而获得新产生的比特币作为奖励。
- 交易记录:每笔比特币交易都会被记录在区块链上,确保交易的透明度和不可篡改性。
比特币的特点
- 无需中介:比特币交易不需要银行或金融机构的参与,减少了交易费用和时间。
- 全球流通:比特币可以在任何地方流通,只要有网络连接,无国界限制。
运行机制
区块链
区块链是比特币的核心技术,每个区块包含一批交易信息,通过哈希链接在一起。区块的哈希值是区块唯一标识。
去中心化电子记账系统
问题:
-
账单以谁的为准? 当A和B同时交易时,如何确定交易顺序?
- 解决方案:通过工作量证明机制(PoW),矿工竞赛决定记账顺序。
-
为什么要记账? 为什么要在自己电脑上记录别人的交易?
- 激励机制:记账有奖励:
- 手续费:交易者支付一定的手续费给记录交易的矿工。
- 打包奖励:每10分钟打一个包,获得50个比特币的奖励,每4年减半。
- 激励机制:记账有奖励:
数学计算:
50∗6∗24∗365∗4∗[1+1/2+(1/2)^2+…]=2100万
因此,运行区块链只需要准备2100万的打包奖励费用。
工作量证明——挖矿
哈希函数:
- 把任意长度的输入通过散列算法变换成固定长度(256位)的输出,常见的哈希算法是SHA256。
- 哈希计算正向容易,反向计算困难至极。
原理:
应用层
- 钱包:比特币钱包用于存储用户的私钥和地址,管理交易。
- 交易:用户可以通过钱包发送和接收比特币。
激励层
- 挖矿奖励:比特币通过奖励给创建新区块的矿工的方式产生,奖励大约每四年减半。目前每10分钟产生一个新区块,奖励12.5个比特币给矿工。
- 交易费用:所有交易都需要支付手续费给记录区块的矿工,交易费不足的交易会被矿工拒绝。
共识层
- 拜占庭将军问题:在分布式网络中如何达成共识,避免少数节点作恶。
- 工作量证明机制(Proof of Work, POW):通过竞赛解决数学难题,获取记账权。
- 挖矿:SHA256(SHA256(Version+HashPreBlock + Merkle_root + Timestamp + Bits + Nonce)) ≤ 难度目标Bits
“双花”问题
如何保证每一笔数字现金只被花掉一次,避免重复支出?
- 解决方案:区块链为每一笔交易加入时间戳,使用UTXO模型。
51%攻击
51%攻击并不能修改数据,但可以进行“双花”攻击。
为什么区块10分钟发布一次
- 原因:区块间隔时间短会导致交易少、浪费大、网络延迟影响大,容易分叉。
如何保证区块发布时间保持在10分钟
- 难度调整:每完成2016个块,根据出块平均时间调整一次难度。
网络层
- P2P网络:点对点技术,没有中心服务器,依靠用户群交换信息,具有耐攻击、高容错的优点。
数据层
- 非对称加密:
- 公钥和私钥成对出现,公钥公开,私钥保密;
- 私钥加密(签名)的信息只有对应的公钥才能解密(验签);
- 公钥加密的信息只有对应的私钥才能解密。
- 公钥加密,私钥解密;
- 私钥签名,公钥验签。
智能合约
什么是智能合约?
智能合约是一种基于区块链技术的自动化协议,它能够在满足预设条件时自动执行、验证或强制执行合同条款。虽然目前尚没有统一的明确定义,但智能合约的核心特点可以概括为以下几点:
- 事件驱动:智能合约的执行由特定事件触发,例如交易的发起或特定条件的满足。
- 状态管理:智能合约能够存储和管理状态信息,例如账户余额或合同条款的履行情况。
- 多方共识:智能合约运行在区块链上,所有参与方都认可其规则和执行结果。
- 自动化执行:智能合约通过预设的算法自动处理资产和交易,减少人为干预和仲裁的需求。
智能合约的优势在于利用程序算法替代传统合同中的仲裁和执行过程,从而提高效率、降低成本,并减少人为错误和欺诈的可能性。
智能合约(Smart Contract)是一种基于区块链技术的自动化合约,通过编程代码执行合同条款和交易条件。智能合约充当了合同的执行者,在满足特定条件时自动执行预设的操作,无需第三方中介的干预,确保了合同的自动化执行和可靠性。
智能合约具有以下几个重要特点:
-
自动化执行:智能合约是以编程代码的形式编写而成,当特定条件满足时,合约会自动执行预定义的操作。这种自动化的执行方式大大简化了合同的执行流程,提高了效率。
-
不可篡改性:一旦智能合约部署在区块链上,就无法篡改或修改,确保了合同条款的不可更改性,防止恶意操纵和第三方干预。
-
透明性和信任:智能合约的所有代码和操作都被记录在区块链上,所有参与者都可以查看和验证。这种透明性增加了合同的信任度,并减少了争议的可能性。
-
高安全性:智能合约采用密码学技术确保交易和数据的安全性,不易受到黑客攻击。只有满足特定条件的交易才能触发智能合约的执行。
-
去中心化:智能合约在区块链上执行,无需中间人或第三方干预,实现了去中心化的交易和合约执行模式。
智能合约在各个领域具有广泛应用,包括金融、供应链管理、投资、保险、不动产等。例如,可以用智能合约实现无需第三方的去中心化交易、自动化的供应链管理、投资基金的管理和分配等。以太坊是一个支持智能合约的区块链平台,开发者可以在其上编写和部署智能合约,推动区块链技术在各个行业的应用和创新。
区块链2.0 VS 区块链1.0
特性 | 区块链2.0 | 区块链1.0 |
---|---|---|
图灵完备性 | 支持图灵完备的编程语言,能够执行复杂逻辑 | 非图灵完备,只能执行有限类型的指令 |
智能合约 | 支持智能合约,实现自动化合同执行 | 不支持智能合约,仅支持简单交易 |
应用定位 | 定位于平台,支持多种应用开发 | 定位于特定应用,如支付网络 |
区块链问题
尽管区块链技术具有许多优势,但它也面临一些挑战:
- 效率低和确认时间长:区块链网络的交易处理速度较慢,尤其是在比特币网络中,确认时间可能长达数分钟甚至数小时。
- 高耗能问题:工作量证明(PoW)机制需要大量的计算资源,导致能源消耗巨大。
- 技术性能问题:区块链的可扩展性和吞吐量有限,难以应对大规模应用需求。
- 安全性问题:虽然区块链本身具有较高的安全性,但智能合约和外部接口可能存在漏洞,导致攻击风险。
例如,在单机环境下,实现功能是主要目标,而在分布式环境下,如何解决一致性问题成为关键。区块链技术通过分布式账本和共识机制解决了这一问题,而智能合约则是实现这一目标的核心工具。
智能合约语言
比特币脚本语言是一种简单的智能合约语言,它实现了安全的数字货币计算模型,确保在无需第三方的情况下进行价值转移。然而,现实世界的需求非常复杂,比特币脚本语言无法满足多样化的应用场景。因此,以太坊等区块链平台引入了图灵完备的智能合约语言,如Solidity。
图灵完备的语言能够实现所有可计算的功能,支持循环和复杂逻辑。为了避免程序陷入死循环,以太坊引入了“gas”机制,每个操作都会消耗一定的gas,从而限制程序的执行时间和资源消耗。
共识机制
区块链2.0支持多种无资源消耗的共识机制,如POS(权益证明)、DPOS(委托权益证明)和PBFT(实用拜占庭容错)。这些机制具有以下优点:
- 节省资源:不需要大量算力挖矿,减少能源消耗。
- 提高交易速度:以太坊采用POS共识机制后,交易确认时间缩短至秒级。
然而,这些共识机制也存在一些缺点:
- PBFT:无法防范女巫攻击,不适合公有链,但性能较好。
- POS和DPOS:需要代币参与,限制了其在行业应用中的适用性。
PoS VS PoW
以太坊的PoS(Proof of Stake)共识机制和比特币的PoW(Proof of Work)共识机制是两种不同的区块链共识机制,它们分别采用不同的方式来确保区块链网络的安全性和可靠性。
-
以太坊的PoS(Proof of Stake)共识机制: 以太坊正在计划过渡至PoS共识机制,以提高网络的可扩展性和效率。在PoS共识机制下,区块链网络的确认和验证工作由具有一定数量代币的持有者来完成,持有代币的数量越多,参与确认和验证的机会就越大。持有代币的节点(称为验证者)根据其持有的代币数量被选中来创建新的区块,并获得相应的奖励。PoS共识机制不需要繁重的计算工作,因此能够降低能源消耗,并提高网络的效率。
-
比特币的PoW(Proof of Work)共识机制: 比特币使用的是PoW共识机制,即通过解决复杂的密码学难题(称为工作量证明)来确认并创建新的区块。矿工利用计算力来解决难题,第一个解出难题的矿工将获得相应的奖励。PoW共识机制需要消耗大量的能源和算力,来确保网络的安全性和不可篡改性。
异同点:
-
异同之一是PoS共识机制更加节能高效,因为不需要进行大量的计算来解决难题,而PoW共识机制消耗大量能源。这是由于PoS是根据验证者持有的代币数量来进行验证,而PoW是通过大量计算来争夺记账权。
-
另一个不同之处是PoS共识机制可以提高网络的可扩展性,减少了产生分叉的可能性,而PoW共识机制可能会导致网络的分叉。
-
共同之处是两种共识机制都致力于确保网络的安全性和不可篡改性,只是实现的方式有所不同。
总的来说,以太坊的PoS和比特币的PoW共识机制各有优势和劣势,选择使用哪种共识机制取决于具体的需求和目标。两种共识机制的发展都有助于推动区块链技术在各个领域的应用和创新。
其他共识机制
除了PoW(Proof of Work)和PoS(Proof of Stake)这两种常见的共识机制,区块链技术还涌现了多种其他类型的共识机制,每种共识机制都有其独特的特点和适用场景。以下是一些其他常见的区块链共识机制:
-
Proof of Authority(PoA):PoA共识机制是一种基于身份验证的共识机制,其中网络的验证者是经过认证的特定实体或个人。验证者被授予参与网络确认和验证的权限,而不是通过计算力或代币持有来获得权益。PoA共识机制通常被用于企业或私有区块链网络中,以确保网络的安全性和可靠性。
-
Delegated Proof of Stake(DPoS):DPoS共识机制是一种代理式的权益证明机制,持有代币的用户可以通过投票选举出代表来确认和验证区块链上的交易。选举出的代表会产生新的区块,获得相应的奖励。DPoS机制可以提高效率和可扩展性,被一些公共区块链项目(如EOS)采用。
-
Proof of Burn(PoB):PoB共识机制是指参与者需要“烧毁”(即将代币发送到一个无法再次访问的地址)一定数量的代币,作为获得记账权和权益的证明。PoB机制旨在减少代币的供应量,并鼓励长期持有者参与网络维护。
-
Proof of Capacity(PoC):PoC共识机制是一种与硬盘空间相关的共识机制,参与者必须证明拥有一定数量的硬盘空间用于存储区块链数据。PoC机制被用于一些存储类区块链项目,可以降低能源消耗并提高效率。
-
Proof of Elapsed Time(PoET):PoET共识机制是由英特尔开发的一种共识算法,基于随机等待时间来选择下一个产生区块的节点。PoET通过在物理随机性上保证公平性,被广泛应用于一些企业级联盟链项目中。
这些不同类型的共识机制各有特点和适用场景,可以根据具体的需求和情况选择最合适的共识机制。区块链技术的不断发展和创新也会促进更多共识机制的出现,推动区块链在各个领域的广泛应用和实践。
女巫攻击
在区块链领域,女巫攻击(Sybil Attack)是一种恶意行为,其中攻击者通过创建大量虚假身份(也称为Sybil节点)来控制或破坏网络。这种攻击方式得名于美国精神分裂症患者Sybil Dorsett。
女巫攻击的目的通常是操纵网络中的投票过程、破坏共识机制或欺骗其他参与者。攻击者通过创建大量虚假身份,在网络中生成大量虚假交易或事件,以扭曲真实数据、干扰网络运作、窃取信息等方式达到其不良目的。
女巫攻击在去中心化的区块链系统中特别危险,因为区块链的核心设计理念是去中心化和信任,攻击者一旦控制大部分网络节点,就可以破坏系统的正常运行,篡改交易记录,甚至发动双重支付等恶意行为。
为了防范女巫攻击,区块链系统通常采取以下措施:
-
Proof of Work(PoW)和Proof of Stake(PoS)等共识机制:这些共识机制通过要求节点提供计算能力或代币权益等资源来获得记账权,从而限制了攻击者创建大量虚假身份的成本,降低了女巫攻击的风险。
-
身份验证和信任度系统:在某些区块链项目中,采用身份验证和信任度系统来确保网络中节点的身份和行为真实有效,防范女巫攻击。
-
去中心化设计和多重验证:采用去中心化的设计和多重验证机制,确保网络中没有单一实体或节点能够轻易控制整个网络,避免女巫攻击的发生。
女巫攻击是区块链领域中的一种常见攻击方式,对系统的安全性和可靠性构成严重威胁。因此,开发者和研究人员需要不断改进和加强区块链系统的安全机制,防范和应对各种潜在攻击。
智能合约与DAPP
智能合约相当于服务器后台,而DAPP(去中心化应用)则是通过前端页面与用户交互的应用。通过RPC接口,DAPP可以与智能合约进行通信,实现完整的去中心化功能。以太坊节点程序Geth在8545端口提供了JSON RPC API,支持Web3库的命令,为前端应用提供区块链数据。
常见具备2.0特性的区块链
- 以太坊:2015年正式发布,拥有丰富的用户和应用生态。
- Hyperledger:企业级区块链平台,由Linux基金会管理。
- LISK:2016年6月发布,用户和应用较少,但具有潜力。
- Fabric:由IBM主导的企业级区块链框架,支持模块化设计和隐私保护。
Defi
DeFi(Decentralized Finance)是区块链技术和智能合约技术相结合的一种金融服务模式,旨在通过去中心化的方式提供传统金融服务。DeFi在去中心化的区块链网络上运行,不依赖于传统金融机构,通过智能合约实现各种金融服务和交易活动。
智能合约在DeFi中扮演着关键的角色,通过智能合约编程实现了自动化的金融服务,如借贷、交易、投资、支付、预测市场等。DeFi使得任何人都可以参与各种金融活动,无需信任第三方中介,享受更低成本、更高效率和更易访问的金融服务。
DeFi涉及的应用和服务非常广泛,包括但不限于以下几种:
-
借贷平台:用户可以通过抵押数字资产来借款,或者提供数字资产赚取利息,实现点对点的借贷服务,如Compound、Aave等。
-
去中心化交易所(DEX):用户可以直接在去中心化交易所上进行数字资产交易,无需传统交易所的中介,如Uniswap、SushiSwap等。
-
保险平台:提供去中心化的智能合约保险服务,保障用户数字资产的安全,如Nexus Mutual等。
-
预测市场:允许用户在去中心化平台上进行各种事件的预测和交易,如Augur、Polymarket等。
-
合成资产:发行和交易抵押实物资产的数字化衍生品,如合成黄金、股票等资产。
DeFi在近年来得到了快速发展和广泛关注,为区块链技术的应用提供了重要的示范和创新模式。然而,DeFi市场仍面临着许多挑战,如智能合约漏洞、安全性风险等问题,需要加强监管和技术进步以确保DeFi的可持续发展。
NFT
NFT(Non-Fungible Token)是指非同质化代币,是一种在区块链上发行的数字资产标准,每一个NFT都具有唯一性和不可替代性。NFT利用智能合约技术,将数字资产(如艺术品、游戏道具、音频、视频、虚拟地产等)的所有权和身份信息记录在区块链上,实现了数字资产的唯一性和真实性。
与传统的加密货币(如比特币、以太币等)不同,NFT代表的是唯一的数字资产,每一个NFT都具有独特的标识信息,不可互换或替代。这使得NFT在数字艺术、虚拟世界、收藏品、游戏等领域具有广泛的应用场景。持有NFT的用户可以证明自己拥有特定的数字资产,并在区块链上追溯该资产的所有历史记录。
NFT的发展也推动了虚拟资产经济的发展,为数字内容创作者、艺术家和游戏开发者提供了新的数字化作品展示、销售和收益模式。通过NFT,他们可以将自己的作品售卖为独有的数字艺术品,实现数字资产的有效管理、交易和分发。
总的来说,NFT是智能合约技术在数字资产领域的一种重要应用形式,为数字资产的所有权和交易提供了更加透明、安全、不可篡改的解决方案,推动了数字资产领域的进一步发展和创新。
以太坊和Metamask
托管钱包和自托管钱包
托管钱包是由第三方提供和维护的数字货币钱包。用户在使用托管钱包时,将私钥交由第三方管理,而不是用户自己保存私钥。用户可以通过第三方托管服务方便地管理和交易数字货币,同时也需要信任第三方保护私钥的安全。
自托管钱包是用户自己保存和管理私钥的数字货币钱包。用户需要自行保管私钥并确保其安全性,可以选择将私钥保存在硬件钱包、纸钱包、桌面钱包或移动钱包中。自托管钱包具有更高的安全性和隐私性,用户拥有对私钥的完全控制权,并不依赖于第三方服务。
自托管钱包和托管钱包各有优缺点,用户可以根据自己的需求和风险承受能力选择适合自己的钱包形式。托管钱包适合那些对私钥管理不熟悉或不愿意自行管理私钥的用户,而自托管钱包适合那些更注重安全性和隐私性的用户。
MetaMask 是领先的自动托管钱包。 访问区块链应用程序和 web3 的安全简便方式。 深受全球数百万用户的信赖。
签名&发送交易
燃料费(gas费)介绍
Gas Limit是指每个区块中可以包含的Gas总量的上限,Gas Price是指每个Gas的价格,Wei是以太坊中的最小货币单位。
Gas是以太坊区块链中用于衡量交易和合约执行成本的单位,类似于燃料。Gas Limit是指交易或合约执行最多可以消耗的Gas数量,Gas Price是指每个Gas的价格,通过Gas Price和Gas Limit可以确定一个交易的手续费。
Wei
Wei是以太坊中的最小货币单位,用来支付以太坊网络中的各种费用,包括Gas费用。
首先我们来看一下以太币单位之间的转换,以太币的最小单位为wei,1个eth相当于10的18次方wei。通常,大家也使用Gwei作为展示单位。比较常用的就是eth,Gwei和wei。
在以太坊世界中,Wei 是一个关键概念,它是这个区块链网络中最小的以太单位。随着加密货币的普及,理解各种面值变得至关重要,以实现在以太坊生态系统内的无缝交易和互动。让我们深入探讨 Wei 和它在加密货币领域的重要性。
Wei 的名字是以密码学家 Wei Dai 命名的,它代表了以太坊网络的基本构建块。换句话说,10 亿个 Wei 相当于一个以太币。随着以太币的价值波动,Wei 充当了基本单位,促进微小交易,并使用户能够交易这种受欢迎的加密货币的最小分数。
II. Wei 的面值
在探索以太坊网络复杂的面值结构时,我们发现 Wei 可以进一步细分为更小的单位,每个单位都有独特的用途。其中包括 Kwei、Mwei、Gwei 等等。然而,Gwei 因其实用性和易用性而受到了广泛的欢迎和采用。
III. 什么是 Gwei
Gwei,即 Gigawei 的缩写,表示 10 亿个 Wei 单位。换句话说,一个 Gwei 等于十亿个 Wei。以太坊网络主要在处理燃气费时使用 Gwei 作为单位选择。对于那些不熟悉的人来说,燃气费是在以太坊区块链上执行智能合约或任何其他交易时产生的费用。
随着 2021 年以太坊费用机制的改革,用户现在在燃气费计算方面体验到了更大的透明度和可预测性。这些费用确保网络的安全性,并激励矿工验证交易并维护区块链。
IV. Wei 转换为 Gwei
在 Wei 和 Gwei 之间进行转换是一个简单的过程。要将一定数量的 Wei 转换为 Gwei,只需将给定的数量除以十亿。例如,如果你有 420,069,000,000 Wei,将它除以 10 亿,得到 420.069 Gwei。
V. Wei 转换为以太币
除了将 Wei 转换为 Gwei,还了解如何将 Wei 转换为更常见的以太币也是很重要的。要做到这一点,将 Wei 的数量除以 10^18,这是一个重要的数字,因为一个以太币由 100,000,000,000,000,000 个 Wei 组成。举个例子,如果你拥有 100 万亿个 Wei,将其除以 10^18 将得到 0.0001 以太币。
VI. 代码中的 Wei 转换为以太币
对于开发人员,通过 Chainbase SDK 的 formatEther 方法轻松地将 Wei 转换为以太币。通过使用这种方法,你可以轻松地从特定地址获取余额,然后将其格式化为以太币。这种方便高效的方法简化了各种基于以太坊的应用和服务,提升了整个网络中的用户体验。
总之,Wei 和 Gwei 是以太坊生态系统的关键组成部分,使用户能够进行精确高效的加密货币交易。随着以太坊网络的不断发展,理解这些面值对于普通用户和开发人员来说变得越来越重要。
常见问题解答
- 在以太坊中,Wei 的重要性是什么?
Wei 代表以太坊网络中最小的 Ether 单位,这对于进行微小交易和精确价值转移至关重要。
- 谁是 Wei Dai,为什么 Wei 以他的名字命名?
Wei Dai 是一位著名的密码学家,以他在密码学领域的贡献而闻名。以太币的最小单位 Wei 是以他的名字命名的。
- 为什么 Gwei 是以太坊中最常用的单位?
Gwei,即 Gigawei 的缩写,因其在计算燃气费时的实用性而在以太坊中广泛使用和采纳。
- 如何将 Wei 转换为 Gwei?
要将 Wei 转换为 Gwei,将 Wei 的数量除以十亿。例如,1,000,000,000 Wei 等于 1 Gwei。
- 燃气费在以太坊交易中的作用是什么?
燃气费是执行以太坊网络上的交易和智能合约时产生的费用,确保其安全性和功能性。
Dai Wei
戴伟是贝恩资本加密货币的研究合伙人,密码学家,加州大学圣地亚哥分校博士。 研究兴趣:密码协议的设计和分析,尤其是有关区块链及其应用程序的安全性,可扩展性和隐私性。
戴伟是一位兴趣广泛的密码学专家,他在 1998 年发明了 B-money 匿名的、分布式的电子加密货币系统,强调点对点的交易和不可更改的交易记录。中本聪发明比特币的时候,借鉴了很多戴伟的设计,并和戴伟有很多邮件交流。
Solidity基础
- Remix,Solidity编译器和开源协议
- Solidity 基础数据类型
- Solidity 函数
- Solidity 存储模式:memory, storage, calldata
- Solidity 基础数据结构:结构体,数组和映射
- 合约间交互:工厂模式
address(0)
- 简单智能合约示例
Remix,Solidity编译器和开源协议
开源协议网站spdx
An open standard capable of representing systems with software components in as SBOMs (Software Bill of Materials) and other AI, data and security references supporting a range of risk management use cases.
The SPDX specification is a freely available international open standard (ISO/IEC 5692:2021).
Remix
Remix编译器是一个基于Web的以太坊智能合约开发工具,提供了在线编辑、编译、调试和部署智能合约的功能。Remix编译器有几个版本,每个版本提供了不同的功能和特性,以下是关于几个常见版本的介绍:
-
Remix IDE:Remix IDE是最常见的版本,是一个功能齐全的在线IDE,包括编辑器、编译器、调试器和部署者等功能。Remix IDE支持Solidity智能合约的编写和编译,同时还提供了交互式调试和测试合约的功能。
-
Remix Alpha:Remix Alpha是一个基于JavaScript的轻量级编辑器,专注于提供简单的合约编辑和快速编译的功能。Remix Alpha适用于快速原型开发和简单合约编写。
-
Remixd:Remixd是一个独立的后端服务,用于在本地编写智能合约并与Remix IDE进行交互。通过Remixd,用户可以在本地编辑器中编写合约,并将更改同步到Remix IDE中进行编译和调试。
-
Remix Desktop:Remix Desktop是一个基于Electron框架构建的桌面应用程序,集成了Remix IDE的功能,并支持通过本地文件系统访问和编辑智能合约。Remix Desktop提供了更好的性能和用户体验,适合于在本地环境中进行智能合约开发。
不同版本的Remix编译器适用于不同的开发需求和环境,用户可以根据自己的需求选择合适的版本进行智能合约开发和调试。
基本代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract HelloWorld {
string strVar = "Hello World";
function sayHello() public view returns(string memory){
return addInfo(strVar);
}
function sayHelloWorld(string memory newString) public {
strVar = newString;
}
function addInfo(string memory helloWorldStr) internal pure returns(string memory){
return string.concat(helloWorldStr, " from Trump's contact.");
}
}
Solidity 基础数据类型
Solidity 是一种高级智能合约编程语言,适用于以太坊平台上的智能合约开发。Solidity 支持多种基本数据类型,以下是 Solidity 中常用的基础数据类型及其详细介绍:
-
布尔类型(bool):布尔类型表示 true 或 false 值,只有两个可能的取值。
-
整数类型:Solidity 支持有符号和无符号的整数类型,包括 int、uint、uint8 到 uint256(步长为 8,即 uint8、uint16、uint24 等)及 int8 到 int256。int 类型表示带符号整数,uint 类型表示无符号整数。
-
固定点数类型:Solidity 支持固定点数类型用以表示小数值,包括 fixed 和 ufixed 类型。例如 fixed128x18 表示带符号固定点数,共有 128 位,小数点后有 18 位。
-
地址类型(address):地址类型用于存储以太坊账户地址。在合约中,可以使用 address 类型来处理以太坊地址。
-
字符串类型(string):字符串类型用于存储 UTF-8 编码的字符串数据。
-
字节类型(bytes):字节类型是一个动态大小的字节数组,可以用来存储二进制数据。
-
数组类型:Solidity 支持多种数组类型,如定长数组(如 uint[5]),动态数组(如 uint[])和多维数组。
-
枚举类型(enum):枚举类型用于定义一组有限的命名常数。
-
其他特殊类型:还包括合约类型(contract)用于实例化合约对象、元组类型(tuple)、映射类型(mapping)用于键值对存储等。
以上是 Solidity 中常用的基础数据类型,了解这些数据类型将有助于开发人员正确声明和使用在智能合约中的变量和函数。
bool、unit、int、bytes32、address,基础数据类型不需要指定存储方式。
string需要指定存储方式。
array默认是storage。
Solidity 函数
下面是一个简单的 Solidity 合约示例,展示了如何声明和定义函数,以及如何在函数中处理数据:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SimpleContract {
// 状态变量
uint public data;
// 构造函数
constructor() {
data = 0;
}
// 函数:增加数据
function addData(uint _value) public {
data += _value;
}
// 函数:获取数据
function getData() public view returns (uint) {
return data;
}
// 函数:重置数据
function resetData() public {
data = 0;
}
}
在上述示例中,我们定义了一个简单的 Solidity 合约 SimpleContract。该合约包含了以下几个部分:
-
状态变量:在合约中声明了一个
uint
类型的状态变量data
,用于存储数据。 -
构造函数:合约的构造函数在合约创建时会自动执行,用于初始化合约的状态变量。在本例中,构造函数将
data
初始化为0
。 -
函数:合约中包含了三个函数,分别实现了增加数据、获取数据和重置数据的功能。
addData(uint _value)
:接受一个uint
类型的参数_value
,并将其加到data
上。getData()
:返回当前data
的值,使用view
关键字表示该函数只读取合约状态而不改变它。resetData()
:重置data
为0
。
通过这些函数,可以实现对合约中状态变量 data
的修改和查询。在 Solidity 合约中,函数的定义可以包括访问修饰符、参数列表、返回值、函数体等内容,开发人员根据需求定义不同功能的函数以实现智能合约的逻辑。
Solidity 存储模式:memory, storage, calldata
这一节知识是底层原理,不需要完全理解也可以继续学习,这部分英文文档会更加清晰。
Solidity 存储模式是指 Solidity 合约中数据的存储和布局方式。了解 Solidity 存储模式对于理解智能合约的内部工作原理和有效管理合约中的数据非常重要。以下是 Solidity 存储模式的详细介绍:
-
状态变量存储模式:
- Solidity 合约中声明的状态变量默认存储在以太坊区块链中。
- 每个状态变量都有自己的存储位置,可以通过变量名来访问。
- 存储的数据占用以太坊网络的存储空间,需要消耗一定的 Gas(燃料)费用。
- 状态变量的存储位置在合约的存储空间中是持久化的,即数据会保存在区块链中。
-
内部变量存储模式:
- 在函数内声明的变量是内部变量,存储在内存中而不是区块链上。
- 内部变量的生存周期仅限于函数的执行过程,函数执行完毕后变量的值会被销毁。
- 内部变量不会产生永久性存储开销,因此 Gas 消耗较低。
-
引用类型存储模式:
- 引用类型(数组、结构体、映射等)的存储方式涉及到数据复制或引用操作。
- 对于数组和结构体等复杂数据类型,其存储方式取决于类型的大小和复杂程度。
- 大型数组或结构体的存储操作可能引起 Gas 成本增加,因为复制或引用操作会占用更多资源。
-
存储和内存关键字:
- Solidity 提供了
storage
和memory
关键字,可以用来指定变量是存储在区块链上还是内存中。 storage
关键字用于表示将数据存储在区块链中(状态变量)。memory
关键字用于表示将数据存储在内存中(函数内部变量)。
- Solidity 提供了
总的来说,Solidity 存储模式需要根据合约的具体需求和数据大小来选择合适的存储方式。合理使用状态变量、内部变量和引用类型,并正确管理存储位置,有助于优化合约的 Gas 消耗和合约性能。对 Solidity 存储模式的理解是 Solidity 开发人员必备的基础知识之一。
storage、memory、calldata、stack、codes、logs,前三种是重点
类型 | 特点 |
---|---|
storage | 永久,默认 |
memory | 暂时,充当函数可修改参数 |
calldata | 暂时,充当函数不可修改参数,修改报错 |
Solidity 基础数据结构:结构体,数组和映射
Solidity 官方文档中,关于数据和结构体,英文文档比中文文档在定义上表述更清晰。
基本数据结构
Solidity 中的基础数据结构包括结构体(struct)、数组(array)和映射(mapping),它们用于组织和存储数据。以下是对这些数据结构的详细介绍:
-
结构体(struct):
- 结构体是一种用户定义的数据结构,用于存储多个不同类型的数据。
- 结构体可以包含多个成员变量,每个成员变量可以是不同的数据类型。
- 结构体的定义使用
struct
关键字,类似于面向对象编程中的类。 - 示例:
struct Person { string name; uint age; }
- 在合约中可以实例化结构体,并访问结构体的成员变量。
-
数组(array):
- 数组是一组相同类型的数据元素的集合。
- 数组可以是定长数组或动态数组。定长数组在声明时指定长度,动态数组可以动态增加或减少其长度。
- 数组的元素可以通过索引来访问。
- Solidity 支持单维和多维数组。
- 示例:
uint[] public numbers; // 动态数组 uint[3] public fixedNumbers; // 定长数组
- 数组在合约中常用于存储列表、集合或序列的数据。
-
映射(mapping):
- 映射是一种键值对的数据结构,类似于字典或哈希表。
- 映射在 Solidity 中用于快速查找和存储键值对数据。
- 映射的键和值可以是任意数据类型。
- 映射的定义使用
mapping
关键字。 - 示例:
mapping(address => uint) public balances; // 地址到 uint 的映射
- 映射常用于存储账户余额、数据索引等键值对数据。
结构体、数组和映射是 Solidity 中常用的基础数据结构,它们可以灵活应用于智能合约的数据组织和存储需求。合约开发者可以根据实际情况选择合适的数据结构,设计出高效和清晰的智能合约。对这些基础数据结构的深入了解和灵活运用是 Solidity 开发过程中的关键之一。
基本代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract HelloWorld {
string strVar = "Hello World";
function sayHello() public view returns(string memory){
return addInfo(strVar);
}
function sayHelloWorld(string memory newString) public {
strVar = newString;
}
function addInfo(string memory helloWorldStr) internal pure returns(string memory){
return string.concat(helloWorldStr, " from Trump's contract.");
}
}
msg 环境变量,有msg.value、msg.sender
结构体和数组的使用
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract HelloWorld {
string strVar = "Hello World";
struct Info {
string phrase;
uint256 id;
address addr;
}
Info[] infos;
function sayHello(uint256 _id) public view returns(string memory){
for(uint256 i = 0; i < infos.length; i++){
if(infos[i].id == _id){
return addInfo(infos[i].phrase);
}
}
return addInfo(strVar);
}
function setHelloWorld(string memory newString, uint256 _id) public {
Info memory info = Info(newString,_id,msg.sender);
infos.push(info);
}
function addInfo(string memory helloWorldStr) internal pure returns(string memory){
return string.concat(helloWorldStr, " from Trump's contract.");
}
}
mapping和struct的使用
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract HelloWorld {
string strVar = "Hello World";
struct Info {
string phrase;
uint256 id;
address addr;
}
// mapping(uint256 => Info) infoMapping;
mapping(uint256 id => Info infos) infoMapping;
function sayHello(uint256 _id) public view returns(string memory){
if(infoMapping[_id].addr == address(0x0)){
return addInfo(strVar);
}else{
return addInfo(infoMapping[_id].phrase);
}
}
function setHelloWorld(string memory newString, uint256 _id) public {
Info memory info = Info(newString,_id,msg.sender);
infoMapping[_id] = info;
}
function addInfo(string memory helloWorldStr) internal pure returns(string memory){
return string.concat(helloWorldStr, " from Trump's contract.");
}
}
合约间交互:工厂模式
合约引入
在 Solidity 中,可以通过引入(import)其他合约来重用已有的代码逻辑和功能。
Solidity 引入在线合约。
以下是一个使用引入合约的简单示例:
假设有两个 Solidity 合约:ContractA.sol
和 ContractB.sol
,现在我们想在 ContractA.sol
中引入 ContractB.sol
。
- ContractB.sol:
// ContractB.sol
pragma solidity ^0.8.0;
contract ContractB {
uint public data;
function setData(uint _value) public {
data = _value;
}
function getData() public view returns (uint) {
return data;
}
}
- ContractA.sol:
// ContractA.sol
pragma solidity ^0.8.0;
// 引入ContractB.sol
import "./ContractB.sol";
contract ContractA {
ContractB contractBInstance;
constructor() {
contractBInstance = new ContractB();
}
function setDataInB(uint _value) public {
contractBInstance.setData(_value);
}
function getDataFromB() public view returns (uint) {
return contractBInstance.getData();
}
}
在上述示例中,我们做了如下操作:
- 在
ContractA.sol
中使用import "./ContractB.sol";
引入了ContractB.sol
,使得ContractA.sol
可以访问ContractB.sol
中的合约定义。 - 在
ContractA
合约的构造函数中创建了一个ContractB
的实例contractBInstance
。 - 在
setDataInB
函数中,调用了ContractB
实例的setData
函数,设置了数据。 - 在
getDataFromB
函数中,调用了ContractB
实例的getData
函数,获取了数据。
通过引入合约的方式,可以将代码逻辑模块化,提高代码的可读性和可维护性。在 Solidity 中,引入合约是一种常见的代码复用技术,有助于降低代码重复度并提高开发效率。在 Solidity 中,可以通过引入(import)其他合约来重用已有的代码逻辑和功能。以下是一个使用引入合约的简单示例:
假设有两个 Solidity 合约:ContractA.sol
和 ContractB.sol
,现在我们想在 ContractA.sol
中引入 ContractB.sol
。
- ContractB.sol:
// ContractB.sol
pragma solidity ^0.8.0;
contract ContractB {
uint public data;
function setData(uint _value) public {
data = _value;
}
function getData() public view returns (uint) {
return data;
}
}
- ContractA.sol:
// ContractA.sol
pragma solidity ^0.8.0;
// 引入ContractB.sol
import "./ContractB.sol";
contract ContractA {
ContractB contractBInstance;
constructor() {
contractBInstance = new ContractB();
}
function setDataInB(uint _value) public {
contractBInstance.setData(_value);
}
function getDataFromB() public view returns (uint) {
return contractBInstance.getData();
}
}
在上述示例中,我们做了如下操作:
- 在
ContractA.sol
中使用import "./ContractB.sol";
引入了ContractB.sol
,使得ContractA.sol
可以访问ContractB.sol
中的合约定义。 - 在
ContractA
合约的构造函数中创建了一个ContractB
的实例contractBInstance
。 - 在
setDataInB
函数中,调用了ContractB
实例的setData
函数,设置了数据。 - 在
getDataFromB
函数中,调用了ContractB
实例的getData
函数,获取了数据。
通过引入合约的方式,可以将代码逻辑模块化,提高代码的可读性和可维护性。在 Solidity 中,引入合约是一种常见的代码复用技术,有助于降低代码重复度并提高开发效率。
工厂模式
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { HelloWorld } from "./test.sol";
contract HelloWorldFactory {
HelloWorld hw;
HelloWorld[] hws;
function createHelloWorld() public {
hw = new HelloWorld();
hws.push(hw);
}
function getHelloWorldByIndex(uint256 _index) public view returns (HelloWorld) {
return hws[_index];
}
function callSayHelloFromFactory(uint256 _index, uint256 _id) public view returns (string memory) {
return hws[_index].sayHello(_id);
}
function callSetHelloWorldFromFactory(uint256 _index, uint256 _id, string memory newString) public {
hws[_index].setHelloWorld(newString, _id);
}
}
address(0)
address(0)
在 Solidity 中是一个特殊的地址,表示 "零地址" 或 "空地址"。它是一个没有有效以太坊地址的存在,其值为 0x0000000000000000000000000000000000000000
。在智能合约编程中,address(0)
通常用于执行以下几种主要目的:
1. 检查状态或初始化
在智能合约中,使用 address(0)
可以用来检查某个地址是否未被设置或未分配。例如,在您的合约中,通过检查 owners[petId]
是否等于 address(0)
来判断某个宠物(petId
)是否尚未被购买(即无有效拥有者)。
require(owners[petId] == address(0), "This pet has already been purchased.");
如果 owners[petId]
为 address(0)
,这意味着该宠物尚未有业主,购买请求是有效的。
2. 指示空值
在以太坊和智能合约的上下文中,address(0)
通常用作“空值”或“无效值”的指示。比如,在某些情况下,如果智能合约需要表示一个没有实际存在的地址,使用 address(0)
是一种标准做法。
3. 防止重入攻击
在某些模式中,合约可以使用 address(0)
来记录状态,以确保函数调用不能被重入。例如,当一个合约在执行某个重要操作时,可以暂时将某些状态变量设置为 address(0)
,以防止由于重入导致意外的结果。
4. 代币和分配机制
在代币合约中,address(0)
通常被用作销毁代币的地址。将代币发送到 address(0)
可以表明这些代币已被销毁,而不是被转移到另一个账户。这样可以在代币的总供给中减去这些代币。
总结
总的来说,address(0)
是 Solidity 中用于表示空地址或未设置状态的强有力工具,广泛用于状态检查、合约逻辑管理和安全性增强。使用 address(0)
可以简化合约逻辑,降低出错带来的风险。
简单智能合约示例
如何在Remix上部署和运行这些合约
-
访问Remix:在浏览器中打开Remix IDE。
-
新建文件:
- 在“File Explorers”中,点击“新建文件”图标。
- 输入文件名,如
SimpleStorage.sol
,然后点击“Enter”。
-
粘贴合约代码:
- 在新建的文件中,粘贴上面的合约代码。
-
编译合约:
- 转到“Solidity编译器”选项卡。
- 选择编译的Solidity版本(例如:0.8.0,确保用到的版本和代码中的
pragma
声明的版本匹配)。 - 点击“编译
SimpleStorage.sol
”。
-
部署合约:
- 转到“部署&运行交易”选项卡。
- 在“环境”下拉列表中,选择“JavaScript VM”。
- 点击“部署”按钮。
-
运行合约:
- 部署后,在“已部署合约”下,你将看到合约实例。
- 可以展开合约实例,查看并与合约中的函数交互(如调用
set
及get
函数)。
对于每个合约,重复创建文件、粘贴代码、编译和部署的步骤即可。
1. 简单存储合约
这个合约允许用户存储和检索一个整数。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SimpleStorage {
uint256 private data;
function set(uint256 x) public {
data = x;
}
function get() public view returns (uint256) {
return data;
}
}
2. 数字代币合约
这是一个简单的代币合约,允许用户转账代币。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract BasicToken {
mapping(address => uint256) private balances;
constructor() {
balances[msg.sender] = 10000; // 初始供应给合约部署者
}
function transfer(address to, uint256 amount) public {
require(balances[msg.sender] >= amount, "Not enough tokens");
balances[msg.sender] -= amount;
balances[to] += amount;
}
function balanceOf(address account) public view returns (uint256) {
return balances[account];
}
}
3. 简单拍卖合约
一个简单的拍卖合约,允许人们出价,并将最高出价者设置为赢家。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SimpleAuction {
address public highestBidder;
uint256 public highestBid;
function bid() public payable {
require(msg.value > highestBid, "There already is a higher bid.");
if (highestBidder != address(0)) {
payable(highestBidder).transfer(highestBid);
}
highestBidder = msg.sender;
highestBid = msg.value;
}
}
4. 一次性合约
一个合约只允许接收一次Ether的简单实现。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract OneTimeSend {
bool private received;
function receiveEther() public payable {
require(!received, "Ether has already been received.");
received = true;
}
function withdraw() public {
require(received, "No Ether to withdraw.");
payable(msg.sender).transfer(address(this).balance);
}
}
5. 投票合约
一个投票合约,允许用户为不同的候选人投票。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Voting {
mapping(string => uint256) private votes;
function vote(string memory candidate) public {
votes[candidate] += 1;
}
function getVotes(string memory candidate) public view returns (uint256) {
return votes[candidate];
}
}
6. 简单投票简单安全合约
这个合约允许用户为一个选项进行投票,同时为保证安全性,只有合约创建者可以结束投票。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SecureVoting {
address public owner;
mapping(string => uint256) private voteCounts;
bool public votingEnded;
modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call this function.");
_;
}
constructor() {
owner = msg.sender;
votingEnded = false;
}
function vote(string memory candidate) public {
require(!votingEnded, "Voting has ended.");
voteCounts[candidate] += 1;
}
function getVotes(string memory candidate) public view returns (uint256) {
return voteCounts[candidate];
}
function endVoting() public onlyOwner {
votingEnded = true;
}
}
7. 简单拍卖合约与拍卖时间限制
一个简单的拍卖合约,增加了拍卖的时间限制。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract TimedAuction {
address public highestBidder;
uint256 public highestBid;
uint256 public auctionEndTime;
constructor(uint256 _biddingTime) {
auctionEndTime = block.timestamp + _biddingTime;
}
function bid() public payable {
require(block.timestamp < auctionEndTime, "Auction already ended.");
require(msg.value > highestBid, "There already is a higher bid.");
if (highestBidder != address(0)) {
payable(highestBidder).transfer(highestBid);
}
highestBidder = msg.sender;
highestBid = msg.value;
}
function auctionEnded() public view returns (bool) {
return block.timestamp >= auctionEndTime;
}
}
8. 基本存储为数组合约
该合约允许用户将值存入一个数组,并检索这些值。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ArrayStorage {
uint256[] private data;
function addValue(uint256 value) public {
data.push(value);
}
function getValue(uint256 index) public view returns (uint256) {
return data[index];
}
function getArrayLength() public view returns (uint256) {
return data.length;
}
}
9. 基本的以太坊提款合约
这是一个简单的提款合约,允许合约部署者将捐款提取到他们自己的地址。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SimpleDonation {
address public owner;
uint256 public totalDonations;
modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call this function.");
_;
}
constructor() {
owner = msg.sender;
}
function donate() public payable {
totalDonations += msg.value;
}
function withdraw() public onlyOwner {
payable(owner).transfer(address(this).balance);
}
}
10. 简易访问控制合约
这个合约允许只有指定的管理员可以执行特定操作。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract AccessControl {
mapping(address => bool) private admins;
address public owner;
modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call this function.");
_;
}
modifier onlyAdmins() {
require(admins[msg.sender], "Only admins can call this function.");
_;
}
constructor() {
owner = msg.sender;
}
function addAdmin(address admin) public onlyOwner {
admins[admin] = true;
}
function removeAdmin(address admin) public onlyOwner {
admins[admin] = false;
}
function executeRestrictedAction() public onlyAdmins view returns(string memory) {
return "Action performed.";
}
}