AES詳解(Java實現)

来源:https://www.cnblogs.com/mx-lqk/archive/2019/01/19/10285379.html
-Advertisement-
Play Games

GitHub AES 高級數據加密標準(Advanced Encryption Standard),簡稱AES,由美國政府於1997年開始公開徵集的新的數據加密標準演算法。經過三輪篩選,美國政府最終於2000年10月2日正式宣佈選中密碼學家Joan Daemen和Vincent Rijmen提出的RI ...


GitHub


AES

  高級數據加密標準(Advanced Encryption Standard),簡稱AES,由美國政府於1997年開始公開徵集的新的數據加密標準演算法。經過三輪篩選,美國政府最終於2000年10月2日正式宣佈選中密碼學家Joan Daemen和Vincent Rijmen提出的RINJDAEL演算法作為AES。

  RINJDAEL演算法之所以能夠最終被選為AES的原因是其安全、性能好、效率高、實用、靈活。

  RINJDAEL演算法是一個數據塊長度和密鑰長度都可變的分組加密演算法,其數據塊長度和密鑰長度都可獨立地選定為大於等於128位且小於等於256位的32位的任意倍數。而美國頒佈AES時卻規定數據塊的長度為128位,密鑰的長度可分別選擇為128位、192位或256位。

  RINJDAEL演算法仍然採用分組密碼的一種通用結構:對輪函數實施迭代的結構。只是輪函數結構採用的是代替/置換的網路結構(SP結構)。


數學基礎

  AES演算法中的許多運算是按位元組和4位元組的字來定義的。把一個位元組看成是在有限域GF(28)上的一個元素,把一個4位元組的字看成是繫數取自GF(28),並且次數小於4次的多項式。

GF(28)上的基礎運算

  一個由比特位b7b6b5b4b3b2b1b0組成的位元組B可表示成繫數為0或1的二進位多項式:b7x7+b6x6+b5x5+b4x4+b3x3+b2x2+b1x+b0。例如,位元組B=10011011與二進位多項式b(x)=x7+x4+x3+x+1相對應。

(1)在GF(28)上的加法

  在GF(28)上的加法定義為二進位多項式的加法,其繫數模2相加。

  e.m.

(x7+x4+x3+x+1)+(x6+x5+x3+x2+x+1)=x7+x6+x5+x4+x2

(2)在GF(28)上的乘法

  在GF(28)上的乘法定義為二進位多項式的乘積,如果乘積次數大於7次則模一個次數為8的不可約二進位多項式。

  e.m.對於不可約多項式m(x)=x8+x4+x3+x+1,

(x6+x4+x2+x+1)(x7+x+1)                                               

=(x13+x11+x9+x8+x6+x5+x4+x3+1)(mod x8+x4+x3+x+1)

=x7+x6+1                                                                            

(3)在GF(28)上的乘法逆

  在GF(28)中,二進位多項式b(x)的乘法逆為滿足a(x)b(x)=1的二進位多項式a(x),並記為

a(x)=b-1(x)。其中,'00'的乘法逆為其本身。

  可以使用擴展歐幾裡得演算法求得乘法逆,也可以直接將'00'~'FF'這128中情況全部試代求得乘法逆。

  e.m.對於不可約多項式m(x)=x8+x4+x3+x+1,因為

x6+x2+x+1=(x8+x4+x3+x+1)+(x6+x4+x2+x+1)

x4=(x6+x4+x2+x+1)+(x6+x2+x+1)=(x8+x4+x3+x+1)+(x2+1)(x6+x4+x2+x+1)

x2+x+1=(x6+x2+x+1)+x2·x4=(x2+1)(x8+x4+x3+x+1)+x4(x6+x4+x2+x+1)

x=x4+(x2+x)(x2+x+1)=x6+x2+x+1=(x4+x+1)(x8+x4+x3+x+1)+(x6+x5+x2+1)(x6+x4+x2+x+1)

1=(x2+x+1)+(x+1)x=(x5+x4)(x8+x4+x3+x+1)+(x7+x5+x4+x3+x2+x+1)(x6+x4+x2+x+1)

          所以

(x6+x4+x2+x+1)(x7+x5+x4+x3+x2+x+1)=1

          即

(x6+x4+x2+x+1)-1=x7+x5+x4+x3+x2+x+1

