Substitution-Cipher

如何測試我的加密?(絕對業餘)

  • February 28, 2016

我是一名具有生物學背景的業餘程序員,並開發了基於 DNA 的加密程序。我試圖讓它難以破解,但它本質上是一種替代密碼,並使用預設的 Java 隨機數生成器,所以我猜它可以相對容易地被破解。但是我如何才能知道我的加密有多好呢?我可以在這裡發布一條加密消息,看看是否有人可以破解它?

再說一次,我不是專業的密碼學家或程序員,我是一名研究生,在實驗室之外做了太多的事情,比如嘗試編寫加密程序,所以如果已經有關於這個的問題,我不知道,因為我不知道不理解我在類似問題中看到的任何術語。

這是我的程式碼:

import java.util.Random;
import java.util.ArrayList;
import java.util.HashMap;
public class GenenCrypt {

 private Random ranGen;
 private Random coinFlip;
 private String[] bases;
 private ArrayList<String> originalCodonList;
 private ArrayList<String> shuffledCodonList;
 private String[] charList;
 private HashMap<String,String[]> codonTable;
 private HashMap<String, String> decryptTable;
 private String key;



 public GenenCrypt(String key){



   // define the initial, unshuffled codon list of 4 base codons
   originalCodonList = new ArrayList<String>();
   bases = new String[]{"A", "T", "G", "C"};
   for(int i = 0; i < 4; i++){ 
     for(int j = 0; j < 4; j++){
       for(int k = 0; k < 4; k++){
         for(int l = 0; l < 4; l++){
           originalCodonList.add("" + bases[i] + bases[j] + bases[k] + bases[l]);
         }
       }
     }
   }

   // make a random number generator with a seed based on the key
   this.key = key;
   ranGen = new java.util.Random(makeKey(key));
   coinFlip = new java.util.Random(makeKey(key));

   // use the random number generator and the originalCodonList to make a shuffled list
   shuffledCodonList = new ArrayList<String>();
   while(originalCodonList.size() > 0){
     int index = ranGen.nextInt(originalCodonList.size());
     shuffledCodonList.add(originalCodonList.get(index));
     originalCodonList.remove(index);
   }

   // define the characters that can be encoded, 64 in total
   // 26 capital letters
   // 10 digits
   // space, newline, and tab
   // the symbols . , ? " ! @ # $ % ^ & * ( ) - + = / _ \ : ; < >
       charList = new String[]{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", " ", "\t", "\n", ".", ",", "?", "\"", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "-", "+", "=", "/", "_", "\\", ":", ";", "<", ">", "|"};

   // define the codon table to encode text
   codonTable = new HashMap<String, String[]>();
   for(int i = 0; i < charList.length; i++){
     String[] tempArray = new String[]{shuffledCodonList.get(4 * i), shuffledCodonList.get(4 * i + 1), shuffledCodonList.get(4 * i + 2), shuffledCodonList.get(4 * i + 3)};
     //System.out.println(i);
     codonTable.put(charList[i], tempArray);
   }

   // define the decryption table
   decryptTable = new HashMap<String, String>();
   for(int i = 0; i < codonTable.size(); i++){
     String s = charList[i];
     String[] sa = codonTable.get(s);
     decryptTable.put(sa[0], s);
     decryptTable.put(sa[1], s);
     decryptTable.put(sa[2], s);
     decryptTable.put(sa[3], s);
   }


 }

 public void printShuffledList(){
   for(int i = 0; i < shuffledCodonList.size(); i++){
     System.out.println(shuffledCodonList.get(i));
   }
 }
 public void printOriginalList(){
   for(int i = 0; i < originalCodonList.size(); i++){
     System.out.println(originalCodonList.get(i));
   }
 }
 public void printCodonTable(){
   // print the codon table
   for(int i = 0; i < codonTable.size(); i++){
     String s = charList[i];
     String[] sa = codonTable.get(s);
     if(s == "\t"){
       System.out.println(i + "\t" + "\\t" + "\t" + sa[0] +", "+ sa[1] +", "+ sa[2] +", "+ sa[3]);
     } else if(s == "\n"){
       System.out.println(i + "\t" + "\\n" + "\t" + sa[0] +", "+ sa[1] +", "+ sa[2] +", "+ sa[3]);
     } else if(s == " "){
       System.out.println(i + "\t" + "\" \"" + "\t" + sa[0] +", "+ sa[1] +", "+ sa[2] +", "+ sa[3]);
     } else{
       System.out.println(i + "\t" + s + "\t" + sa[0] +", "+ sa[1] +", "+ sa[2] +", "+ sa[3]);
     }
   }
 }

