Cách mã hóa Chuỗi trong Java


150

Những gì tôi cần là mã hóa chuỗi sẽ hiển thị dưới dạng mã vạch 2D (PDF-417) để khi ai đó có ý tưởng quét, nó sẽ không thể đọc được gì.

Những yêu cầu khác:

  • không nên phức tạp
  • nó không nên bao gồm RSA, cơ sở hạ tầng PKI, các cặp khóa, v.v.

Nó phải đủ đơn giản để thoát khỏi những người rình mò xung quanh và dễ dàng giải mã cho các công ty khác quan tâm đến việc lấy dữ liệu đó. Họ gọi cho chúng tôi, chúng tôi nói với họ tiêu chuẩn hoặc cung cấp cho họ một số khóa đơn giản có thể được sử dụng để giải mã.

Có lẽ các công ty đó có thể sử dụng các công nghệ khác nhau nên sẽ tốt hơn nếu tuân theo một số tiêu chuẩn không gắn với một số nền tảng hoặc công nghệ đặc biệt.

Bạn có đề nghị gì? Có một số lớp Java đang làm encrypt()& decrypt()không có nhiều phức tạp trong việc đạt được các tiêu chuẩn bảo mật cao?



Cảnh báo . Rất nhiều câu trả lời dưới đây cho thấy một phương pháp hoặc phương pháp khác để thực hiện bất kỳ loại mật mã nào trên Java. Câu trả lời có thể không phản ánh thực tiễn mật mã tốt và có thể không được xem xét tốt; không có những thứ như bảo mật sao chép / dán . Câu trả lời ít nhất nên đưa chuyển đổi chuỗi vào tài khoản. Câu hỏi thực tế với mã vạch 2D bao gồm quá rộng và cần có giải pháp cụ thể cho khách hàng.
Maarten Bodewes

Câu trả lời:


156

Đây là trang đầu tiên xuất hiện thông qua Google và các lỗ hổng bảo mật trong tất cả các triển khai khiến tôi chùn bước nên tôi đăng bài này để thêm thông tin liên quan đến mã hóa cho người khác vì đã 7 năm kể từ bài đăng gốc. Tôi có bằng Thạc sĩ về Kỹ thuật Máy tính và dành nhiều thời gian để học và học Mật mã, vì vậy tôi sẽ ném hai xu của mình để biến internet thành một nơi an toàn hơn.

Ngoài ra, xin lưu ý rằng rất nhiều triển khai có thể an toàn cho một tình huống nhất định, nhưng tại sao lại sử dụng chúng và có khả năng vô tình mắc lỗi? Sử dụng các công cụ mạnh nhất bạn có sẵn trừ khi bạn có lý do cụ thể không. Nhìn chung, tôi khuyên bạn nên sử dụng một thư viện và tránh xa các chi tiết khó chịu nếu bạn có thể.

CẬP NHẬT 4/5/18: Tôi viết lại một số phần để đơn giản hơn để hiểu và thay đổi thư viện được đề xuất từ Jasypt sang thư viện mới của Tink , tôi khuyên bạn nên xóa hoàn toàn Jasypt khỏi thiết lập hiện có.

Lời tựa

Tôi sẽ phác thảo những điều cơ bản về mật mã đối xứng an toàn dưới đây và chỉ ra những lỗi phổ biến tôi thấy trực tuyến khi mọi người tự mình thực hiện tiền điện tử với thư viện Java tiêu chuẩn. Nếu bạn muốn bỏ qua tất cả các chi tiết chạy đến thư viện mới của Google, hãy nhập vào dự án của bạn và sử dụng chế độ AES-GCM cho tất cả các mã hóa của bạn và bạn sẽ được an toàn.

Bây giờ nếu bạn muốn tìm hiểu các chi tiết nghiệt ngã về cách mã hóa trong java, hãy đọc tiếp :)

Mật mã khối

Điều đầu tiên trước tiên bạn cần chọn một khóa Mã hóa đối xứng. Mã hóa khối là một chức năng / chương trình máy tính được sử dụng để tạo Giả ngẫu nhiên. Giả ngẫu nhiên là tính ngẫu nhiên giả mà không có máy tính nào ngoài Máy tính lượng tử có thể cho biết sự khác biệt giữa nó và tính ngẫu nhiên thực. Mật mã khối giống như khối xây dựng thành mật mã, và khi được sử dụng với các chế độ hoặc sơ đồ khác nhau, chúng ta có thể tạo mã hóa.

Bây giờ liên quan đến Thuật toán mã hóa khối có sẵn ngày hôm nay, Hãy chắc chắn KHÔNG BAO GIỜ , tôi lặp lại KHÔNG BAO GIỜ sử dụng DES , thậm chí tôi sẽ nói KHÔNG BAO GIỜ sử dụng 3DES . Mã hóa khối duy nhất mà ngay cả bản phát hành NSA của Snowden cũng có thể xác minh là thực sự gần với Pseudo-Random nhất có thể là AES 256 . Ngoài ra còn có AES 128; sự khác biệt là AES 256 hoạt động trong các khối 256 bit, trong khi AES 128 hoạt động trong 128 khối. Nói chung, AES 128 được coi là an toàn mặc dù một số điểm yếu đã được phát hiện, nhưng 256 vẫn vững chắc như nó có.

Sự thật thú vị DES đã bị NSA phá vỡ khi nó được thành lập ban đầu và thực sự giữ bí mật trong một vài năm. Mặc dù một số người vẫn cho rằng 3DES là an toàn, nhưng có khá nhiều tài liệu nghiên cứu đã tìm thấy và phân tích điểm yếu trong 3DES .

Chế độ mã hóa

Mã hóa được tạo khi bạn lấy một mật mã khối và sử dụng một sơ đồ cụ thể để tính ngẫu nhiên được kết hợp với một khóa để tạo ra thứ gì đó có thể đảo ngược miễn là bạn biết khóa. Điều này được gọi là Chế độ mã hóa.

Dưới đây là một ví dụ về chế độ mã hóa và chế độ đơn giản nhất được gọi là ECB để bạn có thể hiểu trực quan những gì đang xảy ra:

Chế độ ECB

Các chế độ mã hóa bạn sẽ thấy phổ biến nhất trên mạng là như sau:

TLB ECB, CBC, GCM

Có tồn tại các chế độ khác bên ngoài các chế độ được liệt kê và các nhà nghiên cứu luôn làm việc hướng tới các chế độ mới để cải thiện các vấn đề hiện có.

Bây giờ hãy chuyển sang triển khai và những gì an toàn. KHÔNG BAO GIỜ sử dụng ECB, điều này rất tệ trong việc ẩn dữ liệu lặp lại như được hiển thị bởi chim cánh cụt Linux nổi tiếng .Ví dụ chim cánh cụt Linux

Khi triển khai trong Java, lưu ý rằng nếu bạn sử dụng mã sau đây, chế độ ECB được đặt theo mặc định:

Cipher cipher = Cipher.getInstance("AES");

... NGUY HIỂM NÀY LÀ MỘT VULNH VIỄN! và thật không may, điều này được nhìn thấy trên khắp StackOverflow và trực tuyến trong các hướng dẫn và ví dụ.

Nonces và IV

Để đối phó với vấn đề được tìm thấy với chế độ ECB, thông báo còn được gọi là IV đã được tạo. Ý tưởng là chúng tôi tạo ra một biến ngẫu nhiên mới và gắn nó vào mọi mã hóa để khi bạn mã hóa hai tin nhắn giống nhau, chúng sẽ xuất hiện khác nhau. Vẻ đẹp đằng sau điều này là IV hoặc nonce là kiến ​​thức công cộng. Điều đó có nghĩa là kẻ tấn công có thể có quyền truy cập vào điều này nhưng miễn là họ không có chìa khóa của bạn, họ không thể làm gì với kiến ​​thức đó.

Các vấn đề phổ biến tôi sẽ thấy là mọi người sẽ đặt IV làm giá trị tĩnh như trong cùng một giá trị cố định trong mã của họ. và đây là cạm bẫy đối với IV ngay khi bạn lặp lại một lần bạn thực sự thỏa hiệp toàn bộ bảo mật mã hóa của bạn.

Tạo IV ngẫu nhiên

SecureRandom randomSecureRandom = SecureRandom.getInstance("SHA1PRNG");
byte[] iv = new byte[cipher.getBlockSize()];
randomSecureRandom.nextBytes(iv);
IvParameterSpec ivParams = new IvParameterSpec(iv);

Lưu ý: SHA1 bị hỏng nhưng tôi không thể tìm thấy cách triển khai SHA256 vào trường hợp sử dụng này một cách chính xác, vì vậy nếu bất cứ ai muốn có một bản crack tại đây và cập nhật thì thật tuyệt vời! Ngoài ra các cuộc tấn công SHA1 vẫn còn độc đáo vì có thể mất vài năm trên một cụm lớn để phá vỡ. Kiểm tra chi tiết tại đây.

Thực hiện TLB

Không có phần đệm được yêu cầu cho chế độ CTR.

 Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");

Triển khai CBC

Nếu bạn chọn triển khai Chế độ CBC, hãy thực hiện với PKCS7Padding như sau:

 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");

Lỗ hổng CBC và CTR và Tại sao bạn nên sử dụng GCM