(4)在GF(28)上的倍乘

  在GF(28)中,倍乘函數 xtime(b(x))定義為x·b(x) (mod m(x))。即把位元組B左移一位,若結果次數大於7次,則加上不可約多項式m(x)。

  e.m.對於不可約多項式m(x)=x8+x4+x3+x+1,

x(x6+x4+x2+x+1)=x7+x5+x3+x2+x

x(x7+x5+x3+x2+x)=(x8+x6+x4+x3+x2)+(x8+x4+x3+x+1)=x6+x2+x+1

GF(28)上的多項式運算

  有限域GF(28)上的多項式是繫數取自GF(28)域元素的多項式。這樣,一個4位元組的字與一個次數小於4次的GF(28)上的多項式相對應。例如,字c='03010102'與多項式c(x)='03'x3+'01'x2+'01'x+'02'相對應。

(1)GF(28)上的多項式的加法

  GF(28)上的多項式的加法定義為相應項繫數相加。所以,在域GF(28)上的兩個4位元組的字相加也就是按位異或。

  e.m.

('03'x3+'01'x2+'01'x+'02')+('0B'x3+'0D'x2+'09'x+'0E')='08'x3+'0C'x2+'08'x+'0C'

(2)GF(28)上的多項式的乘法

  GF(28)上的多項式a(x)=a3x3+a2x2+a1x+a0和b(x)=b3x3+b2x2+b1x+b0相乘,如果乘積次數超過4次,則模x4+1。即對於c(x)=a(x)b(x)=c3x3+c2x2+c1x+c0

c0=a0b0⊕a3b1⊕a2b2⊕a1b3

c1=a1b0⊕a0b1⊕a3b2⊕a2b3

c2=a2b0⊕a1b1⊕a0b2⊕a3b3

c3=a3b0⊕a2b1⊕a1b2⊕a0b3

(3)GF(28)上的多項式的倍乘

  GF(28)上的多項式b(x)=b3x3+b2x2+b1x+b0的倍乘x·b(x)=b2x3+b1x2+b0x+b3。即多項式的繫數迴圈左移一位。


AES參數

數據塊字數Nb

  在AES演算法中,加解密要經過多次數據變換操作,每一次變換操作都會產生一個中間結果,這個結果稱為狀態。把狀態表示為一個4行Nb列的二維位元組數組,其中Nb為數據塊長度除以32。因為狀態數組有4行,所以狀態數組的每一列便為一個4位元組的字。

  例如,對於長度為128的數據塊B15B14...B1B0,Nb=128÷32=4,即該數據塊可以表示為狀態數組

  AES演算法中規定數據塊長度為128位,即Nb=128÷32=4。

密鑰字數Nk

  類似地,密鑰也可以表示為4行Nk列的二維位元組數組,其中Nk為密鑰長度除以32。同樣地,密鑰數組的每一列為一個4位元組的字。

  例如,對於長度為128的密鑰K15K14...K1K0,Nk=128÷32=4,即該密鑰可以表示為密鑰數組

  AES演算法中規定密鑰長度為128位、192位或256位,即Nk取值為4、6或8。

迭代輪數Nr

  AES演算法的迭代輪數Nr由Nb和Nk共同決定:

  AES規定Nb=4,所以對應Nk取值4、6、8,Nr取值分別為10、12、14,即在Nb=4的情況下,Nr=Nk+6。

不可約多項式m(x)

  在AES演算法中,不可約多項式建議為:m(x)=x8+x4+x3+x+1,其繫數的十六進位表示為m='11B'。


SP結構

  AES的輪函數由以下3層組成:

  1.非線性層:進行非線性S盒變換,由16個S盒並置而成,起混淆的作用;

  2.線性混合層:進行行移位變換和列混合變換以確保多輪之上的高度擴散;

  3.密鑰加層:進行輪密鑰加變換,將輪密鑰簡單地異或到中間狀態上。


AES演算法細節

(1)S盒變換

  S盒變換是按位元組進行的代替變換,是作用在狀態中每個位元組上的一種非線性位元組變換。

加密過程

  加密過程中的S盒變換按以下2步進行:

  (1)把位元組的值用它的乘法逆來代替;

  (2)進行如下的仿射變換:

xi'=xi⊕xi+4⊕xi+5⊕xi+6⊕xi+7

y7y6y5y4y3y2y1y0=(x7'x6'x5'x4'x3'x2'x1'x0')⊕(01100011)

   加密過程的S盒表如下:

解密過程

  解密過程中的S盒變換按以下2步進行:

  (1)進行如下的仿射變換:

xi'=xi+2⊕xi+5⊕xi+7