 public String encrypt(String input){
   String output = "";
   for(int i = 0; i < input.length(); i++){
     // insert junk bases
     int offset  = ((int)key.charAt(i % key.length()))%100;
     String junk = "";
     for(int j = 0; j < offset; j++){
       junk += bases[ranGen.nextInt(4)];
     }
     output += junk;
     int comp = coinFlip.nextInt(2);


     int choose = ranGen.nextInt(4);

     String s = ("" + input.charAt(i)).toUpperCase();
     if(codonTable.containsKey(s)){
       String[] sa = codonTable.get(s);
       if(comp == 0){
         output += sa[choose];
       }else{
         output += complement(sa[choose]);
       }
     }
   }
   // add some junk bases to the end of the cipher text
   int offset = ((int)key.charAt(input.length() % key.length()))%100;
   // add bases to make the total length a mutliple of 4
   offset += (output.length() + offset) % 4;
   String junk = "";
   for(int j = 0; j < offset; j++){
     junk += bases[ranGen.nextInt(4)];
   }
   output += junk;

   // reset the random number generators
   ranGen.setSeed(makeKey(key));
   coinFlip.setSeed(makeKey(key));
   return output;
 }

 public String decrypt(String in){
   String input = "" + in;
   String output = "";
   int keyCount = 0;
   int junk = ((int)key.charAt(keyCount % key.length()))%100;
   while(input.length() > junk + 4 ){
     // cuts out the junk bases
     input = input.substring(junk);
     // get the codon, decrypt the codon, remove it from the input string
     String codon = input.substring(0, 4);

     int comp = coinFlip.nextInt(2);


     if(comp == 1){
       codon = complement(codon);
     }

     output += decryptTable.get(codon);
     input = input.substring(4);
     // increment the key counter and update junk
     keyCount++;
     junk = ((int)key.charAt(keyCount % key.length()))%100;
   }
   //reset the random number generators
   ranGen.setSeed(makeKey(key));
   coinFlip.setSeed(makeKey(key));
   return output;
 }

 private String complement(String in){
   String out = "";
   for(int i = 0; i < in.length(); i++){
     switch(in.charAt(i)){
       case 'A':   out+= 'T';
             break;
       case 'T':   out+= 'A';
             break;
       case 'G':   out+= 'C';
             break;
       case 'C':   out+= 'G';
             break;
       default:    out+= in.charAt(i);
             break;
     }
   }
   return out;
 }

 private long makeKey(String k){
   long longKey = 0;
   for(int i = 0; i < key.length(); i++){
     longKey += (int)key.charAt(i);
   }
   return longKey;
 }

 public static void main(String[] args){

   String plaintext = "This is the plaintext";
   String key = "this is the key";

   GenenCrypt gc1 = new GenenCrypt(key);

   System.out.println("Encrypting the line \"" + plaintext + "\"");
   System.out.println();
   String encrypted = gc1.encrypt(plaintext);
   System.out.println(encrypted);
   System.out.println();
   System.out.println("Decrypting the ciphertext");
   System.out.println(gc1.decrypt(encrypted));




 }
}

這些評論可能不足以理解我在做什麼。首先,您應該了解一點 DNA 的工作原理。有 4 個鹼基,A、G、C 和 T。DNA 編碼蛋白質,由 20 個氨基酸組成。自從 $ 4^1 $ = 4,並且 $ 4^2 $ = 16,我們需要 $ 4^3 $ , 用於 3 個鹼基的 64 種可能組合。這 3 個鹼基單位稱為密碼子。由於 64 大於 20,大多數氨基酸由 1 個以上的密碼子編碼,3 個密碼子是終止密碼子,只是標記蛋白質的結束位置。

但是 20 個符號不足以加密一條消息,我認為 64 個符號就可以了,這給了我所有的字母(僅限大寫)、所有的數字和大部分標點符號。我還希望每個符號由超過 1 個密碼子表示,因此我使用了 4 個鹼基密碼子而不是 3 個鹼基密碼子,它提供了 256 種可能的組合。所以我為每個符號分配了 4 個隨機的 4 個鹼基密碼子。

DNA的另一個概念是閱讀框。DNA 雙鏈有 6 種可能的方式來翻譯蛋白質,3 種正向和 3 種反向,這取決於您是從任一端的第一個、第二個或第三個鹼基開始。為了弄亂我加密消息中的閱讀框,我在每個密碼子之間插入了隨機數量的隨機鹼基。此外,每個密碼子有 50% 的機會反轉為其補碼,因此 A 變為 T,G 變為 C,以此類推。

這意味著為了成功解密一條消息,您需要找到每個符號的所有 4 個密碼子,並整理出垃圾,並確定哪些密碼子已被反轉。更複雜的是,您可以使用凱撒密碼或其他簡單的加密方法對密文進行加密,使其看起來像您擁有超過 4 個字元並偽裝 DNA。或者,您可以採用隱藏在視線範圍內的方法,並將消息發佈到任意數量的公開可用的 DNA 數據庫中。

根據您的範常式式碼,我認為該方案對於實施來說不夠安全。此外,如果您實際嘗試實現此功能以生成其中包含加密消息的 DNA 鏈(例如某種未來派科幻驚悚片),您將遇到一些問題。

