2012-04-04 28 views
25

Tôi đang sử dụng khóa rsa để mã hóa chuỗi dài mà tôi sẽ gửi đến máy chủ của mình (sẽ mã hóa nó bằng khóa công khai của máy chủ và khóa riêng của tôi) ném một ngoại lệ như javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes Tôi cảm thấy rằng tôi đã không hiểu công việc của rsa đúng cho đến bây giờ (sử dụng các thư viện sẵn có là nguyên nhân cho việc này).
Có thể một số người vui lòng giải thích lý do ngoại lệ này bị ném. Nó không phải là có thể gửi chuỗi dài mã hóa?nhận được một IllegalBlockSizeException: Dữ liệu không được dài hơn 256 byte khi sử dụng rsa

+0

Chỉ cần sử dụng HTTPS và mã hóa sẽ được thực hiện một cách minh bạch. – zaph

Trả lời

44

Thuật toán RSA chỉ có thể mã hóa dữ liệu có chiều dài tối đa byte chiều dài khóa RSA trong bit chia với tám trừ mười một đệm byte, tức là số byte tối đa = chiều dài then chốt trong bit/8-11 .

Vì vậy, về cơ bản bạn chia độ dài khóa bằng 8 -11 (nếu bạn có đệm). Ví dụ: nếu bạn có khóa 2048bit, bạn có thể mã hóa 2048/8 = 256 byte (- 11 byte nếu bạn có đệm). Vì vậy, hoặc sử dụng một khóa lớn hơn hoặc bạn mã hóa dữ liệu bằng một khóa đối xứng và mã hóa khóa đó bằng rsa (đó là phương pháp được khuyến nghị).

Điều đó sẽ yêu cầu bạn phải:

  1. tạo ra một đối xứng chính
  2. Mã hóa dữ liệu với các đối xứng chính
  3. Mã hóa khóa đối xứng với rsa
  4. gửi chìa khóa mã hóa và dữ liệu
  5. Giải mã khóa đối xứng được mã hóa bằng rsa
  6. giải mã dữ liệu bằng khóa đối xứng
  7. done :)
+1

Tại sao có hạn chế như vậy, bạn có thể mã hóa dữ liệu tối đa một độ dài nhất định? – Ashwin

+2

Giới hạn 'chiều dài khóa bằng bit/8 - 11' chỉ hợp lệ khi' PKCS1Padding' được sử dụng. Ví dụ, với giới hạn 'NoPadding' sẽ là' chiều dài khóa bằng bit/8'. – divanov

+4

Cả hai PKCS # 1v1.5 padding và không có padding là không an toàn. Sử dụng đệm OAEP (làm giảm kích thước khối nhiều hơn 11 byte) – CodesInChaos

10

Bạn không nên sử dụng RSA trực tiếp trên dữ liệu bí mật của mình. Bạn chỉ nên sử dụng RSA trên giả ngẫu nhiên hoặc hoàn toàn ngẫu nhiên dữ liệu, chẳng hạn như khóa phiên hoặc mã xác thực thư.

Bạn đã gặp sự cố ở 256 byte - đó là vì bạn có thể đang làm việc với 2048 khóa bit. Các khóa có thể mã hóa bất kỳ số nguyên nào trong phạm vi 0 thành 2^2048 - 1 vào cùng một phạm vi và điều đó có nghĩa là dữ liệu của bạn phải từ 256 byte trở xuống.

Nếu bạn dự định mã hóa nhiều hơn, hãy sử dụng một mã hóa RSA để mã hóa khóa phiên cho thuật toán đối xứng và sử dụng để mã hóa dữ liệu của bạn.

+0

Tại sao có hạn chế như vậy, bạn có thể mã hóa dữ liệu tối đa một độ dài nhất định? – Ashwin

+3