y7y6y5y4y3y2y1y0=(x7'x6'x5'x4'x3'x2'x1'x0')⊕(00000101)

  (2)把位元組的值用它的乘法逆來代替。

  解密過程的S盒表如下:

 

(2)行移位變換

  行移位變換是對狀態的行進行迴圈移位變換。移位值C1、C2、C3與Nb有關:

  AES規定Nb=4,所以C1=1,C2=2,C3=3。

加密過程

  加密過程中的行移位變換,狀態的第0行不移位,第1行迴圈左移C1位元組,第2行迴圈左移C2位元組,第3行迴圈左移C3位元組。

解密過程

  解密過程中的行移位變換,狀態的第0行不移位,第1行迴圈左移Nb-C1位元組,第2行迴圈左移Nb-C2位元組,第3行迴圈左移Nb-C3位元組。

(3)列混合變換

  列混合變換是對狀態的列進行混合變換。

加密過程

  加密過程中的列混合變換,把狀態中的每一列看作GF(28)上的多項式,並與固定多項式c(x)='03'x3+'01'x2+'01'x+'02'相乘。

解密過程

  解密過程中的列混合變換,把狀態中的每一列看作GF(28)上的多項式,並與固定多項式c(x)='0B'x3+'0D'x2+'09'x+'0E'相乘。

(4)輪密鑰加變換

  輪密鑰加變換是利用輪密鑰對狀態進行模2相加的變換。輪密鑰長度等於數據塊長度。在這個操作中,輪密鑰被簡單地異或到狀態中去。

(5)輪密鑰產生演算法

  輪密鑰根據輪密鑰產生演算法由主密鑰產生得到。輪密鑰產生分2步進行:密鑰擴展和輪密鑰選擇,且遵循以下原則:

  1.輪密鑰的比特總數為數據塊長度與輪數加1的,即Nb(Nr+1)。

  2.首先將用戶密鑰擴展為一個擴展密鑰。

  3.再從擴展密鑰中選出輪密鑰:第1個輪密鑰由擴展密鑰中的前Nb個字組成,第2個輪密鑰由接下來的Nb個字組成,以此類推。

加密過程

  加密過程的輪密鑰產生分以下2步進行:

  1.密鑰擴展:用1個字元素的一維數組W[Nb(Nr+1)]存儲擴展密鑰。把主密鑰放在數組W最開始的Nk個字中,其他的字由它前面的字經過處理(處理過程參照代碼部分)後得到。分Nk≤6和Nk>6兩種情況進行密鑰擴展,兩種情況的密鑰擴展策略稍有不同。

  2.輪密鑰選擇:輪密鑰i由輪密鑰緩衝區W[Nb*i]到W[Nb*(i+1)-1]的字組成。

解密過程

  解密過程的輪密鑰產生分以下2步進行:

  1.加密過程的輪密鑰產生。

  2.把解密過程的列混合變換應用到除第一個和最後一個輪密鑰之外的所有輪密鑰上。


AES演算法結構

  無論是加密過程還是解密過程,都由以下部分組成:

  1.一個初始輪密鑰加。

  2.Nr-1輪的標準輪函數(包括S盒變換、行移位、列混合、輪密鑰加)。

  3.最後一輪的非標準輪函數(只包括S盒變換、行移位、輪密鑰加,不需要列混合)。


AES的安全性

  AES的安全涉及策略是寬軌跡策略。寬軌跡策略是針對差分攻擊和先行攻擊提出來的。它的最大優點是可以給出演算法的最佳差分特征的概率以及最佳線性逼近的偏差界,由此可以分析演算法的抵抗差分攻擊和線性攻擊的能力。從而確保密碼演算法具有所需要的抵抗差分攻擊和線性攻擊的能力,確保密碼演算法的安全。

(1)抗攻擊能力

  AES的主要密碼部件S盒和列混合都設計得相當好。其列混合的一個重要密碼學指標分支數達到了最佳值,其S盒在非線性度、自相關性、差分均勻性、代數免疫性等主要密碼學指標方面都達到相當好的水平。

  然而,AES中的列混合的擴散度不夠,密鑰擴展的非線性不夠,而且缺少抵抗側通道分析的設計。這些不足之處給密碼分析提供了可乘之機。

(2)弱密鑰

  AES的加解密演算法採用不同的密鑰擴展演算法,而且都使用了非線性的S盒變換,並且在擴展產生每一輪密鑰中使用不同的輪函數。這些措施使得AES不存在弱密鑰和半弱密鑰,也就是說,在AES加解密演算法中,對密鑰的選擇沒有除長度外的任何限制。