Mặc dù một số chế độ khác như CBC và CTR được bảo mật, chúng vẫn gặp phải vấn đề trong đó kẻ tấn công có thể lật dữ liệu được mã hóa, thay đổi giá trị của nó khi được giải mã. Vì vậy, giả sử bạn mã hóa một tin nhắn ngân hàng tưởng tượng "Bán 100", tin nhắn được mã hóa của bạn trông giống như "eu23ng", kẻ tấn công thay đổi một bit thành "eu53ng" và bất ngờ khi giải mã tin nhắn của bạn, nó đọc là "Bán 900".

Để tránh điều này, phần lớn internet sử dụng GCM và mỗi khi bạn thấy HTTPS thì có lẽ họ đang sử dụng GCM. GCM ký tin nhắn được mã hóa bằng hàm băm và kiểm tra để xác minh rằng tin nhắn không bị thay đổi khi sử dụng chữ ký này.

Tôi sẽ tránh thực hiện GCM vì sự phức tạp của nó. Bạn nên sử dụng thư viện mới của Googles Tink bởi vì ở đây một lần nữa nếu bạn vô tình lặp lại IV, bạn đang làm tổn hại khóa trong trường hợp với GCM, đó là lỗ hổng bảo mật tối thượng. Các nhà nghiên cứu mới đang làm việc theo hướng IV lặp lại các chế độ mã hóa trong đó ngay cả khi bạn lặp lại IV, khóa vẫn không gặp nguy hiểm nhưng điều này vẫn chưa trở thành xu hướng.

Bây giờ nếu bạn muốn thực hiện GCM, đây là một liên kết đến một triển khai GCM đẹp . Tuy nhiên, tôi không thể đảm bảo an ninh hoặc nếu nó được thực hiện đúng nhưng nó làm giảm cơ sở. Cũng lưu ý với GCM không có phần đệm.

Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

Phím vs Mật khẩu

Một lưu ý rất quan trọng khác là khi nói về mật mã, Khóa và Mật khẩu không giống nhau. Khóa trong mật mã cần phải có một lượng entropy và tính ngẫu nhiên nhất định để được coi là an toàn. Đây là lý do tại sao bạn cần đảm bảo sử dụng các thư viện mật mã phù hợp để tạo khóa cho bạn.

Vì vậy, bạn thực sự có hai triển khai bạn có thể làm ở đây, đầu tiên là sử dụng mã được tìm thấy trên luồng StackOverflow này để tạo khóa ngẫu nhiên . Giải pháp này sử dụng trình tạo số ngẫu nhiên an toàn để tạo khóa từ đầu mà bạn có thể sử dụng.

Tùy chọn kém an toàn khác là sử dụng, nhập liệu của người dùng như mật khẩu. Vấn đề như chúng ta đã thảo luận là mật khẩu không có đủ entropy, vì vậy chúng ta sẽ phải sử dụng PBKDF2 , một thuật toán lấy mật khẩu và củng cố nó. Đây là một triển khai StackOverflow tôi thích . Tuy nhiên, thư viện Google Tink có tất cả những thứ này được tích hợp sẵn và bạn nên tận dụng nó.

Nhà phát triển Android

Một điểm quan trọng cần chỉ ra ở đây là biết rằng mã Android của bạn có thể thiết kế ngược và hầu hết các trường hợp hầu hết mã java cũng vậy. Điều đó có nghĩa là nếu bạn lưu trữ mật khẩu bằng văn bản đơn giản trong mã của bạn. Một hacker có thể dễ dàng lấy nó. Thông thường, đối với các loại mã hóa này, bạn muốn sử dụng Mật mã bất đối xứng, v.v. Điều này nằm ngoài phạm vi của bài này vì vậy tôi sẽ tránh đi sâu vào nó.

Một bài đọc thú vị từ năm 2013 : Chỉ ra rằng 88% việc triển khai Crypto trong Android đã được thực hiện không đúng cách.

Suy nghĩ cuối cùng

Một lần nữa tôi đề nghị tránh trực tiếp triển khai thư viện java cho tiền điện tử và sử dụng Google Tink , điều này sẽ giúp bạn đỡ đau đầu vì họ đã thực sự làm tốt việc thực hiện đúng tất cả các thuật toán. Và thậm chí sau đó hãy chắc chắn rằng bạn kiểm tra các vấn đề được đưa ra trên github Tink, cửa sổ bật lên lỗ hổng ở đây và đó.

Nếu bạn có bất kỳ câu hỏi hoặc phản hồi hãy bình luận! Bảo mật luôn thay đổi và bạn cần cố gắng hết sức để theo kịp nó :)


15
Đây là thứ sạch nhất tôi từng thấy.
Seraf

1
@SabirKhan Nó có thể là một nguyên nhân gây lo ngại nhưng các thuật toán cốt lõi vẫn chưa bị phá vỡ vì vậy tôi sẽ không quá lo lắng về điều đó. Trong trường hợp bạn không tin tưởng thì cũng hãy xem github.com/google/keyczar , Nó được phát triển bởi nhóm bảo mật của Google.
Konstantino Sparakis

1
@KonstantinoSparakis: Nếu tôi không hiểu sai tài liệu về Jasypt's BasicTextEncryptor và StrongTextEncryptor, các lớp đó sử dụng DES3DES để mã hóa, đó chính xác là những gì bạn nói với độc giả không sử dụng. IMO, bạn nên thay thế các ví dụ mã đã cho bằng một ví dụ sử dụng StandardPBEStringEncryptor của Jasypt và xác định thủ công thuật toán AES để sử dụng.
xpages-noob 23/03/18

1
@ xpages-noob Tôi đã cập nhật bài viết. Tôi thực sự đã tìm thấy Google Tink, đây là thư viện được hỗ trợ mới nhất cho tiền điện tử vì vậy bạn nên kiểm tra nó!
Konstantino Sparakis

2
Kích thước khối AES là 128 bit. Trong AES 256, kích thước khóa là 256 bit. Tương tự, AES 192 và AES 128. Ngoài ra, kể từ Java 8, getInstanceStrong()phương pháp thích hợp Cipherhơn SHA1PRNG
Saptarshi Basu

110

Tôi khuyên bạn nên sử dụng một số cypher đối xứng tiêu chuẩn có sẵn rộng rãi như DES , 3DES hoặc AES . Mặc dù đó không phải là thuật toán an toàn nhất, có rất nhiều triển khai và bạn chỉ cần cung cấp khóa cho bất kỳ ai được cho là giải mã thông tin trong mã vạch. javax.crypto.Codes là những gì bạn muốn làm việc ở đây.

Giả sử các byte để mã hóa nằm trong

byte[] input;

Tiếp theo, bạn sẽ cần các byte vector khóa và khởi tạo

byte[] keyBytes;
byte[] ivBytes;

Bây giờ bạn có thể khởi tạo Mật mã cho thuật toán mà bạn chọn:

// wrap key data in Key/IV specs to pass to cipher
SecretKeySpec key = new SecretKeySpec(keyBytes, "DES");
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
// create the cipher with the algorithm you choose
// see javadoc for Cipher class for more info, e.g.
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");

Mã hóa sẽ như thế này:

cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] encrypted= new byte[cipher.getOutputSize(input.length)];
int enc_len = cipher.update(input, 0, input.length, encrypted, 0);
enc_len += cipher.doFinal(encrypted, enc_len);

Và giải mã như thế này:

cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] decrypted = new byte[cipher.getOutputSize(enc_len)];
int dec_len = cipher.update(encrypted, 0, enc_len, decrypted, 0);
dec_len += cipher.doFinal(decrypted, dec_len);

9
Tôi có thể đề nghị bạn cập nhật ví dụ này để tham khảo DESedethuật toán không? Vì đây là một câu hỏi phổ biến (và câu trả lời), sẽ thật xấu hổ khi khuyến khích mọi người sử dụng DES, vì mật mã rất yếu theo các tiêu chuẩn ngày nay.
Duncan Jones

có gì đó không ổn với javax.crypto.BadPaddingException: Đưa ra khối cuối cùng không được đệm đúng cách trong khi giải mã
tò mò

2
@Duncan Thật vậy, DES yếu nhưng tôi cho rằng AES sẽ thích hợp hơn so với DESede (hay còn gọi là TiplDES): http://security.stackexchange.com/a/26181/69785
Piovezan 29/2/2016

2
Điều này cần được cập nhật để có AES / GCM / NoPadding, DES dễ bị tấn công bruteforce, TripleDes cũng không được khuyến nghị
Konstantino Sparakis

1
Câu trả lời từ Konstantino Sparakis dưới đây là SO tốt hơn nhiều so với câu hỏi này.
Steve

22

Cảnh báo

Không sử dụng điều này như một số loại đo lường bảo mật.

Cơ chế mã hóa trong bài đăng này là một bộ đệm Một lần, có nghĩa là khóa bí mật có thể dễ dàng được phục hồi bởi kẻ tấn công sử dụng 2 tin nhắn được mã hóa. Tin nhắn được mã hóa XOR 2 và bạn nhận được chìa khóa. Thật đơn giản!

Được chỉ ra bởi Moussa


Tôi đang sử dụng Base64Encoder / Decoder của Sun được tìm thấy trong JRE của Sun, để tránh một JAR khác trong lib. Điều đó nguy hiểm từ điểm sử dụng OpenJDK hoặc một số JRE khác. Ngoài ra, có một lý do nào khác mà tôi nên xem xét khi sử dụng Apache commons lib với Encoder / Decoder không?

