Categories
密碼學 All

密碼學(2): Modern Block Ciphers

Block Ciphers 可以譯作分組加密或者分塊加密。

常用 Block Ciphers 相提并論的加密方式是 Stream Ciphers,可譯作串流加密法。兩者都是替換式密碼(Substitution ciphers),都應用了可逆映射(reversible mapping):

Stream Ciphers

串流加密法是一種對稱密鑰密碼,其中明文數字與偽隨機密碼數字流(密鑰流)相結合。在實踐中,一個數字通常是一個 bit,與異或 (XOR)組合操作,把明文訊息逐個 bit 轉換成密文。

典型的 Stream Ciphers 是自動加密的 Vigenère 密碼和 Vernam 密碼。

較有名的 Stream Ciphers 是 RC4。

RC4

RSA DSI 擁有的專有密碼,由 Ron Rivest 設計,簡單但有效。密鑰大小可變,是一種面向字節的串流加密法。在 SSL/TLS、無線 WEP 中廣泛使用。密鑰形成所有 8-bit 的隨機排列,使用該排列來打亂輸入信息,一次處理一個字節。

RC4 Key Schedule:

  • 以數字數組 S 開頭:0..255
  • 使用鍵來完全地打亂
  • S 形成密碼的內部狀態
  • 給定一個長度為 keylen 個字節的密鑰 K:
for i= 0 to 255 do
   S(i) = i
   T(i) = K(imod keylen)
   j = 0
for i= 0 to 255 do 
   j = (j + S(i) + T(i)) mod 256 
   swap (S(i), S(j))

RC4 加解密:

  • 加密繼續打亂數組值。
  • 已打亂的每對的總和選取 stream key 值。
  • 與要加密/解密的下一個消息字節進行異或(XOR)運算:
i= j = 0 
for each message byte Mi
   i= (i+ 1) mod 256
   j = (j + S(i)) mod 256
   swap(S(i), S(j)) //keep shuffling 
   S()t = (S(i) + S(j)) mod 256
   Ci= Mi⊕S(t)

RC4 的安全性:

  • 聲稱對已知攻擊安全(有一些分析,沒有實踐)。
  • 結果是非常非線性的。
  • 由於 RC4 是一種串流加密法,絕不能重複使用密鑰。
  • 對 WEP 有疑慮,但這部分由密鑰處理而不是 RC4 本身。

Block Ciphers

明文區塊被視為一個整體,用於生成等長(64 / 128 位)的密文區塊。

密鑰長度/區塊大小/輪數可變。

混合運算符,數據。

費斯妥密碼(Feistel cipher) 是一種用於構造 Block Ciphers 的對稱結構,以 Horst Feistel 命名。

Feistel 提出我們可以利用乘積密碼的概念來逼近理想的分組密碼,即依次執行兩個或多個簡單密碼。

替代:每個明文元素或元素組唯一地被相應的密文元素或元素組替換。

排列:明文元素的序列被該序列的不同排列替換。 也就是說,序列中沒有元素被添加、刪除或替換,而是元素在序列中出現的順序發生了變化。

Feistel 加解密:

數據加密標準 Data Encryption Standard (DES):

一種用於加密數字數據的對稱密鑰算法,於 1970 年代初在 IBM 開發。儘管其 56 bits 的短密鑰長度使其對應用程序來說太不安全,它對密碼學的進步產生了很大的影響。

Key size – 56 bits; Block size – 64 bits

DES 算法:

DES 加密:

Electronic Codebook (ECB):

消息被分成獨立的區塊,這些區塊被加密。每個區塊都是一個被替換的值,就像一個密碼本,因此得名。每個區塊都獨立於所有其他區塊。

Ci = De (Pi, K1)

ECB 的優劣:

  • 重複的消息可以反映在密文中。
  • 如果與消息區塊對齊,特別是與圖形等數據或變化很小的消息對齊,這將造成密碼書分析問題。
  • 加密消息區塊是獨立的,這是一個弱點。
  • 因此只有真正發送幾個數據區塊時很有用。

Cipher Block Chaining (CBC):

消息被分成多個區塊,但它們在加密操作中是聯繫在一起的。密碼區塊與明文鏈接,因此得名。

使用已知的初始向量 (IV) 開始流程:

  • Ci= En (Pi⊕Ci-1, K1)
  • C0= IV