(3)適應性

  AES的密鑰長度可變,因此能夠適應不同的安全應用環境。即使今後計算能力和攻擊能力提高了,只要及時提高密鑰的長度,便可獲得滿意的安全,因此密碼的安全使用壽命長。


Java實現

word類

  1 public class word {
  2     byte[] word;
  3 
  4     public word(byte[] b) {
  5         word = new byte[4];
  6         for (int i = 0; i < 4; i++) 
  7             word[i] = b[i];
  8     }
  9 
 10     public word(word w) {
 11         word = new byte[4];
 12         for (int i = 0; i < 4; i++)
 13             word[i] = w.word[i];
 14     }
 15 
 16     @Override
 17     public String toString() {
 18         String str = "";
 19         for (byte b : word) 
 20             str += Integer.toHexString((b & 0xff) + 0x100).substring(1);
 21         return str;
 22     }
 23 
 24     /**
 25      * 在GF(2^8)上的多項式加法
 26      * @param a
 27      * @param b
 28      * @return
 29      */
 30     static word add(word a, word b) {
 31         word c = new word(new byte[4]);
 32         for (int i = 0; i < 4; i++)
 33             c.word[i] = add(a.word[i], b.word[i]);
 34         return c;
 35     }
 36 
 37     /**
 38      * 在GF(2^8)上的多項式乘法
 39      * @param a
 40      * @param b
 41      * @return
 42      */
 43     static word multiply(word a, word b) {
 44         word c = new word(new byte[4]);
 45         c.word[0] = add(
 46                 add(
 47                         add(
 48                                 multiply(a.word[0], b.word[0]), multiply(a.word[3], b.word[1])), 
 49                         multiply(a.word[2], b.word[2])), 
 50                 multiply(a.word[1], b.word[3]));
 51         c.word[1] = add(
 52                 add(
 53                         add(
 54                                 multiply(a.word[1], b.word[0]), multiply(a.word[0], b.word[1])), 
 55                         multiply(a.word[3], b.word[2])), 
 56                 multiply(a.word[2], b.word[3]));
 57         c.word[2] = add(
 58                 add(
 59                         add(
 60                                 multiply(a.word[2], b.word[0]), multiply(a.word[1], b.word[1])), 
 61                         multiply(a.word[0], b.word[2])), 
 62                 multiply(a.word[3], b.word[3]));
 63         c.word[3] = add(
 64                 add(
 65                         add(
 66                                 multiply(a.word[3], b.word[0]), multiply(a.word[2], b.word[1])), 
 67                         multiply(a.word[1], b.word[2])), 
 68                 multiply(a.word[0], b.word[3]));
 69         return c;
 70     }
 71 
 72     /**
 73      * 在GF(2^8)上的多項式倍乘
 74      * @param a
 75      * @return
 76      */
 77     static word xtime(word a) {
 78         word b = new word(new byte[4]);
 79         for (int i = 0; i < 4; i++)
 80             b.word[i] = a.word[(i + 1) % 4];
 81         return b;
 82     }
 83 /**********************************************************************************/
 84     static int m = 0x11b;   //m=100011011
 85 
 86     /**
 87      * 在GF(2^8)上的加法
 88      * @param a
 89      * @param b
 90      * @return
 91      */
 92     static byte add(byte a, byte b) {
 93         return (byte) (a ^ b);
 94     }
 95 
 96     /**
 97      * 在GF(2^8)上的求模
 98      * @param a
 99      * @param b
100      * @return
101      */
102     static byte mod(int a, int b) {
103         String str_a = Integer.toBinaryString(a);
104         String str_b = Integer.toBinaryString(b);
105         if (str_a.length() < str_b.length()) 
106             return (byte) a;
107         return mod(a ^ (b << (str_a.length() - str_b.length())), b);
108     }
109 
110     /**
111      * 在GF(2^8)上的乘法
112      * @param a
113      * @param b
114      * @return
115      */
116     static byte multiply(byte a, byte b) {
117         int op = a & 0xff;
118         char[] c = Integer.toBinaryString((b & 0xff) + 0x100).substring(1).toCharArray();
119         int r = 0;
120         for (int i = 0; i < c.length; i++) 
121             if (c[i] == '1') 
122                 r ^= op << (7 - i);
123         return mod(r, m);
124     }
125 
126     /**
127      * 在GF(2^8)上的乘法逆
128      * @param a
129      * @return
130      */
131     static byte inverse(byte a) {
132         if (a == 0) return 0;
133         byte b = -128;
134         while (mod(multiply(a, b), m) != 1) 
135             b++;
136         return b;
137     }
138 
139     /**
140      * 在GF(2^8)上的倍乘
141      * @param a
142      * @return
143      */
144     static byte xtime(byte a) {
145         int r = (a & 0xff) << 1;
146         if (r > 127) 
147             return mod(r, m);
148         return (byte) r;
149     }
150 }