public class EncryptUtils {
    public static final String DEFAULT_ENCODING = "UTF-8"; 
    static BASE64Encoder enc = new BASE64Encoder();
    static BASE64Decoder dec = new BASE64Decoder();

    public static String base64encode(String text) {
        try {
            return enc.encode(text.getBytes(DEFAULT_ENCODING));
        } catch (UnsupportedEncodingException e) {
            return null;
        }
    }//base64encode

    public static String base64decode(String text) {
        try {
            return new String(dec.decodeBuffer(text), DEFAULT_ENCODING);
        } catch (IOException e) {
            return null;
        }
    }//base64decode

    public static void main(String[] args) {
        String txt = "some text to be encrypted";
        String key = "key phrase used for XOR-ing";
        System.out.println(txt + " XOR-ed to: " + (txt = xorMessage(txt, key)));

        String encoded = base64encode(txt);       
        System.out.println(" is encoded to: " + encoded + " and that is decoding to: " + (txt = base64decode(encoded)));
        System.out.print("XOR-ing back to original: " + xorMessage(txt, key));
    }

    public static String xorMessage(String message, String key) {
        try {
            if (message == null || key == null) return null;

            char[] keys = key.toCharArray();
            char[] mesg = message.toCharArray();

            int ml = mesg.length;
            int kl = keys.length;
            char[] newmsg = new char[ml];

            for (int i = 0; i < ml; i++) {
                newmsg[i] = (char)(mesg[i] ^ keys[i % kl]);
            }//for i

            return new String(newmsg);
        } catch (Exception e) {
            return null;
        }
    }//xorMessage
}//class

1
Tôi cũng đã sử dụng đề xuất giải pháp này qua sun.misc.BASE64Encoder nhưng khi sử dụng các chuỗi khá lớn để mã hóa, bộ mã hóa trả về các chuỗi chunk (mỗi ký tự gồm 76 ký tự). Sau đó tôi đã chuyển sang Apache Commons Codec Base64, cung cấp các phương thức mã hóa không phân đoạn!
basZero

78
Cơ chế mã hóa mà bạn mô tả là RẤT NGUY HIỂM nếu được sử dụng nhiều lần. đó là lý do tại sao nó được gọi là pad một lần. Khóa bí mật có thể được phục hồi dễ dàng bởi kẻ tấn công sử dụng 2 tin nhắn được mã hóa. xor 2 tin nhắn được mã hóa và bạn nhận được chìa khóa. Thật đơn giản!
xtrem

3
Ý tưởng của nó không phải là nặng nề, chỉ để khiến mọi người cố gắng đọc những gì được viết bằng mã vạch 2D-417 2D. Và dù sao, chỉ có một số chỉ mục không quan trọng đối với bất cứ ai ...
ante.sabo

2
ĐỒNG Ý. Chỉ quan tâm rằng ai đó sử dụng điều này như một cơ chế mã hóa.
xtrem

Đối với Mã hóa, có thể tránh được bộ mã hóa (ví dụ.BASE64Encoder) để có các cuộc tấn công vũ phu.
Jagrut Dalwadi

13

cảm ơn tôi đã tạo lớp này bằng mã của bạn, có lẽ ai đó tìm thấy nó đầy đủ

đối tượng crypter

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;


public class ObjectCrypter {

private Cipher deCipher;
private Cipher enCipher;
private SecretKeySpec key;
private IvParameterSpec ivSpec;


public ObjectCrypter(byte[] keyBytes,   byte[] ivBytes) {
    // wrap key data in Key/IV specs to pass to cipher


     ivSpec = new IvParameterSpec(ivBytes);
    // create the cipher with the algorithm you choose
    // see javadoc for Cipher class for more info, e.g.
    try {
         DESKeySpec dkey = new  DESKeySpec(keyBytes);
          key = new SecretKeySpec(dkey.getKey(), "DES");
         deCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
         enCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
    } catch (NoSuchAlgorithmException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}
public byte[] encrypt(Object obj) throws InvalidKeyException, InvalidAlgorithmParameterException, IOException, IllegalBlockSizeException, ShortBufferException, BadPaddingException {
    byte[] input = convertToByteArray(obj);
    enCipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);

    return enCipher.doFinal(input);




//  cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
//  byte[] encypted = new byte[cipher.getOutputSize(input.length)];
//  int enc_len = cipher.update(input, 0, input.length, encypted, 0);
//  enc_len += cipher.doFinal(encypted, enc_len);
//  return encypted;


}
public Object decrypt( byte[]  encrypted) throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, IOException, ClassNotFoundException {
    deCipher.init(Cipher.DECRYPT_MODE, key, ivSpec);

    return convertFromByteArray(deCipher.doFinal(encrypted));

}



private Object convertFromByteArray(byte[] byteObject) throws IOException,
        ClassNotFoundException {
    ByteArrayInputStream bais;

    ObjectInputStream in;
    bais = new ByteArrayInputStream(byteObject);
    in = new ObjectInputStream(bais);
    Object o = in.readObject();
    in.close();
    return o;

}



private byte[] convertToByteArray(Object complexObject) throws IOException {
    ByteArrayOutputStream baos;

    ObjectOutputStream out;

    baos = new ByteArrayOutputStream();

    out = new ObjectOutputStream(baos);

    out.writeObject(complexObject);

    out.close();

    return baos.toByteArray();

}


}

đăng một câu hỏi liên quan ở đây !
dùng2023507

Không phải là trường hợp chuyển các khóa khác nhau trong quá trình mã hóa và giải mã không nên trả lại văn bản? Điều đó dường như không xảy ra ở đây. PS: Tôi đang sử dụng các đối tượng khác nhau của lớp này để thực hiện bài kiểm tra này.
instanceOfObject

6

Cập nhật ngày 12 tháng 12 năm 2019

Không giống như một số chế độ khác như CBC, chế độ GCM không yêu cầu IV không thể đoán trước. Yêu cầu duy nhất là IV phải là duy nhất cho mỗi lần gọi với một khóa đã cho. Nếu nó lặp lại một lần cho một khóa nhất định, bảo mật có thể bị xâm phạm. Một cách dễ dàng để đạt được điều này là sử dụng IV ngẫu nhiên từ trình tạo số ngẫu nhiên giả mạnh như dưới đây.

Sử dụng một chuỗi hoặc dấu thời gian như IV cũng có thể, nhưng nó có thể không tầm thường như nó có thể nghe. Ví dụ: nếu hệ thống không theo dõi chính xác các chuỗi đã được sử dụng như IV trong một cửa hàng liên tục, một lệnh gọi có thể lặp lại IV sau khi khởi động lại hệ thống. Tương tự như vậy, không có đồng hồ hoàn hảo. Đồng hồ máy tính sẵn sàng, vv

Ngoài ra, khóa phải được xoay sau mỗi 2 ^ 32 lần gọi. Để biết thêm chi tiết về yêu cầu IV, hãy tham khảo câu trả lời này và các khuyến nghị của NIST .


Đây là mã hóa và mã hóa mà tôi vừa viết trong Java 8 xem xét các điểm sau. Hy vọng ai đó sẽ tìm thấy điều này hữu ích:

  1. Thuật toán mã hóa : Chặn mã hóa AES với khóa 256 bit được coi là đủ an toàn. Để mã hóa một tin nhắn hoàn chỉnh, một chế độ cần được chọn. Mã hóa xác thực (cung cấp cả tính bảo mật và tính toàn vẹn) được khuyến nghị. GCM, CCM và EAX là các chế độ mã hóa được xác thực phổ biến nhất. GCM thường được ưa thích và nó hoạt động tốt trong các kiến ​​trúc Intel cung cấp các hướng dẫn dành riêng cho GCM. Tất cả ba chế độ này là chế độ dựa trên TLB (dựa trên bộ đếm) và do đó chúng không cần đệm. Kết quả là họ không dễ bị tấn công đệm

  2. Một Vector khởi tạo (IV) là bắt buộc cho GCM. IV không phải là một bí mật. Yêu cầu duy nhất là nó phải ngẫu nhiên hoặc không thể đoán trước. Trong Java, SecuredRandomlớp có nghĩa là tạo ra các số ngẫu nhiên giả mã hóa mạnh. Thuật toán tạo số giả ngẫu nhiên có thể được chỉ định trong getInstance()phương thức. Tuy nhiên, kể từ Java 8, cách được khuyến nghị là sử dụng getInstanceStrong()phương thức sẽ sử dụng thuật toán mạnh nhất được cấu hình và cung cấp bởiProvider

  3. NIST khuyến nghị 96 bit IV cho GCM để thúc đẩy khả năng tương tác, hiệu quả và đơn giản của thiết kế

  4. Để đảm bảo an ninh bổ sung, trong quá trình thực hiện sau đây SecureRandomđược gieo lại sau khi tạo ra mỗi 2 ^ 16 byte tạo byte ngẫu nhiên giả

  5. Người nhận cần biết IV để có thể giải mã văn bản mật mã. Do đó IV cần phải được chuyển cùng với văn bản mật mã. Một số triển khai gửi IV dưới dạng AD (Dữ liệu được liên kết) có nghĩa là thẻ xác thực sẽ được tính trên cả văn bản mã hóa và IV. Tuy nhiên, điều đó là không bắt buộc. IV có thể được duyệt trước bằng văn bản mật mã vì nếu IV bị thay đổi trong quá trình truyền do một cuộc tấn công có chủ ý hoặc lỗi hệ thống mạng / tệp, dù sao việc xác thực thẻ xác thực sẽ thất bại

  6. Không nên sử dụng Chuỗi để giữ tin nhắn văn bản rõ ràng hoặc khóa vì Chuỗi không thay đổi và do đó chúng tôi không thể xóa chúng sau khi sử dụng. Các chuỗi không rõ ràng này sau đó lưu lại trong bộ nhớ và có thể hiển thị trong một đống heap. Vì lý do tương tự, khách hàng gọi các phương thức mã hóa hoặc giải mã này sẽ xóa tất cả các biến hoặc mảng giữ thông điệp hoặc khóa sau khi chúng không còn cần thiết.

  7. Không có nhà cung cấp nào được mã hóa cứng trong mã theo các khuyến nghị chung

  8. Cuối cùng để truyền qua mạng hoặc lưu trữ, khóa hoặc văn bản mật mã phải được mã hóa bằng mã hóa Base64. Các chi tiết của Base64 có thể được tìm thấy ở đây . Cách tiếp cận Java 8 nên được tuân theo