正如另一個答案所暗示的,最好考慮使用 DNA 序列作為數據儲存層。這將允許儲存任何類型的加密或明文數據。隨著技術的進步,生成定制 DNA 鏈的成本只會下降,在 20 年內這可能是一種司空見慣的方法。

問題 1:冗餘 如果您生成一條 DNA 鏈,將其註入任何東西,在一段時間內將其運送到幾個大陸,然後嘗試閱讀它……它很可能會被解釋為其他東西。您將需要大量冗餘以確保鏈的退化和轉錄錯誤不會破壞加密數據。有些物種編碼了幾十個甚至幾百個相同的序列,這樣它就可以完整地生存下來。至少,您需要一種非常健壯的輸入數據編碼方法,該方法考慮到鏈所暴露的時間和環境。

問題 2:另一面 DNA 鏈有 2 個面。一側的 A 是另一側的 T。如果您使用所有 4 個鹼基進行編碼,則在讀取鏈時(可能)會遇到問題。最簡單的解決方案是使用鹼基對作為二進制值而不是密碼子,AT 和 CG 作為 0 和 1。這簡化了核苷酸編碼算法(現在沒有!)並允許從任何一側(不是任何一端)讀取) 沒有確定哪一側是正確的那一側(也許通過一些終止序列)

問題3:另一端正 如你所說,從一端讀取可能是一個問題,其中有2個解決方案。首先是對序列進行編碼,然後反轉和追加。這使得股線在兩個方向上都相同。另一種解決方案是使用某種終止序列來確定從哪一端讀取鏈。

問題 4:錯誤的程式碼 您不想意外地為肉毒桿菌毒素或其他東西編碼,這可能不是問題,除非您實際上生成的 DNA 鏈可能最終會暴露於活的有機體中。這可以通過使用僅使用產生相同氨基酸的密碼子的編碼來解決。長序列的精氨酸不會自發產生肉毒桿菌毒素。精氨酸有 6 個密碼子可以創建它,提供多種編碼選項。問題 2 的簡單解決方案可能對這個問題的解決方案不太友好,儘管 CGC 和 CGG 都編碼精氨酸,而另一方面 GCG 和 GCC 都編碼丙氨酸,因此您可以在鏈的任一側編碼二進制數據讓它產生一長串相同的氨基酸!

使用一個密碼子只編碼 1 位數據,並且需要幾個密碼子來實現遺傳冗餘和幾個位來實現數據冗餘,這將很快加起來。我可以看到 5 個重複密碼子需要 3 個匹配才能編碼一個位(每位 15 個鹼基對),然後是一個 8x4 漢明碼,它需要 16 個位來編碼一個字節(每個字節 480 個鹼基對!!),還有一些終止每端的程式碼以確保以正確的方式讀取(每端另外 100 個左右的鹼基對)。缺點是大量的 DNA,優點是高度冗餘的儲存,易於讀取並且不會意外創建生物武器。

至於實際的密碼學部分,最好的方法是壓縮您的輸入數據,然後使用 AES_CTR 之類的東西(如果不需要身份驗證)並使用該數據生成 DNA 程式碼。乍一看,您使用的加密方法似乎是一種簡單的替換,從已知的明文和算法知識中找出它並不困難。

DNA數字數據儲存

我還希望每個符號由超過 1 個密碼子表示,因此我使用了 4 個鹼基密碼子而不是 3 個鹼基密碼子,它提供了 256 種可能的組合。所以我為每個符號分配了 4 個隨機的 4 個鹼基密碼子。

使用這種編碼,您最終將代表每四個密碼子一個字節。

目前大多數加密算法都使用比特組,通常要求組需要是 8 位、64 位或 128 位的倍數。這樣的組可以用 4 個密碼子、32 個密碼子等來表示。

DNA數字數據儲存是指最近在DNA上儲存數字資訊的一些實驗。在 DNA 上儲存數據的概念並不新鮮。DNA 有一個重要優勢:數據儲存密度比目前商用的旋轉磁碟和固態磁碟儲存解決方案要好幾個數量級。因此,它是一個有趣的研究對象。它目前沒有作為儲存設備在商業上使用的主要原因是寫入和讀取 DNA 非常昂貴。


考慮:

  1. 當將 DNA 視為二進制數據儲存介質(每個密碼子 2 位)時,很明顯,您實際上可以使用任何目前經過良好測試的加密算法(如 AES 和 RSA)來處理資訊以儲存在 DNA 上。
  2. 經典的密碼機制通常不能提供足夠強大的安全性來抵禦目前的攻擊者。

結果:我建議嘗試重新劃分問題:將 DNA 視為儲存層,將密碼學視為另一層,並在每一層上應用可用的最佳解決方案。

引用自:https://crypto.stackexchange.com/questions/11614