Java - Mã hóa và giải mã với thuật toán RSA

Công nghệ mã hóa

Mã hóa và giải mã dữ liệu là kỹ thuật nhằm bảo mật dữ liệu, xác thực và đảm bảo tính toàn vẹn của thông tin được truyền trên các phương tiện truyền thông. Mã hóa là thao tác chuyển đổi văn bản hoặc dữ liệu ban đầu thành một hình thức mã hóa hay các bản mã bằng cách áp dụng các biến đổi toán học. Ngược lại với thao tác này là giải mã. Giải mã truy xuất dữ liệu gốc từ các bản mã sử dụng một khóa giải mã.

Khi nói đến mã hóa, chúng ta dễ liên tưởng đến những bộ phim trình chiếu những đoạn mã số dài chạy nhấp nháy trên màn hình với những thông điệp khó hiểu. Hay cuộc chiến giữa Apple và FBI về vụ thông tin bị mã hóa mà chính phủ Mỹ buộc Apple phải giải mã thông tin trên chiếc iPhone của thủ phạm trong vụ xả súng ở San Bernardino, Mỹ. Nói đơn giản, mã hóa là một kỹ thuật làm cho nội dung không thể đọc được đối với bất kỳ ai không có chìa khóa. Ta có nghe thấy những thông tin như gián điệp sử dụng mã hóa để gửi thông tin bí mật, các tướng chỉ huy quân đội gửi nội dung mã hóa để phối hợp chiến đấu cho đồng bộ, còn tội phạm sử dụng mã hóa để trao đổi thông tin và lên kế hoạch hành động, tin nhắn của người dùng được mã hóa để kể cả quản trị tối cao của hệ thống cũng không đọc được...

Các hệ thống mã hóa cũng xuất hiện trong hầu hết mọi lĩnh vực liên quan đến công nghệ, không chỉ che dấu thông tin của tội phạm, kẻ thù hay gián điệp mà còn xác thực và làm rõ những thông tin rất cơ bản, rất cá nhân.

Mã hóa và giải mã dữ liệu trong java – Các thuật toán mã hoá cơ bản

Có nhiều thuật toán mã hóa được phổ biến rộng rãi và được sử dụng trong an ninh thông tin. Chúng được phân loại gồm mã hoá khoá đối xứng và mã hoá khoá không đối xứng. Trong mã hóa khóa đối xứng chỉ có một khoá (key) được sử dụng để mã hóa và giải mã dữ liệu. Trong mã hoá khóa bất đối xứng sử dụng hai khoá là khóa riêng và khoá công khai. Trong đó khóa công khai được sử dụng để mã hóa và khóa riêng được sử dụng để giải mã. Bảng bên dưới trình bày các thuật toán mã hoá phổ biến.

AES DES RSA
Developed 2000 1977 1978
Key Size 128, 192, 256 bits 56 bits >1024 bits
Algorithm Đối xứng (Symmetric) Đối xứng (Symmetric) Bất đối xứng (Asymmetric)

Tuy nhiên trong bài này mình sẽ sử dụng RSA để mô tả 😄

Mã hóa và giải mã dữ liệu trong java(RSA) – Cách thức hoạt động

Thuật toán RSA có hai khóa: khóa công khai (public key) và khóa bí mật (private key). Mỗi khóa là những số cố định sử dụng trong quá trình mã hóa và giải mã. Khóa công khai được công bố rộng rãi cho mọi người và được dùng để mã hóa. Những thông tin được mã hóa bằng khóa công khai chỉ có thể được giải mã bằng khóa bí mật tương ứng. Nói cách khác, mọi người đều có thể mã hóa nhưng chỉ có người biết khóa cá nhân (bí mật) mới có thể giải mã được. Ta có thể mô phỏng trực quan một hệ mật mã khoá công khai như sau: Bob muốn gửi cho Alice một thông tin mật mà Bob muốn duy nhất Alice có thể đọc được. Để làm được điều này, Alice gửi cho Bob một chiếc hộp có khóa đã mở sẵn và giữ lại chìa khóa. Bob nhận chiếc hộp, cho vào đó một tờ giấy viết thư bình thường và khóa lại (như loại khoá thông thường chỉ cần sập chốt lại, sau khi sập chốt khóa ngay cả Bob cũng không thể mở lại được-không đọc lại hay sửa thông tin trong thư được nữa). Sau đó Bob gửi chiếc hộp lại cho Alice. Alice mở hộp với chìa khóa của mình và đọc thông tin trong thư. Trong ví dụ này, chiếc hộp với khóa mở đóng vai trò khóa công khai, chiếc chìa khóa chính là khóa bí mật.

Trên đây chỉ là mô tả sơ lược về RSA, các bạn có thể đọc chi tiết hơn về thuật toán này tại wikipedia.