Mảng byte có thể được xóa bằng cách sử dụng:

Arrays.fill(clearTextMessageByteArray, Byte.MIN_VALUE);

Tuy nhiên, kể từ Java 8, không có cách nào dễ dàng để xóa SecretKeyspecSecretKeyvì việc triển khai hai giao diện này dường như không thực hiện phương thức destroy()của giao diện Destroyable. Trong đoạn mã sau, một phương thức riêng biệt được viết để xóa SecretKeySpecSecretKeysử dụng sự phản chiếu.

Khóa phải được tạo bằng một trong hai cách tiếp cận được đề cập dưới đây.

Lưu ý rằng các khóa là bí mật như mật khẩu, nhưng không giống như mật khẩu dành cho sử dụng của con người, các khóa được sử dụng bởi các thuật toán mã hóa và do đó chỉ nên được tạo bằng cách sử dụng ở trên.

package com.sapbasu.javastudy;

import java.lang.reflect.Field;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class Crypto {

  private static final int AUTH_TAG_SIZE = 128; // bits

  // NIST recommendation: "For IVs, it is recommended that implementations
  // restrict support to the length of 96 bits, to
  // promote interoperability, efficiency, and simplicity of design."
  private static final int IV_LEN = 12; // bytes

  // number of random number bytes generated before re-seeding
  private static final double PRNG_RESEED_INTERVAL = Math.pow(2, 16);

  private static final String ENCRYPT_ALGO = "AES/GCM/NoPadding";

  private static final List<Integer> ALLOWED_KEY_SIZES = Arrays
      .asList(new Integer[] {128, 192, 256}); // bits

  private static SecureRandom prng;

  // Used to keep track of random number bytes generated by PRNG
  // (for the purpose of re-seeding)
  private static int bytesGenerated = 0;

  public byte[] encrypt(byte[] input, SecretKeySpec key) throws Exception {

    Objects.requireNonNull(input, "Input message cannot be null");
    Objects.requireNonNull(key, "key cannot be null");

    if (input.length == 0) {
      throw new IllegalArgumentException("Length of message cannot be 0");
    }

    if (!ALLOWED_KEY_SIZES.contains(key.getEncoded().length * 8)) {
      throw new IllegalArgumentException("Size of key must be 128, 192 or 256");
    }

    Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);

    byte[] iv = getIV(IV_LEN);

    GCMParameterSpec gcmParamSpec = new GCMParameterSpec(AUTH_TAG_SIZE, iv);

    cipher.init(Cipher.ENCRYPT_MODE, key, gcmParamSpec);
    byte[] messageCipher = cipher.doFinal(input);

    // Prepend the IV with the message cipher
    byte[] cipherText = new byte[messageCipher.length + IV_LEN];
    System.arraycopy(iv, 0, cipherText, 0, IV_LEN);
    System.arraycopy(messageCipher, 0, cipherText, IV_LEN,
        messageCipher.length);
    return cipherText;
  }

  public byte[] decrypt(byte[] input, SecretKeySpec key) throws Exception {
    Objects.requireNonNull(input, "Input message cannot be null");
    Objects.requireNonNull(key, "key cannot be null");

    if (input.length == 0) {
      throw new IllegalArgumentException("Input array cannot be empty");
    }

    byte[] iv = new byte[IV_LEN];
    System.arraycopy(input, 0, iv, 0, IV_LEN);

    byte[] messageCipher = new byte[input.length - IV_LEN];
    System.arraycopy(input, IV_LEN, messageCipher, 0, input.length - IV_LEN);

    GCMParameterSpec gcmParamSpec = new GCMParameterSpec(AUTH_TAG_SIZE, iv);

    Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);
    cipher.init(Cipher.DECRYPT_MODE, key, gcmParamSpec);

    return cipher.doFinal(messageCipher);
  }

  public byte[] getIV(int bytesNum) {

    if (bytesNum < 1) throw new IllegalArgumentException(
        "Number of bytes must be greater than 0");

    byte[] iv = new byte[bytesNum];

    prng = Optional.ofNullable(prng).orElseGet(() -> {
      try {
        prng = SecureRandom.getInstanceStrong();
      } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException("Wrong algorithm name", e);
      }
      return prng;
    });

    if (bytesGenerated > PRNG_RESEED_INTERVAL || bytesGenerated == 0) {
      prng.setSeed(prng.generateSeed(bytesNum));
      bytesGenerated = 0;
    }

    prng.nextBytes(iv);
    bytesGenerated = bytesGenerated + bytesNum;

    return iv;
  }

  private static void clearSecret(Destroyable key)
      throws IllegalArgumentException, IllegalAccessException,
      NoSuchFieldException, SecurityException {
    Field keyField = key.getClass().getDeclaredField("key");
    keyField.setAccessible(true);
    byte[] encodedKey = (byte[]) keyField.get(key);
    Arrays.fill(encodedKey, Byte.MIN_VALUE);
  }
}

Khóa mã hóa có thể được tạo chủ yếu theo hai cách:

  • Không có mật khẩu

    KeyGenerator keyGen = KeyGenerator.getInstance("AES");
    keyGen.init(KEY_LEN, SecureRandom.getInstanceStrong());
    SecretKey secretKey = keyGen.generateKey();
    SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(),
        "AES");
    Crypto.clearSecret(secretKey);
    // After encryption or decryption with key
    Crypto.clearSecret(secretKeySpec);
  • Với mật khẩu

    SecureRandom random = SecureRandom.getInstanceStrong();
    byte[] salt = new byte[32];
    random.nextBytes(salt);
    PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterations, 
       keyLength);
    SecretKeyFactory keyFactory = 
        SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
    SecretKey secretKey = keyFactory.generateSecret(keySpec);
    SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(),
        "AES");
    Crypto.clearSecret(secretKey);
    // After encryption or decryption with key
    Crypto.clearSecret(secretKeySpec);

Cập nhật dựa trên nhận xét

Như @MaartenBodewes đã chỉ ra, câu trả lời của tôi không xử lý bất kỳ Stringcâu hỏi nào được yêu cầu. Do đó, tôi sẽ cố gắng lấp đầy khoảng trống đó trong trường hợp ai đó vấp phải câu trả lời này và để lại thắc mắc về việc xử lý String.

Như đã chỉ ra trước đó trong câu trả lời, việc xử lý thông tin nhạy cảm trong một String, nói chung, không phải là một ý tưởng tốt vì Stringkhông thay đổi và do đó chúng tôi không thể xóa thông tin đó sau khi sử dụng. Và như chúng ta biết, ngay cả khi Stringkhông có tài liệu tham khảo mạnh mẽ, người thu gom rác cũng không ngay lập tức vội vã loại bỏ nó. Do đó, việc Stringtiếp tục xuất hiện trong bộ nhớ trong một cửa sổ thời gian không xác định mặc dù chương trình không thể truy cập được. Vấn đề với điều đó là, một đống heap trong khung thời gian đó sẽ tiết lộ thông tin nhạy cảm. Do đó, tốt hơn hết là xử lý tất cả thông tin nhạy cảm trong một mảng byte hoặc mảng char và sau đó điền vào mảng bằng 0 khi mục đích của chúng được phục vụ.

Tuy nhiên, với tất cả kiến ​​thức đó, nếu chúng ta vẫn gặp phải tình huống thông tin nhạy cảm được mã hóa String, trước tiên chúng ta cần chuyển đổi nó thành một mảng byte và gọi các hàm encryptvà các decrypthàm được giới thiệu ở trên. (Khóa đầu vào khác có thể được tạo bằng đoạn mã được cung cấp ở trên).

A Stringcó thể được chuyển đổi thành byte theo cách sau:

byte[] inputBytes = inputString.getBytes(StandardCharsets.UTF_8);

Kể từ Java 8, Stringđược lưu trữ nội bộ trong heap với UTF-16mã hóa. Tuy nhiên, chúng tôi đã sử dụng UTF-8ở đây vì nó thường chiếm ít không gian hơn UTF-16, đặc biệt là đối với các ký tự ASCII.

Tương tự, mảng byte được mã hóa cũng có thể được chuyển đổi thành Chuỗi như dưới đây:

String encryptedString = new String(encryptedBytes, StandardCharsets.UTF_8);

1
Nhiều như tôi muốn nâng cao câu trả lời này vì nó có vẻ tuân thủ các thực tiễn về tiền điện tử hiện tại, tôi không thấy bất kỳ xử lý chuỗi nào cả, làm cho nó giống như một mô tả về cách sử dụng chế độ GCM. Như vậy nó không trả lời được câu hỏi .
Maarten Bodewes

1
@MaartenBodewes Cảm ơn rất nhiều vì đã dành thời gian để xem xét và chia sẻ phản hồi. Tôi đã viết điều này với sự hiểu biết rằng mã hóa Stringbằng cách sử dụng các chức năng được tạo ở trên sẽ là tầm thường. Tuy nhiên, trên một cái nhìn thứ hai sau khi đọc bình luận của bạn, tôi hiểu rằng nó có thể không rõ ràng. Tôi chắc chắn sẽ chỉnh sửa để thêm những chi tiết đó.
Saptarshi Basu

5

Còn cái này thì sao:

private static byte[] xor(final byte[] input, final byte[] secret) {
    final byte[] output = new byte[input.length];
    if (secret.length == 0) {
        throw new IllegalArgumentException("empty security key");
    }
    int spos = 0;
    for (int pos = 0; pos < input.length; ++pos) {
        output[pos] = (byte) (input[pos] ^ secret[spos]);
        ++spos;
        if (spos >= secret.length) {
            spos = 0;
        }
    }
    return output;
}

Hoạt động tốt cho tôi và khá nhỏ gọn.


Điều gì sẽ xảy ra nếu tham số mục bí mật == null hoặc input == null? làm việc với các byte thay vì sau đó với chuỗi là ok, nhưng không thích hợp trong trường hợp của tôi .. chỉ điều gì quan trọng là điều này phải được đọc và giải mã với bất kỳ thiết bị, trong bất kỳ ký tự mã hóa có thể ...
ante.sabo

@ ante.sabo rõ ràng, nó sẽ ném NPE. Đây là điều duy nhất để làm với NULL.
Miha_x64

Miễn là input.length <= secret.lengthgiữ và không secretbao giờ được sử dụng lại, điều này là an toàn và được gọi là a one-time-pad. Trong trường hợp input.length > secret.lengthnày là một biến thể của mật mã Vigenère và được coi là rất yếu.
trichner

5

Bạn có thể sử dụng Jasypt

Với Jasypt, mã hóa và kiểm tra mật khẩu có thể đơn giản như ...

StrongTextEncryptor textEncryptor = new StrongTextEncryptor();
textEncryptor.setPassword(myEncryptionPassword);

Mã hóa:

String myEncryptedText = textEncryptor.encrypt(myText);

Giải mã:

String plainText = textEncryptor.decrypt(myEncryptedText);

Học sinh lớp

compile group: 'org.jasypt', name: 'jasypt', version: '1.9.2'

Đặc trưng:

Jasypt cung cấp cho bạn các kỹ thuật mã hóa đơn hướng (tiêu hóa) và hai chiều dễ dàng.

API mở để sử dụng với bất kỳ nhà cung cấp JCE nào và không chỉ Java VM mặc định. Jasypt có thể dễ dàng được sử dụng với các nhà cung cấp nổi tiếng như Bouncy Castle. Tìm hiểu thêm.

Bảo mật cao hơn cho mật khẩu của người dùng của bạn. Tìm hiểu thêm.

Hỗ trợ mã hóa nhị phân. Jasypt cho phép tiêu hóa và mã hóa nhị phân (mảng byte). Mã hóa các đối tượng hoặc tệp của bạn khi cần (ví dụ để được gửi qua mạng).

Hỗ trợ mã hóa số. Bên cạnh các văn bản và nhị phân, nó cho phép tiêu hóa và mã hóa các giá trị số (BigInteger và BigDecimal, các loại số khác được hỗ trợ khi mã hóa cho tính bền vững của Hibernate). Tìm hiểu thêm.

Hoàn toàn chủ đề an toàn.

Hỗ trợ mã hóa / tổng hợp bộ mã hóa, để đạt được hiệu năng cao trong các hệ thống đa bộ xử lý / đa lõi.

Bao gồm một phiên bản nhẹ ("lite") của thư viện để quản lý tốt hơn trong các môi trường hạn chế kích thước như nền tảng di động.

Cung cấp cả công cụ mã hóa dễ dàng, không có cấu hình cho người dùng mới sử dụng mã hóa và cũng là công cụ mã hóa tiêu chuẩn có cấu hình cao, dành cho người dùng điện.

Tích hợp tùy chọn Hibernate 3 và 4 cho các trường bền vững của các thực thể được ánh xạ của bạn theo cách được mã hóa. Mã hóa các trường được xác định trong các tệp ánh xạ Hibernate và nó vẫn trong suốt cho phần còn lại của ứng dụng (hữu ích cho dữ liệu cá nhân nhạy cảm, cơ sở dữ liệu có nhiều người dùng kích hoạt đọc ...). Mã hóa văn bản, nhị phân, số, booleans, ngày ... Tìm hiểu thêm.

Tích hợp hoàn toàn vào một ứng dụng Spring, với các tính năng tích hợp cụ thể cho Spring 2, Spring 3.0 và Spring 3.1. Tất cả các digesters và mã hóa trong jasypt được thiết kế để dễ dàng sử dụng (khởi tạo, tiêm phụ thuộc ...) từ Spring. Và, vì chúng an toàn cho chuỗi, chúng có thể được sử dụng mà không phải lo lắng đồng bộ hóa trong môi trường định hướng đơn lẻ như Spring. Tìm hiểu thêm: Mùa xuân 2, Mùa xuân 3.0, Mùa xuân 3.1.

Tích hợp tùy chọn Spring Security (trước đây là Acegi Security) để thực hiện mã hóa mật khẩu và các tác vụ khớp với khung bảo mật, cải thiện bảo mật mật khẩu của người dùng bằng cách sử dụng các cơ chế mã hóa mật khẩu an toàn hơn và cung cấp cho bạn mức độ cấu hình và kiểm soát cao hơn. Tìm hiểu thêm.

Cung cấp chức năng nâng cao để mã hóa tất cả hoặc một phần tệp cấu hình của ứng dụng, bao gồm thông tin nhạy cảm như mật khẩu cơ sở dữ liệu. Tích hợp liền mạch cấu hình được mã hóa vào các ứng dụng đơn giản, dựa trên Spring và / hoặc Hibernate. Tìm hiểu thêm.

Cung cấp dễ dàng sử dụng các công cụ CLI (Giao diện dòng lệnh) để cho phép các nhà phát triển khởi tạo dữ liệu được mã hóa của họ và bao gồm các hoạt động mã hóa / giải mã / tiêu hóa trong các tác vụ hoặc tập lệnh bảo trì. Tìm hiểu thêm.

Tích hợp vào Apache Wicket, để mã hóa URL mạnh hơn trong các ứng dụng bảo mật của bạn.

Hướng dẫn toàn diện và tài liệu javadoc, để cho phép các nhà phát triển hiểu rõ hơn những gì họ thực sự làm với dữ liệu của họ.

Hỗ trợ bộ ký tự mạnh mẽ, được thiết kế để mã hóa đầy đủ và tiêu hóa các văn bản bất kể bộ ký tự gốc là gì. Hỗ trợ đầy đủ cho các ngôn ngữ như tiếng Nhật, tiếng Hàn, tiếng Ả Rập ... không có vấn đề về mã hóa hoặc nền tảng.

Khả năng cấu hình ở mức rất cao: Nhà phát triển có thể thực hiện các thủ thuật như hướng dẫn một "người mã hóa" để yêu cầu một máy chủ HTTPS từ xa cho mật khẩu được sử dụng để mã hóa. Nó cho phép bạn đáp ứng nhu cầu bảo mật của bạn.


1
Nhưng bảo mật nào Jasyptcung cấp? Tôi không thể tìm ra nó từ trang web của họ. Có phải nó không thể phân biệt được dưới các cuộc tấn công bằng văn bản được chọn? Chính trực? Bảo mật?
trichner

4

Đây là triển khai của tôi từ meta64.com với tư cách là một Singleton mùa xuân. Nếu bạn muốn tạo một cá thể ciper cho mỗi cuộc gọi cũng hoạt động, và sau đó bạn có thể xóa các cuộc gọi 'được đồng bộ hóa', nhưng hãy cẩn thận 'mật mã' không an toàn cho chuỗi.

import java.security.Key;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("singleton")
public class Encryptor {

    @Value("${aeskey}")
    private String keyStr;

    private Key aesKey = null;
    private Cipher cipher = null;

    synchronized private void init() throws Exception {
        if (keyStr == null || keyStr.length() != 16) {
            throw new Exception("bad aes key configured");
        }
        if (aesKey == null) {
            aesKey = new SecretKeySpec(keyStr.getBytes(), "AES");
            cipher = Cipher.getInstance("AES");
        }
    }

    synchronized public String encrypt(String text) throws Exception {
        init();
        cipher.init(Cipher.ENCRYPT_MODE, aesKey);
        return toHexString(cipher.doFinal(text.getBytes()));
    }

    synchronized public String decrypt(String text) throws Exception {
        init();
        cipher.init(Cipher.DECRYPT_MODE, aesKey);
        return new String(cipher.doFinal(toByteArray(text)));
    }