CBC 的優劣:

  • 每個密文區塊依賴於它之前的所有消息區塊。
  • 最常見的使用模式,消息的更改會影響密文區塊以及原始區塊。
  • 爲了開始流程,需要一個初始值 (IV),發送方和接收方都必須知道它,IV 必須是一個固定值,或者它可以在消息的其餘部分之前以 ECB 模式加密發送。
  • 在消息的最後,必須處理一個可能出現的最後一個短區塊:用 pad 填充最後一個區塊(通常帶有填充大小的計數),Eg. [b1 b2 b3 0 0 0 0 5] ➔3 data bytes and 5 bytes pad+count

Cipher Feedback (CFB):

消息被視為添加到分組密碼輸出的比特流,結果是下一階段的反饋(因此得名)。標准允許反饋任意數量的 bit(1、8 或 64 或其他),表示為 CFB-1、CFB-8、CFB-64 等。

在實踐中,利用所有 64 位是最有效的,稱之為 CFB-64:

  • Ci= Pi⊕Sig(En(Pi, K1))
  • C0= IV

CFB 的優劣:

  • 當數據固有地以位/字節到達時適用。
  • 最常見的流模式。
  • 分組密碼在兩端都用於加密模式。
  • 錯誤在發生之後仍傳播了幾個區塊。

Output Feedback (OFB):

消息被視為比特流,分組密碼的輸出被添加到消息中, 然後反饋(因此得名)輸出,因此反饋與消息無關。

可以提前計算:

  • Ci= Pi⊕Oi
  • Oi= Si( En(Oi-1, K1))
  • O0= IV

OFB 的優劣:

  • 用於錯誤反饋有問題的地方。
  • 沒有錯誤傳播,或者在消息可用之前必須進行加密。
  • 與 CFB 類似,但反饋來自分組密碼的輸出,並且與消息無關。
  • 永遠不要重複使用相同的序列(key + IV),發送者和接收者必須保持同步,並且需要一些恢復方法來確保這種情況發生。
  • 雖然最初在標準中指定了 m-bit 的反饋,但隨後的研究表明,應該只使用 64 位 OFB,這是最有效的方式。

Categories
All 算法

做題筆記:Remove Duplicates from Sorted Array (Java)

今後 Easy 題若沒碰到非常精彩的就不每題都更了。

題目描述:

給定一個按非遞減順序排序的整數數組 nums,就地刪除重複項,使每個單獨的元素只出現一次。元素的相對順序應保持不變。

由於在某些語言中無法更改數組的長度,因此您必須將結果放在數組 nums 的前半部分。更正式地說,如果刪除重複項後有 k 個元素,則 nums 的前 k 個元素應持有最終結果。除了前 k 個元素之外,留下什麼都不重要。

將最終結果放入 nums 的前 k 個插槽後返回 k值。

Example 1:

Input: nums = [1,1,2]
Output: 2, nums = [1,2,_]
Explanation: Your function should return k = 2, with the first two elements of nums being 1 and 2 respectively.
It does not matter what you leave beyond the returned k (hence they are underscores).

Example 2:

Input: nums = [0,0,1,1,1,2,2,3,3,4]
Output: 5, nums = [0,1,2,3,4,_,_,_,_,_]
Explanation: Your function should return k = 5, with the first five elements of nums being 0, 1, 2, 3, and 4 respectively.
It does not matter what you leave beyond the returned k (hence they are underscores).

Stack 解法:

自己還是只能想出來蠢方法,應該刷題量還遠遠不夠吧:

class Solution {
    public int removeDuplicates(int[] nums) {
		Stack<Integer> Stack = new Stack<Integer>();
		int k = 0;
		for (int i = 0; i < nums.length; i++) {
			if (Stack.search(nums[i])==-1) {
				Stack.push(nums[i]);
				nums[k] = nums[i];
				k++;
			} 
		}
		return k;
	}
}

7 ms

利用順序的取巧方法:

class Solution {
    public int removeDuplicates(int[] nums) {
        int i = 0;
        for(int n: nums){
            if(i == 0 || n > nums[i - 1]){
                nums[i] = n;
                i++;
            }
        }
        return i;
    }
}

1 ms

Categories
All 算法

做題筆記:Merge Two Sorted Lists (Java)

題目描述:

給定兩個排好序的 linked list list1list2 的 head。

將前兩個 linked list 的節點拼接在一起合併到一個完成排序的 linked list 中。