Ví dụ về mã hóa và giải mã(RSA) một chuỗi kí tự

  1. Tạo cặp khoá
package vuta.cryptography.demo;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;

public class SecurityKeyPairGenerator {

	public static void main(String[] args) {
		try {
			SecureRandom sr = new SecureRandom();
			// Thuật toán phát sinh khóa - RSA
			// Độ dài khóa 1024(bits), độ dài khóa này quyết định đến độ an toàn của khóa, càng lớn thì càng an toàn
            // Demo chỉ sử dụng 1024 bit. Nhưng theo khuyến cáo thì độ dài khóa nên tối thiểu là 2048
			KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
			kpg.initialize(1024, sr);

			// Khởi tạo cặp khóa
			KeyPair kp = kpg.genKeyPair();
			// PublicKey
			PublicKey publicKey = kp.getPublic();
			// PrivateKey
			PrivateKey privateKey = kp.getPrivate();

			File publicKeyFile = createKeyFile(new File("C:/publicKey.rsa"));
			File privateKeyFile = createKeyFile(new File("C:/privateKey.rsa"));

			// Lưu Public Key
			FileOutputStream fos = new FileOutputStream(publicKeyFile);
			fos.write(publicKey.getEncoded());
			fos.close();

			// Lưu Private Key
			fos = new FileOutputStream(privateKeyFile);
			fos.write(privateKey.getEncoded());
			fos.close();

			System.out.println("Generate key successfully");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private static File createKeyFile(File file) throws IOException {
		if (!file.exists()) {
			file.createNewFile();
		} else {
			file.delete();
			file.createNewFile();
		}
		return file;
	}
}
  1. Mã hoá dữ liệu
package vuta.cryptography.demo;

import java.io.FileInputStream;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

import javax.crypto.Cipher;

public class Encrpytion {

	public static void main(String[] args) {
		try {
			// Đọc file chứa public key
			FileInputStream fis = new FileInputStream("C:/publicKey.rsa");
			byte[] b = new byte[fis.available()];
			fis.read(b);
			fis.close();

			// Tạo public key
			X509EncodedKeySpec spec = new X509EncodedKeySpec(b);
			KeyFactory factory = KeyFactory.getInstance("RSA");
			PublicKey pubKey = factory.generatePublic(spec);

			// Mã hoá dữ liệu
			Cipher c = Cipher.getInstance("RSA");
			c.init(Cipher.ENCRYPT_MODE, pubKey);
			String msg = "helloworld";
			byte encryptOut[] = c.doFinal(msg.getBytes());
			String strEncrypt = Base64.getEncoder().encodeToString(encryptOut);
			System.out.println("Chuỗi sau khi mã hoá: " + strEncrypt);

		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
}

Chuỗi sau khi mã hoá: RR8WsVCiTUkm67vY8dSfv+eJ1h2JLEulXQZf4t7rxP8HynxMKrYcAmGvIYsrUb77ys4K8uUj48ayT3bSsM3wfnoJLtgww2idNB7r8UeIyIGe/UKoO0co5aJoptt8NwuKNCS0uf7fEEZnAfB1rszXqKQj0IxOdCtYLorO7DltwDM=

  1. Giải mã dữ liệu
package vuta.cryptography.demo;

import java.io.FileInputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

import javax.crypto.Cipher;

public class Decryption {

	public static void main(String[] args) {
		try {
			// Đọc file chứa private key
			FileInputStream fis = new FileInputStream("C:/privateKey.rsa");
			byte[] b = new byte[fis.available()];
			fis.read(b);
			fis.close();

			// Tạo private key
			PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(b);
			KeyFactory factory = KeyFactory.getInstance("RSA");
			PrivateKey priKey = factory.generatePrivate(spec);

			// Giải mã dữ liệu
			Cipher c = Cipher.getInstance("RSA");
			c.init(Cipher.DECRYPT_MODE, priKey);
			byte decryptOut[] = c.doFinal(Base64.getDecoder().decode(
					"RR8WsVCiTUkm67vY8dSfv+eJ1h2JLEulXQZf4t7rxP8HynxMKrYcAmGvIYsrUb77ys4K8uUj48ayT3bSsM3wfnoJLtgww2idNB7r8UeIyIGe/UKoO0co5aJoptt8NwuKNCS0uf7fEEZnAfB1rszXqKQj0IxOdCtYLorO7DltwDM="));
			System.out.println("Dữ liệu sau khi giải mã: " + new String(decryptOut));
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
}

Dữ liệu sau khi giải mã: helloworld

Qua bài viết này hy vọng có thể phần nào giúp các bạn có cái nhìn cơ bản về mã hóa 😃.