    public static String toHexString(byte[] array) {
        return DatatypeConverter.printHexBinary(array);
    }

    public static byte[] toByteArray(String s) {
        return DatatypeConverter.parseHexBinary(s);
    }

    /*
     * DO NOT DELETE
     * 
     * Use this commented code if you don't like using DatatypeConverter dependency
     */
    // public static String toHexStringOld(byte[] bytes) {
    // StringBuilder sb = new StringBuilder();
    // for (byte b : bytes) {
    // sb.append(String.format("%02X", b));
    // }
    // return sb.toString();
    // }
    //
    // public static byte[] toByteArrayOld(String s) {
    // int len = s.length();
    // byte[] data = new byte[len / 2];
    // for (int i = 0; i < len; i += 2) {
    // data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i +
    // 1), 16));
    // }
    // return data;
    // }
}

3
Điều này sẽ mã hóa với chế độ ECB thật kinh khủng. Bạn nên cài đặt ít nhất chế độ CBC hoặc Chế độ GCM
Konstantino Sparakis

Cảm ơn lời đề nghị Konstantinto, tôi đã tìm hiểu và tìm thấy một số mã sử dụng "AES / CBC / PKCS5Padding" làm chuỗi init cho Mật mã, thay vì chỉ "AES", nhưng tôi sẽ xem xét kỹ hơn về nó. Hoặc nếu bạn muốn bạn có thể cung cấp bản sửa lỗi thực tế, để những người khác có thể thấy cách tốt hơn. Tuy nhiên, ngoài chi tiết CBC, tôi tin rằng giải pháp của tôi là đơn giản và an toàn nhất, và xứng đáng được nâng cao hơn tất cả những thứ còn lại.

Không phải lo lắng, Crypto là một chủ đề phức tạp. Đáng buồn thay, mọi triển khai trên trang này đều bị hỏng và thật đáng buồn, đó là trang đầu tiên bật lên khi sử dụng google để tìm kiếm "cách thực hiện mã hóa java". Khi tôi có cơ hội tôi sẽ cố gắng sửa tất cả chúng.
Konstantino Sparakis

Ví dụ của tôi cũng giống như thế này: docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/ nam Ngoại trừ tôi cần Codes.getInstance ("AES / ECB / PKCS5Padding"); Mã của tôi giả sử có một số tệp thuộc tính có khóa mã hóa hoàn toàn dài 16 byte, nhưng để mã hóa một chuỗi từ mật khẩu 'do người dùng cung cấp', trang orory (được liên kết ở trên) cũng cho thấy cách thực hiện điều đó.

1
Vì vậy, vấn đề với ECB là nó cực kỳ dễ bị phân tích tần số. Có một ví dụ nổi tiếng về chim cánh cụt Linux, blog.filippo.io/the-ecb-penguin hãy xem mặc dù hình ảnh được mã hóa như thế nào bạn vẫn có thể nói rằng đó là một con chim cánh cụt. Tôi đã đi trước và viết những suy nghĩ của tôi về chủ đề bên dưới :) stackoverflow.com/a/43779197/2607972
Konstantino Sparakis

4

Đây là một giải pháp đơn giản chỉ có java.*javax.crypto.*phụ thuộc vào mã hóa byte cung cấp tính bảo mậttoàn vẹn . Nó sẽ không thể phân biệt được dưới một cuộc tấn công bằng văn bản được chọn cho các tin nhắn ngắn theo thứ tự kilobyte.

Nó sử dụng AEStrong GCMchế độ không có phần đệm, khóa 128 bit có nguồn gốc từ PBKDF2rất nhiều lần lặp và muối tĩnh từ mật khẩu được cung cấp. Điều này đảm bảo rằng mật khẩu buộc mật khẩu là khó và phân phối entropy trên toàn bộ khóa.

Một vectơ khởi tạo ngẫu nhiên (IV) được tạo ra và sẽ được thêm vào bản mã. Hơn nữa, byte tĩnh 0x01được đặt trước là byte đầu tiên dưới dạng 'phiên bản'.

Toàn bộ tin nhắn đi vào mã xác thực tin nhắn (MAC) được tạo bởi AES/GCM.

Ở đây, lớp mã hóa phụ thuộc bên ngoài bằng không cung cấp tính bảo mậttoàn vẹn :

package ch.n1b.tcrypt.utils;

import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

import javax.crypto.*;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * This class implements AES-GCM symmetric key encryption with a PBKDF2 derived password.
 * It provides confidentiality and integrity of the plaintext.
 *
 * @author Thomas Richner
 * @created 2018-12-07
 */
public class AesGcmCryptor {

    // /crypto/26783/ciphertext-and-tag-size-and-iv-transmission-with-aes-in-gcm-mode
    private static final byte VERSION_BYTE = 0x01;
    private static final int VERSION_BYTE_LENGTH = 1;
    private static final int AES_KEY_BITS_LENGTH = 128;


    // fixed AES-GCM constants
    private static final String GCM_CRYPTO_NAME = "AES/GCM/NoPadding";
    private static final int GCM_IV_BYTES_LENGTH = 12;
    private static final int GCM_TAG_BYTES_LENGTH = 16;

    // can be tweaked, more iterations = more compute intensive to brute-force password
    private static final int PBKDF2_ITERATIONS = 1024;

    // protects against rainbow tables
    private static final byte[] PBKDF2_SALT = hexStringToByteArray("4d3fe0d71d2abd2828e7a3196ea450d4");

    public String encryptString(char[] password, String plaintext) throws CryptoException {

        byte[] encrypted = null;
        try {
            encrypted = encrypt(password, plaintext.getBytes(StandardCharsets.UTF_8));
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException //
                | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException //
                | InvalidKeySpecException e) {
            throw new CryptoException(e);
        }
        return byteArrayToHexString(encrypted);
    }

    public String decryptString(char[] password, String ciphertext)
            throws CryptoException {

        byte[] ct = hexStringToByteArray(ciphertext);
        byte[] plaintext = null;
        try {
            plaintext = decrypt(password, ct);
        } catch (AEADBadTagException e) {
            throw new CryptoException(e);
        } catch ( //
                NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeySpecException //
                        | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException //
                        | BadPaddingException e) {
            throw new CryptoException(e);
        }
        return new String(plaintext, StandardCharsets.UTF_8);
    }

    /**
     * Decrypts an AES-GCM encrypted ciphertext and is
     * the reverse operation of {@link AesGcmCryptor#encrypt(char[], byte[])}
     *
     * @param password   passphrase for decryption
     * @param ciphertext encrypted bytes
     * @return plaintext bytes
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws InvalidKeySpecException
     * @throws InvalidAlgorithmParameterException
     * @throws InvalidKeyException
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws IllegalArgumentException           if the length or format of the ciphertext is bad
     * @throws CryptoException
     */
    public byte[] decrypt(char[] password, byte[] ciphertext)
            throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException,
            InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {

        // input validation
        if (ciphertext == null) {
            throw new IllegalArgumentException("ciphertext cannot be null");
        }

        if (ciphertext.length <= VERSION_BYTE_LENGTH + GCM_IV_BYTES_LENGTH + GCM_TAG_BYTES_LENGTH) {
            throw new IllegalArgumentException("ciphertext too short");
        }

        // the version must match, we don't decrypt other versions
        if (ciphertext[0] != VERSION_BYTE) {
            throw new IllegalArgumentException("wrong version: " + ciphertext[0]);
        }

        // input seems legit, lets decrypt and check integrity

        // derive key from password
        SecretKey key = deriveAesKey(password, PBKDF2_SALT, AES_KEY_BITS_LENGTH);

        // init cipher
        Cipher cipher = Cipher.getInstance(GCM_CRYPTO_NAME);
        GCMParameterSpec params = new GCMParameterSpec(GCM_TAG_BYTES_LENGTH * 8,
                ciphertext,
                VERSION_BYTE_LENGTH,
                GCM_IV_BYTES_LENGTH
        );
        cipher.init(Cipher.DECRYPT_MODE, key, params);

        final int ciphertextOffset = VERSION_BYTE_LENGTH + GCM_IV_BYTES_LENGTH;

        // add version and IV to MAC
        cipher.updateAAD(ciphertext, 0, ciphertextOffset);

        // decipher and check MAC
        return cipher.doFinal(ciphertext, ciphertextOffset, ciphertext.length - ciphertextOffset);
    }

    /**
     * Encrypts a plaintext with a password.
     * <p>
     * The encryption provides the following security properties:
     * Confidentiality + Integrity
     * <p>
     * This is achieved my using the AES-GCM AEAD blockmode with a randomized IV.
     * <p>
     * The tag is calculated over the version byte, the IV as well as the ciphertext.
     * <p>
     * Finally the encrypted bytes have the following structure:
     * <pre>
     *          +-------------------------------------------------------------------+
     *          |         |               |                             |           |
     *          | version | IV bytes      | ciphertext bytes            |    tag    |
     *          |         |               |                             |           |
     *          +-------------------------------------------------------------------+
     * Length:     1B        12B            len(plaintext) bytes            16B
     * </pre>
     * Note: There is no padding required for AES-GCM, but this also implies that
     * the exact plaintext length is revealed.
     *
     * @param password  password to use for encryption
     * @param plaintext plaintext to encrypt
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws NoSuchPaddingException
     * @throws InvalidAlgorithmParameterException
     * @throws InvalidKeyException
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws InvalidKeySpecException
     */
    public byte[] encrypt(char[] password, byte[] plaintext)
            throws NoSuchAlgorithmException, NoSuchPaddingException,
            InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException,
            InvalidKeySpecException {

        // initialise random and generate IV (initialisation vector)
        SecretKey key = deriveAesKey(password, PBKDF2_SALT, AES_KEY_BITS_LENGTH);
        final byte[] iv = new byte[GCM_IV_BYTES_LENGTH];
        SecureRandom random = SecureRandom.getInstanceStrong();
        random.nextBytes(iv);

        // encrypt
        Cipher cipher = Cipher.getInstance(GCM_CRYPTO_NAME);
        GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_BYTES_LENGTH * 8, iv);
        cipher.init(Cipher.ENCRYPT_MODE, key, spec);