返回合併之後的鏈結串列的 head。

Example 1:

Input: list1 = [1,2,4], list2 = [1,3,4]
Output: [1,1,2,3,4,4]

Example 2:

Input: list1 = [], list2 = []
Output: []

Example 3:

Input: list1 = [], list2 = [0]
Output: [0]

解法:

class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode result = new ListNode();
        ListNode head = result;
        while (true){
            if (list1==null){
                result.next=list2;
                break;
            }
            if (list2==null){
                result.next=list1;
                break;
            }
            if (list1.val<=list2.val){
                result.next=list1;
                list1=list1.next;
            }
            else {
                result.next=list2;
                list2=list2.next;
            }
            result=result.next;
        }
        return head.next;
    }
}

1 ms

Categories
算法 All

做題筆記:Valid Parentheses (Java)

題目描述:

給定一個僅包含字符 '(', ')', '{', '}', '['']' 的字符串 s,確定輸入字符串是否有效。

有效的情況指:

  1. 前括號必須用相同類型的括號閉合。
  2. 前括號必須以正確的順序閉合。

Example 1:

Input: s = "()"
Output: true

Example 2:

Input: s = "()[]{}"
Output: true

Example 3:

Input: s = "(]"
Output: false

暴力解法:

昨天半夜擼的,還是想不到什麽好點子:

class Solution {
    public boolean isValid(String s) {
        StringBuilder sb = new StringBuilder();
        sb.append(s);
        
        for(int i = 0; i<sb.length()-1; i++ ){
            if ( (sb.charAt(i) == '(' && sb.charAt(i+1) == ')') || 
                 (sb.charAt(i) == '[' && sb.charAt(i+1) == ']') || 
                 (sb.charAt(i) == '{' && sb.charAt(i+1) == '}') 
               ){
                sb = sb.deleteCharAt(i+1);
                sb = sb.deleteCharAt(i);
                i = -1;
            }
        }
        if (sb.toString() == ""){
            return true;
        }
        else{
            return false;
        }
    } 
}

79 ms,慘不忍睹。

Stack 解法:

Hint 中已經提到,應該使用 Stack :

class Solution {
    public boolean isValid(String s) {
	   Stack<Character> stack = new Stack<Character>();
	   for (char c : s.toCharArray()) {
		   if (c == '(')
			   stack.push(')');
		   else if (c == '{')
			   stack.push('}');
		   else if (c == '[')
			   stack.push(']');
		   else if (stack.isEmpty() || stack.pop() != c)
			   return false;
	   }
	   return stack.isEmpty();
    }
}

2 ms

Categories
算法 All

做題筆記:Longest Common Prefix (Java)

還滿難的一道題。

題目描述:

編寫一個函數來查找字符串數組中最長的共有前綴字符串。

如果沒有共有前綴,則返回一個空字符串""

Example 1:

Input: strs = ["flower","flow","flight"]
Output: "fl"

Example 2:

Input: strs = ["dog","racecar","car"]
Output: ""
Explanation: There is no common prefix among the input strings.

Horizontal scanning:

自己嘗試的方法:

class Solution {
    public String longestCommonPrefix(String[] strs)
    {
        int size = strs.length;
        if (size == 0)
            return "";

        if (size == 1)
            return strs[0];

        Arrays.sort(strs);
        int end = Math.min(strs[0].length(), strs[size-1].length());
        int i = 0;
        while (i < end && strs[0].charAt(i) == strs[size-1].charAt(i) )
            i++;
        String pre = strs[0].substring(0, i);
        return pre;
    }
}

1 ms

Vertical scanning:

Solution 中提供的方法二:

public String longestCommonPrefix(String[] strs) {
    if (strs == null || strs.length == 0) return "";
    for (int i = 0; i < strs[0].length() ; i++){
        char c = strs[0].charAt(i);
        for (int j = 1; j < strs.length; j ++) {
            if (i == strs[j].length() || strs[j].charAt(i) != c)
                return strs[0].substring(0, i);             
        }
    }
    return strs[0];
}

Divide and conquer:

Solution 中提供的方法三:

public String longestCommonPrefix(String[] strs) {
    if (strs == null || strs.length == 0) return "";    
        return longestCommonPrefix(strs, 0 , strs.length - 1);
}