Bởi vì RSA được thực hiện trên một _finite [ring] (http://en.wikipedia.org/wiki/Ring_ (toán học)) _, các số duy nhất tồn tại là các số nguyên trong phạm vi '[0, 2^2048- 1] ', bao gồm. Bất kỳ thư nào dài hơn 2048 bit đại diện cho một số nằm ngoài phạm vi này và phải được mã hóa theo hai khối hoặc - nếu bạn muốn an toàn - toàn bộ thông điệp phải được mã hóa trong khóa phiên. RSA được triển khai trong thế giới thực phải bảo vệ chống lại [nhiều cuộc tấn công] (http://en.wikipedia.org/wiki/RSA_ (thuật toán) #Attacks_against_plain_RSA), và không bao giờ làm việc trên văn bản thô "thô" là một phần quan trọng trong việc sử dụng RSA một cách an toàn. – sarnold

2

Để tiếp tục từ câu trả lời của John Snow ở trên, tôi đã tạo một thư viện ngẫu nhiên-đối xứng đơn giản mà bạn có thể sử dụng để mã hóa bất kỳ dữ liệu độ dài nào bằng khóa riêng.

Bạn có thể tìm thấy các thư viện tại GitHub - random-symmetric-crypto

final RandomSymmetricCipher cipher = new RandomSymmetricCipher(); 

// Encrypt the data and the random symmetric key. 
final CryptoPacket cryptoPacket = cipher.encrypt(inputData, PRIVATE_KEY_BASE64); 

// Convert the CryptoPacket into a Base64 String that can be readily reconstituted at the other end. 
final CryptoPacketConverter cryptoPacketConverter = new CryptoPacketConverter(); 
final String base64EncryptedData = cryptoPacketConverter.convert(cryptoPacket); 
System.out.println("Base64EncryptedData=" + base64EncryptedData); 

// Decrypt the Base64 encoded (and encrypted) String. 
final byte[] outputData = cipher.decrypt(base64EncryptedData, PUBLIC_KEY_BASE64); 
+0

Tôi không thể tìm thấy nơi bạn chỉ định chế độ đệm để sử dụng. Bạn nên chỉ định rõ ràng padding OAEP, vì các paddings phổ biến khác như PKCS # 1v1.5 hoặc không có padding ở tất cả đều không an toàn. – CodesInChaos

+0

Vui lòng chấp nhận yêu cầu kéo. Bạn có một URL để hỗ trợ không có phần đệm không an toàn không? – William

+0

Boneh đã viết một tổng quan tốt về [cuộc tấn công chống lại RSA] (http://crypto.stanford.edu/~dabo/papers/RSA-survey.pdf). Crypto.se cũng có rất nhiều câu hỏi về sách giáo khoa RSA (http://crypto.stackexchange.com/search?q=textbook+rsa). Tôi không thể tìm thấy MAC trong phần đối xứng của mã của bạn. Điều này có nghĩa là bạn sẽ dễ bị tấn công chủ động, chẳng hạn như những bí mật đệm. Lớp đệm PKCS # 1v1.5 cũ và vẫn phổ biến cũng dễ bị tấn công chủ động (trừ khi bạn làm việc xung quanh điểm yếu * thực sự * cẩn thận), cụ thể là đòn tấn công của Bleichenbacher. – CodesInChaos

-4

bạn cần chia dữ liệu theo PublicKey

int keyLength = publicKey.getModulus().bitLength()/16; 
String[] datas = splitString(data, keyLength - 11); 
String mi = ""//the data after encrypted; 
for (String s : datas) { 
    mi += bcd2Str(cipher.doFinal(s.getBytes())); 
} 
return mi; 


public static String bcd2Str(byte[] bytes) { 
    char temp[] = new char[bytes.length * 2], val; 

    for (int i = 0; i < bytes.length; i++) { 
     val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f); 
     temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0'); 

     val = (char) (bytes[i] & 0x0f); 
     temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0'); 
    } 
    return new String(temp); 
} 
+0

Chỉ vì bạn có thể làm điều gì đó không có nghĩa là bạn nên làm. Điều cần thiết là mã hóa lai nơi dữ liệu được mã hóa bằng thuật toán đối xứng như AES sử dụng khóa ngẫu nhiên và khóa được mã hóa bằng RSA. – zaph

+0

Lưu ý: 1. "dữ liệu" là số nhiều, "mốc" là dạng số ít. 2. Phương thức 'bcd2Str' thực sự thực hiện mã hóa thập lục phân [0-9A-Z]. 3. Có thể không cần phải mã hóa đầu ra nhị phân bằng mã hóa RSA và nếu mã hóa là cần thiết, trong khi hệ thập lục phân là OK, trường hợp thông thường là sử dụng mã hóa Base64, nó nhỏ gọn hơn. – zaph

3

Dựa trên @ John Tuyết trả lời, tôi đã làm một ví dụ

  1. Tạo khóa đối xứng (AES với 128 bit)

    KeyGenerator generator = KeyGenerator.getInstance("AES"); 
    generator.init(128); // The AES key size in number of bits 
    SecretKey secKey = generator.generateKey(); 
    
  2. Mã hóa văn bản đơn giản sử dụng AES

    String plainText = "Please encrypt me urgently..." 
    Cipher aesCipher = Cipher.getInstance("AES"); 
    aesCipher.init(Cipher.ENCRYPT_MODE, secKey); 
    byte[] byteCipherText = aesCipher.doFinal(plainText.getBytes()); 
    
  3. Encrypt phím sử dụng RSA công cộng quan trọng

    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); 
    kpg.initialize(2048); 
    KeyPair keyPair = kpg.generateKeyPair(); 
    
    PublicKey puKey = keyPair.getPublic(); 
    PrivateKey prKey = keyPair.getPrivate(); 
    
    Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); 
    cipher.init(Cipher.PUBLIC_KEY, puKey); 
    byte[] encryptedKey = cipher.doFinal(byteCipherText); 
    
  4. Gửi mã hóa dữ liệu (byteCipherText) + mã hóa AES Key (encryptedKey)

  5. Ở phía máy khách, giải mã khóa đối xứng bằng khóa riêng RSA

    cipher.init(Cipher.PRIVATE_KEY, prKey); 
    byte[] decryptedKey = cipher.doFinal(encryptedKey); 
    
  6. Giải mã các mật mã bằng cách sử dụng giải mã đối xứng chính

    //Convert bytes to AES SecertKey 
    SecretKey originalKey = new SecretKeySpec(decryptedKey , 0, decryptedKey .length, "AES"); 
    Cipher aesCipher = Cipher.getInstance("AES"); 
    aesCipher.init(Cipher.DECRYPT_MODE, originalKey); 
    byte[] bytePlainText = aesCipher.doFinal(byteCipherText); 
    String plainText = new String(bytePlainText);` 
    
Các vấn đề liên quan