        // add IV to MAC
        final byte[] versionBytes = new byte[]{VERSION_BYTE};
        cipher.updateAAD(versionBytes);
        cipher.updateAAD(iv);

        // encrypt and MAC plaintext
        byte[] ciphertext = cipher.doFinal(plaintext);

        // prepend VERSION and IV to ciphertext
        byte[] encrypted = new byte[1 + GCM_IV_BYTES_LENGTH + ciphertext.length];
        int pos = 0;
        System.arraycopy(versionBytes, 0, encrypted, 0, VERSION_BYTE_LENGTH);
        pos += VERSION_BYTE_LENGTH;
        System.arraycopy(iv, 0, encrypted, pos, iv.length);
        pos += iv.length;
        System.arraycopy(ciphertext, 0, encrypted, pos, ciphertext.length);

        return encrypted;
    }

    /**
     * We derive a fixed length AES key with uniform entropy from a provided
     * passphrase. This is done with PBKDF2/HMAC256 with a fixed count
     * of iterations and a provided salt.
     *
     * @param password passphrase to derive key from
     * @param salt     salt for PBKDF2 if possible use a per-key salt, alternatively
     *                 a random constant salt is better than no salt.
     * @param keyLen   number of key bits to output
     * @return a SecretKey for AES derived from a passphrase
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    private SecretKey deriveAesKey(char[] password, byte[] salt, int keyLen)
            throws NoSuchAlgorithmException, InvalidKeySpecException {

        if (password == null || salt == null || keyLen <= 0) {
            throw new IllegalArgumentException();
        }
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        KeySpec spec = new PBEKeySpec(password, salt, PBKDF2_ITERATIONS, keyLen);
        SecretKey pbeKey = factory.generateSecret(spec);

        return new SecretKeySpec(pbeKey.getEncoded(), "AES");
    }

    /**
     * Helper to convert hex strings to bytes.
     * <p>
     * May be used to read bytes from constants.
     */
    private static byte[] hexStringToByteArray(String s) {

        if (s == null) {
            throw new IllegalArgumentException("Provided `null` string.");
        }

        int len = s.length();
        if (len % 2 != 0) {
            throw new IllegalArgumentException("Invalid length: " + len);
        }

        byte[] data = new byte[len / 2];
        for (int i = 0; i < len - 1; i += 2) {
            byte b = (byte) toHexDigit(s, i);
            b <<= 4;
            b |= toHexDigit(s, i + 1);
            data[i / 2] = b;
        }
        return data;
    }

    private static int toHexDigit(String s, int pos) {
        int d = Character.digit(s.charAt(pos), 16);
        if (d < 0) {
            throw new IllegalArgumentException("Cannot parse hex digit: " + s + " at " + pos);
        }
        return d;
    }

    private static String byteArrayToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X", b));
        }
        return sb.toString();
    }

    public class CryptoException extends Exception {

        public CryptoException(Throwable cause) {
            super(cause);
        }
    }
}

Ở đây toàn bộ dự án với một CLI đẹp: https://github.com/trichner/tcrypt

Chỉnh sửa: bây giờ với thích hợp encryptStringdecryptString


Điều này thật phi thường. Cảm ơn bạn! Tôi đã học được rất nhiều từ mã của bạn và sau khi tạo lớp BadVersionException Exception, mã của bạn đã hoạt động hoàn hảo ngay lần đầu tiên. Thông minh!!
Morkus

Tôi thích nỗ lực này. Điều đó nói rằng ... Muối nên là ngẫu nhiên, không tĩnh. Lặp đi lặp lại có lẽ cũng không nên tĩnh. GCM đã bao gồm IV trong tính toán của thẻ. Nó không chứa số phiên bản mặc dù. Bạn không nên chỉ định nhà cung cấp cho tính di động, "SunJCE" sẽ là mặc định trên các nền tảng hỗ trợ nó. Mã này không chứa bất kỳ xử lý chuỗi thông báo, cần thiết cho câu hỏi cụ thể này .
Maarten Bodewes

Được rồi, tôi dọn dẹp thêm một chút và thêm yêu cầu encryptStringdecryptString:)
trichner

Điều này làm việc rất tốt; ty cho mã. Cần lưu ý rằng mã này yêu cầu API 19 (Kit Kat) trở lên để hoạt động đúng.
PGMacDesign

3

Tôi sẽ cân nhắc sử dụng một cái gì đó như https://www.bouncycastle.org/ Đây là một thư viện dựng sẵn cho phép bạn mã hóa bất cứ thứ gì bạn thích bằng một số Mật mã khác nhau Tôi hiểu rằng bạn chỉ muốn bảo vệ khỏi việc rình mò, nhưng nếu bạn thực sự muốn bảo vệ thông tin, sử dụng Base64 sẽ không thực sự bảo vệ bạn.


1
Chỉ cần đề xuất một thư viện mật mã ngẫu nhiên với mật mã không phải là một câu trả lời cho câu hỏi. Bên cạnh đó, tại sao không sử dụng mật mã tích hợp?
Maarten Bodewes

2

Dưới đây là một số liên kết bạn có thể đọc những gì Java hỗ trợ

Mã hóa / giải mã một luồng dữ liệu.

