2015-10-04 30 views
10

Tôi đang sử dụng mã này để tạo chuỗi ngẫu nhiên với chiều dài chosử dụng RNGCryptoServiceProvider để tạo chuỗi ngẫu nhiên

public string RandomString(int length) 
{ 
    const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; 
    StringBuilder res = new StringBuilder(); 
    Random rnd = new Random(); 
    while (0 < length--) 
    { 
     res.Append(valid[rnd.Next(valid.Length)]); 
    } 
    return res.ToString(); 
} 

Tuy nhiên, tôi đọc mà RNGCryptoServiceProvider là an toàn hơn Random lớp. Làm cách nào để triển khai RNGCryptoServiceProvider cho chức năng này. Nó nên sử dụng chuỗi valid giống như hàm này.

Trả lời

2

Các RNGCryptoServiceProvider lợi nhuận số ngẫu nhiên dưới dạng byte, vì vậy bạn cần một cách để có được một số ngẫu nhiên thuận tiện hơn từ nó:

public static int GetInt(RNGCryptoServiceProvider rnd, int max) { 
    byte[] r = new byte[4]; 
    int value; 
    do { 
    rnd.GetBytes(r); 
    value = BitConverter.ToInt32(r, 0) & Int32.MaxValue; 
    } while (value >= max * (Int32.MaxValue/max)); 
    return value % max; 
} 

Sau đó, bạn có thể sử dụng trong phương pháp của bạn:

public static string RandomString(int length) { 
    const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; 
    StringBuilder res = new StringBuilder(); 
    using (RNGCryptoServiceProvider rnd = new RNGCryptoServiceProvider()) { 
    while (length-- > 0) { 
     res.Append(valid[GetInt(rnd, valid.Length)]); 
    } 
    } 
    return res.ToString(); 
} 

(Tôi đã thực hiện phương pháp tĩnh, vì nó không sử dụng bất kỳ dữ liệu cá thể nào.)

14

Vì RNGRandomNumberGenerator chỉ trả về mảng byte, bạn phải làm điều đó l ike này:

static string RandomString(int length) 
{ 
    const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; 
    StringBuilder res = new StringBuilder(); 
    using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider()) 
    { 
     byte[] uintBuffer = new byte[sizeof(uint)]; 

     while (length-- > 0) 
     { 
      rng.GetBytes(uintBuffer); 
      uint num = BitConverter.ToUInt32(uintBuffer, 0); 
      res.Append(valid[(int)(num % (uint)valid.Length)]); 
     } 
    } 

    return res.ToString(); 
} 

Lưu ý tuy nhiên điều này có một lỗ hổng, 62 ký tự hợp lệ là bằng 5,9541963103868752088061235991756 bit (log (62)/log (2)), vì vậy nó sẽ không chia đều trên 32 số bit (uint).

Điều này có những hậu quả gì? Kết quả là, đầu ra ngẫu nhiên sẽ không đồng đều. Các ký tự có giá trị thấp hơn sẽ xuất hiện nhiều hơn (chỉ bằng một phần nhỏ, nhưng nó vẫn xảy ra).

Để chính xác hơn, 4 ký tự đầu tiên của mảng hợp lệ là 0,00000144354999199840239435286% nhiều khả năng xảy ra hơn.

