2015-04-19 237 views
2

我需要一个简单的c#算法,该算法将采用一串x字符并将其'加密'为另一个x字符串。它不必是安全的,但不应该只通过查看加密字符串来重建原始字符串。例如,如果我输入“你好”,我应该得到像“x = w3q”的东西。它不应该简单地将每个角色映射到其他角色,但它不必比这更复杂。它需要是对称的,所以我需要能够从“x = w3q”构造“hello”。加密字符串以匹配原始字符串的长度

到目前为止,我已经尝试过RijndaelManaged和RSA加密,但加密的字符串比原始字符串长很多。

任何想法?

+2

ROT13始终是一种选择! :-) http://en.wikipedia。org/wiki/ROT13 – xanatos

+0

目前尚不清楚您是否想将“人类可读”字符串加密为另一个“人类可读”字符串。你将'hello'转换为'x = w3q',但实际上加密通常是从字节转换为字节,所以它会将'hello'(UTF8)转换为5个随机不可显示的字节。出于这个原因,你经常base64加密的字符串,使其再次可读...但base64放大字符串。 – xanatos

+0

ROT13可以通过简单地查看来轻松重建。 – Amandil

回答

3

您可以使用ROT13算法开始,然后根据以前的字符更改偏移量。

例: “你好”

ROT13为 'H' - > 'U'

由于U是字母表中的第21封信,你会在下次使用ROT21:

ROT8为'e' - >'z'

等等。

这不仅会保持长度不变,还可以处理额外的字符,只要您将它们添加到字母表。

我很抱歉,如果现在还不够清楚,我正在打电话。

编辑:

下面是一些代码,这将使很多更有意义:

static String alphabet = "abcdefghijklmnopqrstuvwxyz"; 

public static String encrypt(String originalString) 
{ 
    String returnString = ""; 
    int shift = alphabet.Length/2; 

    foreach (char c in originalString) 
    { 
     int nextIndex = alphabet.IndexOf(c) + shift; 

     if (nextIndex > alphabet.Length) 
      nextIndex = nextIndex - alphabet.Length; 

     returnString += alphabet[nextIndex]; 
     shift = alphabet.IndexOf(alphabet[nextIndex]); 
    } 

    return returnString; 
} 

public static String decrypt(String encryptedString) 
{   
    String returnString = ""; 
    int shift = alphabet.Length/2; 

    foreach (char c in encryptedString) 
    { 
     int nextIndex = alphabet.IndexOf(c) - shift; 

     if (nextIndex < 0) 
      nextIndex = alphabet.Length + nextIndex; // nextIndex is negative so we are decreasing regardless 

     returnString += alphabet[nextIndex]; 
     shift = alphabet.IndexOf(c); 
    } 

    return returnString; 
} 

的字母可以根据需要尽可能多的扩大。这并不安全,但它很简单,仅通过查看就不能轻易破译。

+0

谢谢,这是一个好主意。 – Johan

+0

在这里犯了一个错误,我应该使用'加密'字符来改变偏移量,而不是原来的,否则不可能恢复原始文本。 – Amandil

+0

即将编辑帖子。 – Amandil

1

您可以根据字符的索引映射到不同的字符,这只是使其不太明显。

private static readonly int[] charOffsets = new[]{30,26,39,...}; 

private static char EncryptChar(char c, int index) 
{ 
    return (char)(c + charOffests[index % charOffsets.Length]); 
} 

private static char DecryptChar(char c, int index) 
{ 
    return (char)(c - charOffests[index % charOffsets.Length]); 
} 

public static string Encrypt(string str) 
{ 
    return new string(str.Select((c, i) => EncryptChar(c, i))); 
} 

public static string Decrypt(string str) 
{ 
    return new string(str.Select((c, i) => DecryptChar(c, i))); 
} 

你可以用它调整,如果一点点,你只想要字母等,但你的想法,我希望。

+0

谢谢,希望有内置的东西,但猜测它太简单了! – Johan

0

一种选择是使用经典密码,如Vigenère。如果你准备接受字节,而不是可读的字符,那么RC4是一个更现代的选择。

Vigenère和RC4都不安全,不安全的商业级加密。为此你需要AES。对于相同的输出长度,您可以在CTR模式下尝试AES,但在这种情况下您需要单独发送密钥和随机数。

+0

它要复杂得多......提问者认为字符是unicode字符,所以2个字节。即使使用utf8,虽然'a'是1个字节,'è'是两个字节... – xanatos

+0

我和你一起关于Vigenère密码,但RC4和AES的描述完全忽略了编码和解码问题。没有充分解释nonce的问题,这对点击率至关重要。 –

2

如果你想要一个众所周知的(古典)密码这里是一个Vigenère的实现。

import java.util.EnumSet; 
import java.util.Set; 

/** 
* Simple object oriented implementation of the classical Vigenère cipher. 
* Note that Vigenère is - of course - not considered secure anymore. 
* 
* @author maartenb 
*/ 