Ví dụ này cho thấy cách mã hóa (sử dụng thuật toán mã hóa đối xứng như AES, Blowfish, RC2, 3DES, v.v.) một lượng lớn dữ liệu. Dữ liệu được truyền theo từng khối cho một trong các phương thức mã hóa: EncryptBytes, EncryptString, EncryptBytesENC hoặc EncryptStringENC. (Tên phương thức cho biết loại đầu vào (chuỗi hoặc mảng byte) và loại trả về (chuỗi được mã hóa hoặc mảng byte). Các thuộc tính FirstChunk và LastChunk được sử dụng để chỉ ra một đoạn là đầu tiên, giữa hoặc cuối trong luồng được mã hóa. Theo mặc định, cả FirstChunk và LastChunk đều đúng - có nghĩa là dữ liệu được truyền là toàn bộ số tiền.

JCERefGuide

Ví dụ mã hóa Java


Vâng, có mật mã được Java hỗ trợ. Mã hóa của một luồng không phải là những gì được yêu cầu.
Maarten Bodewes

2

Giống như nhiều người đã nói, bạn nên sử dụng một cypher tiêu chuẩn được sử dụng quá mức như DES hoặc AES.

Một ví dụ đơn giản về cách bạn có thể mã hóa và giải mã một chuỗi trong java bằng AES .

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class EncryptorDemo {

    public static String encrypt(String key, String randomVector, String value) {
        try {
            IvParameterSpec iv = new IvParameterSpec(randomVector.getBytes("UTF-8"));
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
            byte[] encrypted = cipher.doFinal(value.getBytes());
            System.out.println("encrypted text: "  + Base64.encodeBase64String(encrypted));
            return Base64.encodeBase64String(encrypted);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String decrypt(String key, String randomVector, String encrypted) {
        try {
            IvParameterSpec iv = new IvParameterSpec(randomVector.getBytes("UTF-8"));
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            byte[] originalText = cipher.doFinal(Base64.decodeBase64(encrypted));
            System.out.println("decrypted text: "  + new String(originalText));
            return new String(originalText);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        String key = "JavasEncryptDemo"; // 128 bit key
        String randomVector = "RandomJavaVector"; // 16 bytes IV
        decrypt(key, randomVector, encrypt(key, randomVector, "Anything you want to encrypt!"));

    }
}

CBC không còn là chế độ bảo mật. Đệm là dễ bị tấn công đệm Oracle. Ngoài ra, xử lý khóa và tin nhắn trong Chuỗi không an toàn. Họ sẽ nán lại trong nhóm String và xuất hiện trong một đống chất đống
Saptarshi Basu

2
Đánh giá cao các bình luận. Đây là một ví dụ đơn giản về các phương thức mã hóa và giải mã của Java như người dùng đang hỏi. Câu hỏi đã được hỏi khoảng 9 năm trước và được trả lời dựa trên điều đó. Cảm ơn.
viveknaskar

2
Có, đây có vẻ là một cách đơn giản để giới thiệu mã hóa / giải mã. Làm việc như một cơ duyên đối với tôi .... Cảm ơn.
Máy đánh mã

0

Đây là một giải pháp sao chép / dán. Tôi cũng khuyên bạn nên đọc và bỏ phiếu cho câu trả lời của @ Konstantino mặc dù nó không cung cấp bất kỳ mã nào. Vectơ khởi tạo (IV) giống như một muối - nó không cần phải giữ bí mật. Tôi chưa quen với GCM và rõ ràng AAD là tùy chọn và chỉ được sử dụng trong một số trường hợp nhất định. Đặt khóa trong biến môi trường SECRET_KEY_BASE. Sử dụng một cái gì đó như KeePass để tạo mật khẩu 32 ký tự. Giải pháp này được mô hình hóa sau giải pháp Ruby của tôi.

    public static String encrypt(String s) {
        try {
            byte[] input = s.getBytes("UTF-8");
            String keyString = System.getProperty("SECRET_KEY_BASE", System.getenv("SECRET_KEY_BASE"));
            if (keyString == null || keyString.length() == 0) {
                Logger.error(Utils.class, "encrypt()", "$SECRET_KEY_BASE is not set.");
                return null;
            }
            byte[] keyBytes = keyString.getBytes("UTF-8");
            SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
            // generate IV
            SecureRandom secureRandom = SecureRandom.getInstanceStrong();
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            byte[] ivBytes = new byte[cipher.getBlockSize()];
            secureRandom.nextBytes(ivBytes);
            GCMParameterSpec gcmSpec = new GCMParameterSpec(96, ivBytes); // 96 bit tag length
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec);
            // generate AAD
//          byte[] aadBytes = new byte[cipher.getBlockSize()];
//          secureRandom.nextBytes(aadBytes);
//          cipher.updateAAD(aadBytes);
            // encrypt
            byte[] encrypted = cipher.doFinal(input);
            byte[] returnBytes = new byte[ivBytes.length + encrypted.length];
//          byte[] returnBytes = new byte[ivBytes.length + aadBytes.length + encrypted.length];
            System.arraycopy(ivBytes, 0, returnBytes, 0, ivBytes.length);
//          System.arraycopy(aadBytes, 0, returnBytes, ivBytes.length, aadBytes.length);
            System.arraycopy(encrypted, 0, returnBytes, ivBytes.length, encrypted.length);
//          System.arraycopy(encrypted, 0, returnBytes, ivBytes.length+aadBytes.length, encrypted.length);
            String encryptedString = Base64.getEncoder().encodeToString(returnBytes);
            return encryptedString;
        } catch (UnsupportedEncodingException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | 
                InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
            Logger.error(Utils.class, "encrypt()", "Could not encrypt string: " + e.getMessage());
            return null;
        }
    }

    public static String decrypt(String s) {
        if (s == null || s.length() == 0) return "";
        try {
            byte[] encrypted = Base64.getDecoder().decode(s);
            String keyString = System.getProperty("SECRET_KEY_BASE", System.getenv("SECRET_KEY_BASE"));
            if (keyString == null || keyString.length() == 0) {
                Logger.error(Utils.class, "encrypt()", "$SECRET_KEY_BASE is not set.");
                return null;
            }
            byte[] keyBytes = keyString.getBytes("UTF-8");
            SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            byte[] ivBytes = new byte[cipher.getBlockSize()];
            System.arraycopy(encrypted, 0, ivBytes, 0, ivBytes.length);
            GCMParameterSpec gcmSpec = new GCMParameterSpec(96, ivBytes);
            cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec);
//          cipher.updateAAD(encrypted, ivBytes.length, cipher.getBlockSize());
            byte[] decrypted = cipher.doFinal(encrypted, cipher.getBlockSize(), encrypted.length - cipher.getBlockSize());
//          byte[] decrypted = cipher.doFinal(encrypted, cipher.getBlockSize()*2, encrypted.length - cipher.getBlockSize()*2);
            String decryptedString = new String(decrypted, "UTF-8");
            return decryptedString;
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | UnsupportedEncodingException | InvalidKeyException | 
                InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
            Logger.error(Utils.class, "decrypt()", "Could not decrypt string: " + e.getMessage());
            return null;
        }
    }

Đây là một ví dụ:

    String s = "This is a test.";
    String enc = Utils.encrypt(s);
    System.out.println(enc);
    // fQHfYjbD+xAuN5XzH2ojk/EWNeKXUrKRSfx8LU+5dpuKkM/pueCMBjKCZw==
    String dec = Utils.decrypt(enc);
    System.out.println(dec);
    // This is a test.

-4

Bạn có thể muốn xem xét một số công cụ tự động để thực hiện việc tạo mã mã hóa / giải mã, vd. https://www.opesencrypt.com/java-encoding/

Nó có thể tạo mã hóa và mã hóa khác nhau mỗi lần cho mã hóa chuỗi hoặc tệp.

Nó khá tiện dụng khi mã hóa chuỗi nhanh mà không cần sử dụng RSA, AES, v.v.

Kết quả mẫu:

// encrypted with https://www.stringencrypt.com (v1.1.0) [Java]
// szTest = "Encryption in Java!"
String szTest = "\u9E3F\uA60F\uAE07\uB61B\uBE1F\uC62B\uCE2D\uD611" +
                "\uDE03\uE5FF\uEEED\uF699\uFE3D\u071C\u0ED2\u1692" +
                "\u1E06\u26AE\u2EDC";

for (int iatwS = 0, qUJQG = 0; iatwS < 19; iatwS++)
{
        qUJQG = szTest.charAt(iatwS);
        qUJQG ++;
        qUJQG = ((qUJQG << 5) | ( (qUJQG & 0xFFFF) >> 11)) & 0xFFFF;
        qUJQG -= iatwS;
        qUJQG = (((qUJQG & 0xFFFF) >> 6) | (qUJQG << 10)) & 0xFFFF;
        qUJQG ^= iatwS;
        qUJQG -= iatwS;
        qUJQG = (((qUJQG & 0xFFFF) >> 3) | (qUJQG << 13)) & 0xFFFF;
        qUJQG ^= 0xFFFF;
        qUJQG ^= 0xB6EC;
        qUJQG = ((qUJQG << 8) | ( (qUJQG & 0xFFFF) >> 8)) & 0xFFFF;
        qUJQG --;
        qUJQG = (((qUJQG & 0xFFFF) >> 5) | (qUJQG << 11)) & 0xFFFF;
        qUJQG ++;
        qUJQG ^= 0xFFFF;
        qUJQG += iatwS;
        szTest = szTest.substring(0, iatwS) + (char)(qUJQG & 0xFFFF) + szTest.substring(iatwS + 1);
}

System.out.println(szTest);

Chúng tôi sử dụng nó tất cả thời gian trong công ty của chúng tôi.


Đây là bảo mật thông qua che khuất và không thực sự an toàn.
Chloe

Câu hỏi này đang yêu cầu mã hóa cường độ mã hóa hiện đại như AES, không chỉ là che giấu để làm cho các chuỗi khó hơn để trích xuất tĩnh. Điều này thậm chí không xuất hiện để giữ bất kỳ trạng thái nào giữa các ký tự nên dễ bị phân tích tần số. ( Mật mã thay thế bảng chữ cái đơn , ngoại trừ mã hóa UTF-16 thay vì bảng chữ cái Latinh. Nhưng nếu bạn sử dụng nó trên văn bản ASCII tiếng Anh, bạn chỉ nhận được một vài giá trị ký tự 16 bit duy nhất, trừ khi tôi đọc sai điều này)
Peter Dây

-4
String s1="arshad"; 
char[] s2=s1.toCharArray(); 
int s3= s2.length; 

  System.out.println(s3);
 int i=0; 

// for(int j=0;j<s3;j++) 
// System.out.println(s2[j]); 

for(i=0;i<((s3)/2);i++) { 

char z,f=10; 
z=(char) (s2[i] * f); 
s2[i]=s2[(s3-1)-i]; 
s2[(s3-1)-i]=z; 

String b=new String(s2);

 print(b);  }

Chính thức nó mã hóa dữ liệu thành định dạng không thể đọc được. Để giải mã sử dụng cùng một mã. Và thay đổi s [i] * f thành s [I] / f.
Arshad shaik

Đây là bảo mật thông qua che khuất và không thực sự an toàn.
Chloe

-5
public static String encryptParams(String myTextInput) {

        String myKey = "40674244454045cb9a70040a30e1c007";
        String myVector = "@1B2c3D4e5F6g7H8";

        String encData = "";

        try{
            JavaEncryprtionUtil encUtil = new JavaEncryprtionUtil();
            encData = Base64.encodeToString(encUtil.encrypt(myTextInput.getBytes("UTF-8"), myKey.getBytes("UTF-8"), myVector.getBytes("UTF-8")),Base64.DEFAULT);
            System.out.println(encData);
        }catch(NoSuchAlgorithmException ex){
            ex.printStackTrace();
        }catch(NoSuchPaddingException ex){
            ex.printStackTrace();
        }catch(InvalidKeyException ex){
            ex.printStackTrace();
        }catch(InvalidAlgorithmParameterException ex){
            ex.printStackTrace();
        }catch(IllegalBlockSizeException ex){
            ex.printStackTrace();
        }catch(BadPaddingException ex){
            ex.printStackTrace();
        }catch(UnsupportedEncodingException ex){
            ex.printStackTrace();
        }

        return encData;
    }

1
JavaEncryprtionUtil là một phần của API JDK? nếu không bạn nên đánh vần tên của thư viện.
Học sinh nhỏ của Fermat

4
Không thể tìm thấy lớp học đó. Cảm thấy như câu trả lời được tạo thành.
james.garriss
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.