仿射變換

 1 /**
 2  * 仿射變換
 3  * @param b
 4  * @param sign  C:加密  D:解密
 5  * @return
 6  */
 7 static byte AffineTransformation(byte b, char sign) {
 8     byte[] x = Integer.toBinaryString((b & 0xff) + 0x100).substring(1).getBytes();
 9     for (int i = 0; i < x.length; i++) x[i] -= '0';
10     if (sign == 'C') {
11         byte[] x_ = new byte[8];
12         byte b_ = 0;
13         for (int i = 0; i < 8; i++) {
14             x_[i] = (byte) (x[i] ^ x[(i + 1) % 8] ^ x[(i + 2) % 8] ^ x[(i + 3) % 8] ^ x[(i + 4) % 8]);
15             b_ += x_[i] * Math.pow(2, 7 - i);
16         }
17         return (byte) (b_ ^ 0x63);
18     } else {
19         byte[] x_ = new byte[8];
20         byte b_ = 0;
21         for (int i = 0; i < 8; i++) {
22             x_[i] = (byte) (x[(i + 1) % 8] ^ x[(i + 3) % 8] ^ x[(i + 6) % 8]);
23             b_ += x_[i] * Math.pow(2, 7 - i);
24         }
25         return (byte) (b_ ^ 0x05);
26     }
27 }

輪密鑰產生演算法

 1 /**
 2  * 加密密鑰擴展
 3  * @param CipherKey
 4  * @return
 5  */
 6 static word[][] KeyExpansion(word[] CipherKey) {
 7     word[] W = new word[Nb * (Nr + 1)];
 8     //密鑰擴展
 9     word Temp;
10     if (Nk <= 6) {
11         for (int i = 0; i < Nk; i++) 
12             W[i] = CipherKey[i];
13         for (int i = Nk; i < W.length; i++) {
14             Temp = new word(W[i - 1]);
15             if (i % Nk ==0) 
16                 Temp = word.add(SubByte(Rotl(Temp)), Rcon(i / Nk));
17             W[i] = word.add(W[i - Nk], Temp);
18         }
19     } else {
20         for (int i = 0; i < Nk; i++) 
21             W[i] = CipherKey[i];
22         for (int i = Nk; i < W.length; i++) {
23             Temp = new word(W[i - 1]);
24             if (i % Nk ==0) 
25                 Temp = word.add(SubByte(Rotl(Temp)), Rcon(i / Nk));
26             else if (i % Nk == 4) 
27                 Temp = SubByte(Temp);
28             W[i] = word.add(W[i - Nk], Temp);
29         }
30     }
31     //輪密鑰選擇
32     word[][] RoundKey = new word[Nr + 1][Nb];
33     for (int i = 0; i < Nr + 1; i++) 
34         for (int j = 0; j < Nb; j++) 
35             RoundKey[i][j] = W[Nb * i + j];
36     return RoundKey;
37 }
 1 static word SubByte(word a) {
 2     word w = new word(a);
 3     for (int i = 0; i < 4; i++) {
 4         //乘法逆代替
 5         w.word[i] = word.inverse(w.word[i]);
 6         //仿射變換
 7         w.word[i] = AffineTransformation(w.word[i], 'C');
 8     }
 9     return w;
10 }
11 
12 static word Rotl(word a) {
13     word w = new word(a);
14     byte b = w.word[0];
15     for (int i = 0; i < 3; i++) 
16         w.word[i] = w.word[i + 1];
17     w.word[3] = b;
18     return w;
19 }
20 
21 static word Rcon(int n) {
22     word Rcon = new word(new byte[4]);
23     byte RC = 1;
24     for (int i = 1; i < n; i++) 
25         RC = word.xtime(RC);
26     Rcon.word[0] = RC;
27     return Rcon;
28 }
 1 /**
 2  * 解密密鑰擴展
 3  * @param CipherKey
 4  * @return
 5  */
 6 static word[][] InvKeyExpansion(word[] CipherKey) {
 7     word[][] InvRoundKey = KeyExpansion(CipherKey);
 8     for (int i = 1; i < Nr; i++) 
 9         InvRoundKey[i] = InvMixColumn(InvRoundKey[i]);
10     return InvRoundKey;
11 }