public class Vigenère { 
    private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 
    private static final int LA = ALPHABET.length(); 

    /** 
    * Flags to be used for the Vigenère cipher. 
    */ 
    public enum Flag { 
     /** 
     * Do not encrypt/decrypt space characters but leave them in the same 
     * position. 
     */ 
     SKIP_SPACE; 
    } 

    private enum State { 
     INSTANTIATED, INITIALIZED; 
    } 

    private enum Operation { 
     ENCRYPT, DECRYPT; 

     public int direction() { 
      switch (this) { 
      case ENCRYPT: 
       return 1; 
      case DECRYPT: 
       return -1; 
      default: 
       throw new IllegalStateException(); 
      } 
     } 
    } 

    private State state = State.INSTANTIATED; 
    private String key; 
    private int lk; 
    private Set<Flag> flags; 

    /** 
    * Initializes the cipher with the given key. 
    * 
    * @param key 
    *   the key that can only use the characters in the alphabet 
    * @param flags 
    *   option flag parameters 
    * @throws IllegalArgumentException 
    *    if the key contains invalid characters 
    */ 
    public void init(final String key, final Set<Flag> flags) { 
     if (key == null || key.isEmpty()) { 
      throw new IllegalArgumentException("Key null or empty"); 
     } 

     this.lk = key.length(); 
     for (int i = 0; i < this.lk; i++) { 
      if (ALPHABET.indexOf(key.codePointAt(i)) == -1) { 
       throw new IllegalArgumentException("Key character nat index " 
         + i + " not in alphabet"); 
      } 
     } 
     this.key = key; 
     this.flags = flags; 
     this.state = State.INITIALIZED; 
    } 

    /** 
    * Encrypts the plaintext using the key set during initialization. 
    * 
    * @param plaintext 
    *   the plaintext, using the characters in the alphabet 
    * @return the ciphertext 
    * @throws IllegalStateException 
    *    if the cipher wasn't initialized 
    * @throws IllegalArgumentException 
    *    if the plaintext contains characters not in the alphabet 
    */ 
    public String encrypt(final String plaintext) { 
     if (this.state != State.INITIALIZED) { 
      throw new IllegalStateException("Not initialized"); 
     } 

     return crypt(plaintext, Operation.ENCRYPT); 
    } 

    /** 
    * Decrypts the ciphertext using the key set during initialization. 
    * 
    * @param ciphertext 
    *   the ciphertext, using the characters in the alphabet 
    * @return the plaintext 
    * @throws IllegalStateException 
    *    if the cipher wasn't initialized 
    * @throws IllegalArgumentException 
    *    if the ciphertext contains characters not in the alphabet 
    */ 
    public String decrypt(final String ciphertext) { 
     if (this.state != State.INITIALIZED) { 
      throw new IllegalStateException("Not initialized"); 
     } 

     return crypt(ciphertext, Operation.DECRYPT); 
    } 

    private String crypt(final String in, final Operation op) 
      throws IllegalStateException, IllegalArgumentException { 

     final StringBuilder out = new StringBuilder(in.length()); 

     // legend: c = character, k = key, o = offset, in = input, out = output 
     int skipped = 0; 
     for (int ino = 0; ino < in.length(); ino++) { 
      // get character (code point in Unicode) 
      final int inc = in.codePointAt(ino); 
      // skip space, if configured 
      if (inc == ' ' && this.flags.contains(Flag.SKIP_SPACE)) { 
       out.appendCodePoint(' '); 
       skipped++; 
       continue; 
      } 
      // get matching key character 
      final int kc = this.key.codePointAt(mod(ino - skipped, this.lk)); 
      final int kco = ALPHABET.indexOf(kc); 
      final int inco = ALPHABET.indexOf(inc); 
      if (inco == -1) { 
       throw new IllegalArgumentException(
         "Invalid character at offset " + ino); 
      } 

      // the main calculation 
      final int outco = mod(inco + op.direction() * kco, LA); 
      final int outc = ALPHABET.codePointAt(outco); 
      out.appendCodePoint(outc); 
     } 
     return out.toString(); 
    } 

    private static int mod(final int x, final int n) { 
     // note that % is the remainder operation in Java 
     // so it doesn't handle negative values correctly 
     return (x % n + n) % n; 
    } 

    /** 
    * Main method for testing purposes only. 
    * 
    * @param args 
    *   ignored 
    */ 
    public static void main(final String[] args) { 
     // example taken from Wikipedia page on Vigenère 
     final Vigenère vigenere = new Vigenère(); 
     vigenere.init("LEMON", EnumSet.of(Vigenère.Flag.SKIP_SPACE)); 
     final String ct = vigenere.encrypt("ATTACK AT DAWN"); 
     System.out.println(ct); 
     final String pt = vigenere.decrypt(ct); 
     System.out.println(pt); 
    } 
} 
+0

我承认,我只是觉得编程它。 –

+0

太好了,谢谢!我会试一试 – Johan