Để tránh điều này, bạn nên sử dụng độ dài mảng mà chia đều như 64 (Xem xét sử dụng Convert.ToBase64String trên đầu ra thay vào đó, kể từ khi bạn sạch sẽ có thể kết hợp 64 bit đến 6 byte.

+1

Bạn phải cẩn thận khi bạn chuyển đổi các giá trị byte thành một số trong một phạm vi cụ thể. Nếu bạn chỉ sử dụng '%' thì nó sẽ tạo ra các số có phân phối không đồng đều, và toàn bộ mục đích của việc sử dụng một trình tạo ngẫu nhiên tốt hơn là vô nghĩa. – Guffa

+1

Đó là sự thật, muốn thêm điều đó sau khi tôi đăng câu trả lời của mình. Câu trả lời của Tamir Vered là khá thú vị mặc dù khủng khiếp không hiệu quả vì nó không sử dụng StringBuilder. Nó bỏ qua các byte không được chấp nhận làm đầu vào, đó là một giải pháp thú vị và sẽ có một phân phối thích hợp như là – hl3mukkel

2

Bạn cần phải tạo ra ngẫu nhiên byte s sử dụng RNGCryptoServiceProvider và thêm chỉ những người hợp lệ để các trở string:.

const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; 

static string GetRandomString(int length) 
{ 
    string s = ""; 
    using (RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider()) 
    { 
     while (s.Length != length) 
     { 
      byte[] oneByte = new byte[1]; 
      provider.GetBytes(oneByte); 
      char character = (char)oneByte[0]; 
      if (valid.Contains(character)) 
      { 
       s += character; 
      } 
     } 
    } 
    return s; 
} 

Bạn cũng có thể sử dụng modulo để không bỏ qua byte giá trị không hợp lệ nhưng cơ hội cho mỗi nhân vật sẽ không được thậm chí

+0

's.Length! = Valid.Length' phải là' s.Length! = Length', nếu không nó sẽ tạo ra một chuỗi có cùng độ dài với chuỗi 'valid' (và tham số' length' là cần thiết). – Guffa

+0

Vâng, tôi không nhận thấy anh ấy muốn cung cấp độ dài cho phương pháp, chỉnh sửa. –

2

Tôi chắc rằng tôi đã trả lời câu hỏi này trước đây với việc triển khai an toàn, không thiên vị và hiệu suất tốt. Nếu có, hãy bình luận.

Nhìn vào câu trả lời của Tamir, tôi nghĩ sẽ tốt hơn nếu sử dụng phép toán mô đun, nhưng cắt bỏ phần còn lại không đầy đủ của các giá trị byte. Tôi cũng đang viết câu trả lời này ngay bây giờ (có thể một lần nữa), bởi vì tôi cần tham khảo giải pháp này cho một đồng đẳng.

Nó chưa được thử nghiệm, do đó có thể có một vài lỗi.

const string lookup = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; 

static string GetRandomString(int length) 
{ 
    if (lookup.Length > 256) 
     throw new Exception("Lookup must be <= 256 characters in length"); 

    var maxMultiples = (int)Math.Floor(256/lookup.Length); 
    var exclusiveLimit = (lookup.Length * maxMultiples); 

    var sb = new StringBuilder(); 
    using (var provider = new RNGCryptoServiceProvider()) 
    { 
     var buffer = new byte[length]; 
     while (true) 
     { 
      var remaining = length - b.Length; 
      if (remaining == 0) 
       break; 

      provider.GetBytes(oneByte, 0 remaining); 

      for (int i = 0; i < remaining) 
      { 
       if (buffer[i] >= exclusiveLimit) 
        continue; 

       var index = buffer[i] % lookup.Length; 
       sb.Append(lookup[index]); 
      } 
     } 
    } 
    return sb.ToString(); 
} 
+0

Ah, tìm thấy nó: https://stackoverflow.com/a/1518495/887092. Nhưng điều này vẫn có thể tốt hơn (khi nó được kiểm tra và xác nhận là đang hoạt động). Làm ơn cho tôi biết. – Todd

0
private string sifreuretimi(int sayı) //3 
    { 
     Random rastgele = new Random(); 
     StringBuilder sb = new StringBuilder(); 
     char karakter1 = ' ', karakter2 = ' ', karakter3 = ' '; 
     int ascii1, ascii2, ascii3 = 0; 

     for (int i = 0; i < sayı/3; i++) 
     { 
      ascii1 = rastgele.Next(48,58); 
      karakter1 = Convert.ToChar(ascii1); 

      ascii2 = rastgele.Next(65, 91); 
      karakter2 = Convert.ToChar(ascii2); 

      ascii3 = rastgele.Next(97, 123); 
      karakter3 = Convert.ToChar(ascii3); 

      sb.Append(karakter1); 
      sb.Append(karakter2); 
      sb.Append(karakter3); 
     } 
     return sb.ToString(); 
    } 
+0

Xin chào, vui lòng cung cấp nhận xét để giải thích câu trả lời của bạn; cảm ơn bạn ! – NatNgs

+0

Sử dụng một mảng ký tự trong phần mềm như vậy không phải là cách tiếp cận đúng. –

0

lưu ý: Tôi biết sử dụng hợp cụ thể lệch OP, nhưng tôi nghĩ rằng nó có thể giúp đỡ những người khác có một tình huống hơi khác nhau

Rất nhiều có thể nói về lý do để chuyển đổi một mảng byte mật mã thành một chuỗi, nhưng thường là cho một số loại mục đích tuần tự hóa; và do đó, trong trường hợp đó: bộ ký tự được chọn là tùy ý.

Vì vậy, nếu và chỉ khi trường hợp trước là đúng VÀ độ dài của chuỗi không bắt buộc phải được tối ưu hóa; bạn có thể sử dụng một đại diện thập lục phân đơn giản của mảng byte như thế này:

//note: since the choice of characters [0..9a..zA...Z] is arbitrary, 
//limiting to [0..9,A..F] would seem to be a really big problem if it can be compensated 
//by the length. 

var rnd = new RNGCryptoServiceProvider(); 
var sb = new StringBuilder(); 
var buf = new byte[10]; //length: should be larger 
rnd.GetBytes(buf); 

//gives a "valid" range of: "ABCDEF" 
foreach (byte b in buf) 
    sb.AppendFormat("{0:x2}", b); 

//sb contains a RNGCryptoServiceProvider based "string" 

Bây giờ bạn sẽ nói: nhưng đợi: đây chỉ là 16 ký tự nơi chuỗi OP của có 62. chuỗi của bạn sẽ lâu hơn rất nhiều.

"Yes", tôi sẽ trả lời, "và nếu đó là một vấn đề, tại sao bạn không chọn 256 dễ đọc-and-serrializable-nhân vật ... hoặc có lẽ 64"; -)