private String longestCommonPrefix(String[] strs, int l, int r) {
    if (l == r) {
        return strs[l];
    }
    else {
        int mid = (l + r)/2;
        String lcpLeft =   longestCommonPrefix(strs, l , mid);
        String lcpRight =  longestCommonPrefix(strs, mid + 1,r);
        return commonPrefix(lcpLeft, lcpRight);
   }
}

String commonPrefix(String left,String right) {
    int min = Math.min(left.length(), right.length());       
    for (int i = 0; i < min; i++) {
        if ( left.charAt(i) != right.charAt(i) )
            return left.substring(0, i);
    }
    return left.substring(0, min);
}

Binary search:

Solution 中提供的方法四:

public String longestCommonPrefix(String[] strs) {
    if (strs == null || strs.length == 0)
        return "";
    int minLen = Integer.MAX_VALUE;
    for (String str : strs)
        minLen = Math.min(minLen, str.length());
    int low = 1;
    int high = minLen;
    while (low <= high) {
        int middle = (low + high) / 2;
        if (isCommonPrefix(strs, middle))
            low = middle + 1;
        else
            high = middle - 1;
    }
    return strs[0].substring(0, (low + high) / 2);
}

private boolean isCommonPrefix(String[] strs, int len){
    String str1 = strs[0].substring(0,len);
    for (int i = 1; i < strs.length; i++)
        if (!strs[i].startsWith(str1))
            return false;
    return true;
}
Categories
All 技術 算法

做題筆記:Roman to Integer (Java)

題目描述:

將羅馬數字轉換為整數,具體規則略。

Example 1:

Input: s = "III"
Output: 3
Explanation: III = 3.

Example 2:

Input: s = "LVIII"
Output: 58
Explanation: L = 50, V= 5, III = 3.

Example 3:

Input: s = "MCMXCIV"
Output: 1994
Explanation: M = 1000, CM = 900, XC = 90 and IV = 4.

自己的 Switch 暴力解法:

兜了好幾個圈……

class Solution {
    public int romanToInt(String s) {
        int num = 0;
        StringBuilder re = new StringBuilder();
        re.append(s).reverse();
        String[] split = re.toString().split("(?<=\\G..)");
        for (String i:split){
            switch(i){
                case "VI":
                    num = num + 4;
                    break;
                case "XI":
                    num = num + 9;
                    break;
                case "LX":
                    num = num + 40; 
                    break;
                case "CX":
                    num = num + 90; 
                    break;
                case "DC":
                    num = num + 400;
                    break;
                case "MC":
                    num = num + 900; 
                    break;
                default:
                    String[] sub = i.split("");
                    for (String j:sub){
                        switch(j){
                            case "I":
                                num = num + 1;
                                break;
                            case "V":
                                num = num + 5;
                                break;
                            case "X":
                                num = num + 10; 
                                break;
                            case "L":
                                num = num + 50; 
                                break;
                            case "C":
                                num = num + 100;
                                break;
                            case "D":
                                num = num + 500; 
                                break;
                            case "M":
                                num = num + 1000;     
                                break;
                        }
                    }
                }
        }
        return num;    
    }
}

意外發生了,本地 IDE 能跑出正確答案,但是在 Leetcode 上跑不出來,我竟然為這種問題浪費了不少時間,最終也沒解決……

正確精練的 Switch 解法:

class Solution {
	 public int romanToInt(String s) {
	    int nums[] = new int[s.length()];
	    for(int i = 0; i < s.length(); i++){
	        switch (s.charAt(i)) {
	            case 'M':
	                nums[i] = 1000;
	                break;
	            case 'D':
	                nums[i] = 500;
	                break;
	            case 'C':
	                nums[i] = 100;
	                break;
	            case 'L':
	                nums[i] = 50;
	                break;
	            case 'X' :
	                nums[i] = 10;
	                break;
	            case 'V':
	                nums[i] = 5;
	                break;
	            case 'I':
	                nums[i] = 1;
	                break;
	        }
	    } 
	    
	    int sum = 0;
	    for(int i=0; i<nums.length-1; i++){
	        if(nums[i] < nums[i+1])
	            sum -= nums[i];
	        else
	            sum += nums[i];
	    }
         
	    return sum + nums[nums.length-1];
	}
}

6 ms

運用 Hashmap:

class solution {
    public int romanToInt(String s) {
        Map map = new HashMap<>();
        map.put('I',1);
        map.put('V',5);
        map.put('X',10);
        map.put('L',50);
        map.put('C',100);
        map.put('D',500);
        map.put('M',1000);
        
        int result = map.get(s.charAt(s.length()-1));
        for(int i=s.length()-2; i>=0; i--){
            if(map.get(s.charAt(i)) < map.get(s.charAt(i+1))){
                result -= map.get(s.charAt(i));
            }
            else{
                result += map.get(s.charAt(i));
            }
        }
        return result;
    }
}

Categories
All 算法

做題筆記:Palindrome Number (Java)

題目描述:

給定一個整數 x,如果 x 是回文數,則返回 true

回文數指一個整數向後讀和向前讀内容相同。

Example 1:

Input: x = 121
Output: true
Explanation: 121 reads as 121 from left to right and from right to left.

Example 2:

Input: x = -121
Output: false
Explanation: From left to right, it reads -121. From right to left, it becomes 121-. Therefore it is not a palindrome.

Example 3:

Input: x = 10
Output: false
Explanation: Reads 01 from right to left. Therefore it is not a palindrome.

算術解法:

利用 Java 整數倒序計算:

class Solution {
    public boolean isPalindrome(int x) {
        if (x < 0) {
            return false;
        }
        
        int ori = x;
        int p = 0;
        while (ori != 0) {
            p = p * 10 + ori % 10;
            ori /= 10;
        }
        
        return x == p;
    }
}

9 ms,超過 87.67% 的答案。

轉換為 String 的解法:

進階要求 Follow Up 竟然問了一句 Could you solve it without converting the integer to a string? 原來我想到的已經是進階方法了,常規解法是轉換為 String:

class Solution {
     public boolean isPalindrome(int x) {
         char[] nums = String.valueOf(x).toCharArray();
         int start = 0;
         int end = nums.length-1;
         while(start < end) {
             if(nums[start] != nums[end]) return false;
             start++; end--;
         }
        return true;
    }
}

19 ms,超過 19.48% 的答案。

Categories
All 算法

做題筆記:Two Sum (Java)

驚恐自己要找不到工作了,開始做題,記錄一下做題筆記。第一道,是刷題界的 Hello World —— Two Sum。

題目描述:

給定一個整數數組 nums 和一個整數 target,返回兩個數字的索引,使它們相加為 target

假設每個輸入都只有一個解決方案,並且不會使用相同的元素兩次。

可以按任何順序返回答案。

Example 1:

Input: nums = [2,7,11,15], target = 9
Output: [0,1]
Explanation: Because nums[0] + nums[1] == 9, we return [0, 1].

Example 2:

Input: nums = [3,2,4], target = 6
Output: [1,2]

Example 3:

Input: nums = [3,3], target = 6
Output: [0,1]

Foreach 暴力解法:

第一次刷題,算法小白,只想得到這種方式:

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int [] empty = new int[2];
        int len = nums.length;
        for(int i = 0; i < len; i++){
            for(int j = 0; j < len; j++){
                    if (nums[i]+nums[j] == target&& i != j){
                        empty[0] = i;
                        empty[1] = j;
                        return empty;
                    }
            }
        }
        return null;
    }
}

果不其然,106 ms,超過了10%的答案,好歹是通過了對吧……

以前上課做作業時,都是秉持著能跑就行的態度,習慣很差,現在我開始認真考慮優化的問題了,也算是一大進步。

Hashmap 解法:

看了 discussion 之後,追求高效率,這道題是應該使用 Hashmap 的:

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int[] result = new int[2];
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        for (int i = 0; i < numbers.length; i++) {
            if (map.containsKey(target - numbers[i])) {
                result[1] = i;
                result[0] = map.get(target - numbers[i]);
                return result;
            }
        map.put(numbers[i], i);
    }
    return result;
}
}

3 ms,超過85%的結果。

Categories
All 密碼學 技術

密碼學(1):古典密碼

前言:一點廢話

程序猿的博客好像都是用來寫技術文的,只有我一直在鍵政,所以心血來潮想寫技術文試一試。我對 WordPress 加上目前正在使用的前端主體的表現并沒有信心,像代碼高亮之類的操作也無法處理的很好,所以準備先談談比較理論少代碼的密碼學這個主題,這也是個人比較有興趣的一個領域。