S盒變換

 1 /**
 2  * S盒變換
 3  * @param state
 4  * @return
 5  */
 6 static word[] ByteSub(word[] state) {
 7     for (int i = 0; i < Nb; i++) 
 8         for (int j = 0; j < 4; j++) {
 9             //乘法逆代替
10             state[i].word[j] = word.inverse(state[i].word[j]);
11             //仿射變換
12             state[i].word[j] = AffineTransformation(state[i].word[j], 'C');
13         }
14     return state;
15 }
 1 /**
 2  * S盒逆變換
 3  * @param state
 4  * @return
 5  */
 6 static word[] InvByteSub(word[] state) {
 7     for (int i = 0; i < Nb; i++) 
 8         for (int j = 0; j < 4; j++) {
 9             //仿射變換
10             state[i].word[j] = AffineTransformation(state[i].word[j], 'D');
11             //乘法逆代替
12             state[i].word[j] = word.inverse(state[i].word[j]);
13         }
14     return state;
15 }

行移位變換

 1 /**
 2  * 行移位變換
 3  * @param state
 4  * @return
 5  */
 6 static word[] ShiftRow(word[] state) {
 7     byte[][] b = new byte[4][Nb];
 8     for (	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 摘要: 理解瀏覽器渲染。 原文: "JavaScript是如何工作的: CSS 和 JS 動畫底層原理及如何優化它們的性能" 作者: "前端小智" "Fundebug" 經授權轉載,版權歸原作者所有。 這是專門探索 JavaScript 及其所構建的組件的系列文章的第 13 篇。 如果你錯過了前面的 ...
  • https://blogs.msdn.microsoft.com/dotnet/2019/01/10/announcing-ml-net-0-9-machine-learning-for-net/ https://marketplace.visualstudio.com/items?itemName ...
  • 既然選擇了遠方,便只顧風雨兼程! __HANS許 系列:零基礎搭建前後端分離項目 系列:零基礎搭建前後端分離項目 [email protected] 創建項目 Vue的Debug(調試) Vue的Http請求 提示:本篇圖片較多,所以篇幅較長。 在前面幾篇文章我們講了Vue的基本內容,語法,組件,插件等等。但例 ...
  • 1. 組合繼承:又叫偽經典繼承,是指將原型鏈和借用構造函數技術組合在一塊的一種繼承方式。 下麵來看一個例子: 組合繼承避免了原型鏈和借用構造函數的缺陷,融合它們的優點。 2. 原型式繼承 可以在不必預先定義構造函數的情況下實現繼承,其本質是執行對給定對象的淺複製。而複製得到的副本還可以得到進一步的改 ...
  • 本文由雲+社區發表 作者:騰訊工蜂用戶:王二衛 從不一樣的視角瞭解git,以便更好的使用git 一、git & git 版本庫認識 git 是一個內容定址的文件系統,其核心部分是一個簡單的鍵值對資料庫(key value data store),可以向該資料庫插入任意類型的內容,它會返回一個40位長 ...
  • 有些小伙伴不熟悉Jenkins, 在mac上安裝,會選擇pkg 安裝包, 安裝後又想卸載,苦於卸載不幹凈,今天給到一個命令即可搞定. 對應qq群號:616961231打開終端輸入下麵命令'/Library/Application Support/Jenkins/Uninstall.command' ...
  • 目錄 0).簡單工廠模式 1).工廠方法模式 2).抽象工廠模式 3).單例模式 4).構建者模式 5).原型模式 6).適配器模式 7).修飾者模式 8).代理模式 9).外觀模式 10).橋接模式 11).組合模式 12).享元模式 13).策略模式 14).模板方法模式 15).觀察者模式 1 ...
  • 第一篇:數據倉庫的概述 1.數據倉庫基本概念 數據倉庫是一個面向主題的、集成的、不可更新的、隨時間變化的。 它用於支持企業或組織的決策分析處理。 2.數據分層 ODS(Operatitional data store)層:稱為源數據層,表結構與業務系統的表保持基本一致。通常在ODS層主要做一些欄位的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...