Như @Guffa đã nêu; sử dụng % bị cấm trừ khi nó không thay đổi phân phối. Để thực hiện điều này, với tập hợp được phân phối đồng đều, tập con phải phù hợp chính xác x lần trong tập hợp gốc.

Vì vậy, mở rộng tập hợp hợp lệ ban đầu của bạn với 2 cho kết quả hợp lệ (vì: 256/64 = 4).

Mã này sẽ là:

//note: added + and/chars. could be any of them 
const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890+/"; 

var rnd = new RNGCryptoServiceProvider(); 
var sb = new StringBuilder(); 
var buf = new byte[10]; //length: should be larger 
rnd.GetBytes(buf); //get the bytes 

foreach (byte b in buf) 
    sb.Append(valid[b%64]); 

Xin lưu ý: trong tất cả các câu trả lời, trong đó có một này, các tiểu bộ nhỏ hơn 256 khả năng của byte. Điều này có nghĩa là có ít thông tin hơn. Điều này có nghĩa nếu bạn có chuỗi của bạn với 4 ký tự, nó dễ dàng hơn để crack kết quả 4 byte ban đầu của RNGCryptoServiceProvider.

Vì vậy, ... bây giờ bạn nói: "Tại sao không sử dụng 64 mã hóa cơ sở?", tốt, nếu nó phù hợp với bạn, nhưng hãy cẩn thận với trailing =, xem Base64 on Wikipedia:

var rnd = new RNGCryptoServiceProvider(); 
var buf = new byte[60]; //length not randomly picked, see 64Base, wikipedia 
rnd.GetBytes(buf); 
string result = Convert.ToBase64String(buf); 

Hãy note²: Một trường hợp sử dụng điển hình là một số url token. Xin lưu ý rằng dấu hiệu + không hợp lệ như một ký tự như vậy.

Các vấn đề liên quan