古典密碼(Classical cipher),顧名思義,就是歷史上曾使用的一些加密方式,大多已不再使用。不過介紹它們有助於我們瞭解密碼學的發展歷史以及認識一些基本的加密思路。古典密碼主要有替換式密碼和移項式密碼兩大類。

替換式密碼

替換式密碼(Substitution ciphers),意指明文中的内容通過字母或字母群被系統性地替換爲其他内容,形成密文。

凱撒密碼

凱撒密碼(Caesar cipher)是最爲人熟知的一種替換式密碼,最早出現在 Gallic wars 中用於軍事目的。它還有另一個名字叫 Caesar shift,意思就很明顯了——通過移位(shift)來達成替換的目的。比如一串明文”ABC”,用”D”替代”A”、”E”替代”B”、”F”替代”C”,則會得到密文”DEF”,我們可以理解為,明文「右」移了三位得到了密文。

多説無益,直接看一個例子:

Plain: I like zttofficial
Cipher: L olnh cwwriilfldo

原文中的每個字母都向右移動了三位,從而產生了密文,替換過程遵循下表:

基於此,我們可以用數學方式表達明文與密文之間的關係:

C = E(k, p) = (p + k) mod 26
p = D(k, C) = (C - k) mod 26

凱撒密碼總共只有25種移位方法,很容易通過暴力(Brute Force)破解,簡簡單單枚舉所有可能的情況便能得到明文。

單表加密

單表加密(monoalphabetic cipher)嚴格定義上是凱撒密碼的母集,即凱撒密碼也是單表加密的一種。不過此處語境下的單表加密,是指對凱撒密碼的一種改良方式,它對整個明文進行替換,而不是替換逐個字母,這意味著單表加密需要一個與明文相同長度的密鑰。

Plain: if we wish to replace letters
Key: dkvqfibjwpescxhtmyauolrgznwi
Cipher: wi rf rwaj uh yftsdvf sfuufya

雖然排列組合的可能性變得非常多,但仍然是風險很高的一種加密方式,因爲在自然語言中一些詞的頻率必然會高於另外的詞,可以通過頻率分析(frequency analysis)進行破解。

維吉尼亞密碼

相較之下,維吉尼亞密碼(Vigenère Cipher)則是多表加密(polyalphabetic cipher)的一個典型例子,可以視之為多個凱撒密碼的組合,以下這個被稱爲維吉尼亞方格的表格被應用在這種加密方法中:

此處簡單引用一下維基中使用到的例子:

Plain: I LOVE CRYPTOGRAPHY
Key: W ORDW ORDWORDWORDW
Cipher: E ZFYA QIBLHFJNOGKU

波雷費密碼

波雷費密碼(Playfair cipher)是最爲著名的一種多字母加密方法。它將明文視爲單個整體的單元放在一幅圖中,并將整個單元轉換爲密文圖,此處的「圖」通常是指一個5×5的矩陣。

選擇密鑰之後,將密鑰的字母逐個填入該5×5的矩陣內,字母不能重複,若遇重複則跳過。填完密鑰之後講剩餘的英文字母依順序填入,因爲一共只有25個空間,一般會將Q去除或將I和J視作同一字(例子使用後者)。

加密時,應該把所有字母分爲兩個一組,如果最後有一個字母落單,則添上X作爲搭配。然後按以下規則,尋找每兩個字母對應的密文以實現加密:

  1. 如果兩個字母并非同行同列,將四個字母組成矩形,取明文對角處的字母作爲密文。
  2. 如果兩個字母在同一橫列,取這兩個字母右方的字母(若字母在最右方則取最左方的字母)。
  3. 如果兩個字母在同一直行,取這兩個字母下方的字母(若字母在最下方則取最上方的字母)。

現在假設明文是”Nice to meet you”,首先兩兩分組,得到”NI CE TO ME ET YO UX”。假設用 Monarchy 一詞作爲密鑰,按上述規則填入:

我們就能得到密文:AGELPRCLKLHNVZ。

移項式密碼

移項式密碼不改變明文本身,而是依照某種規則改變明文中的排列。

中國式密碼

中國式密碼(Chinese cipher)的規則是將明文按從右往左、上下交替寫出一段新的字母排列。例如明文是:THE DOG RAN FAR,則按此規則可以寫為:

 R R G T
 A A O H  
 F N D E  

密文則按照慣用的從左往右、從上往下的閲讀順序寫作:RRGT AAOH FNDE。

下一篇:密碼學(2): Modern Block Ciphers