添加Logon框和RSA加密模块。
This commit is contained in:
		| @@ -1,8 +1,8 @@ | ||||
| #Created by .winboll/winboll_app_build.gradle | ||||
| #Wed Jun 04 05:15:44 GMT 2025 | ||||
| #Wed Jun 04 06:55:23 GMT 2025 | ||||
| stageCount=6 | ||||
| libraryProject=libappbase | ||||
| baseVersion=15.8 | ||||
| publishVersion=15.8.5 | ||||
| buildCount=10 | ||||
| buildCount=24 | ||||
| baseBetaVersion=15.8.6 | ||||
|   | ||||
| @@ -105,6 +105,8 @@ | ||||
|  | ||||
|         <activity android:name="cc.winboll.studio.libappbase.activities.YunActivity"/> | ||||
|  | ||||
|         <activity android:name="cc.winboll.studio.libappbase.activities.LogonActivity"/> | ||||
|  | ||||
|     </application> | ||||
|  | ||||
| </manifest> | ||||
| </manifest> | ||||
|   | ||||
| @@ -0,0 +1,109 @@ | ||||
| package cc.winboll.studio.libappbase.activities; | ||||
|  | ||||
| import android.app.Activity; | ||||
| import android.os.Bundle; | ||||
| import android.view.View; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
| import cc.winboll.studio.libappbase.LogView; | ||||
| import cc.winboll.studio.libappbase.R; | ||||
| import cc.winboll.studio.libappbase.utils.RSAUtils; | ||||
| import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity; | ||||
| import java.security.KeyPair; | ||||
| import java.security.PrivateKey; | ||||
| import java.security.PublicKey; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Date 2025/06/04 13:29 | ||||
|  * @Describe 用户登录框 | ||||
|  */ | ||||
| public class LogonActivity extends Activity implements IWinBoLLActivity { | ||||
|  | ||||
|     public static final String TAG = "LogonActivity"; | ||||
|  | ||||
|     LogView mLogView; | ||||
|  | ||||
|     @Override | ||||
|     public Activity getActivity() { | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String getTag() { | ||||
|         return TAG; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         setContentView(R.layout.activity_logon); | ||||
|         mLogView = findViewById(R.id.logview); | ||||
|         mLogView.start(); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onResume() { | ||||
|         super.onResume(); | ||||
|         mLogView.start(); | ||||
|     } | ||||
|  | ||||
|     public void onTestRSA(View view) { | ||||
|         LogUtils.d(TAG, "onTestRSA"); | ||||
|         String keyPath = getFilesDir() + "/home/keys/"; // 密钥保存路径 | ||||
|         LogUtils.d(TAG, String.format("keyPath %s", keyPath)); | ||||
|         RSAUtils utils = RSAUtils.getInstance(); | ||||
|  | ||||
|         try { | ||||
|             // 测试 1:首次生成密钥对 | ||||
|             LogUtils.d(TAG, "==== 首次生成密钥对 ===="); | ||||
|             if (utils.keysExist(keyPath)) { | ||||
|                 LogUtils.d(TAG, "密钥对已生成"); | ||||
|             } else { | ||||
|                 utils.generateAndSaveKeys(keyPath); | ||||
|                 LogUtils.d(TAG, "密钥对生成成功,保存至:" + keyPath); | ||||
|             } | ||||
|  | ||||
|             // 测试 2:获取密钥对(自动读取已生成的文件) | ||||
|             KeyPair keyPair = utils.getOrGenerateKeys(keyPath); | ||||
|             PublicKey publicKey = keyPair.getPublic(); | ||||
|             PrivateKey privateKey = keyPair.getPrivate(); | ||||
|  | ||||
|             // 打印密钥信息 | ||||
|             LogUtils.d(TAG, "\n==== 密钥信息 ===="); | ||||
|             LogUtils.d(TAG, "公钥算法:" + publicKey.getAlgorithm()); | ||||
|             LogUtils.d(TAG, "公钥编码长度:" + publicKey.getEncoded().length + "字节"); | ||||
|             LogUtils.d(TAG, "私钥算法:" + privateKey.getAlgorithm()); | ||||
|             LogUtils.d(TAG, "私钥编码长度:" + privateKey.getEncoded().length + "字节"); | ||||
|  | ||||
|             // 测试 3:重复调用时检查是否复用文件 | ||||
|             LogUtils.d(TAG, "\n==== 二次调用 ===="); | ||||
|             KeyPair reusedPair = utils.getOrGenerateKeys(keyPath); | ||||
|             LogUtils.d(TAG, "是否为同一公钥:" + (publicKey.equals(reusedPair.getPublic()))); // true(单例引用) | ||||
|             LogUtils.d(TAG, "操作完成"); | ||||
|  | ||||
|             String testMessage = "Hello, RSA Encryption!"; | ||||
|  | ||||
|             // 1. 获取或生成密钥对 | ||||
|             PublicKey publicKeyReused = reusedPair.getPublic(); | ||||
|             PrivateKey privateKeyReused = reusedPair.getPrivate(); | ||||
|  | ||||
|             // 2. 公钥加密 | ||||
|             byte[] encryptedData = utils.encryptWithPublicKey(testMessage, publicKeyReused); | ||||
|             LogUtils.d(TAG, "加密后数据(字节长度):" + encryptedData.length); | ||||
|  | ||||
|             // 3. 私钥解密 | ||||
|             String decryptedMessage = utils.decryptWithPrivateKey(encryptedData, privateKeyReused); | ||||
|             LogUtils.d(TAG, "解密结果:" + decryptedMessage); | ||||
|  | ||||
|             // 4. 验证解密是否成功 | ||||
|             if (testMessage.equals(decryptedMessage)) { | ||||
|                 LogUtils.d(TAG, "加密解密测试通过!"); | ||||
|             } else { | ||||
|                 LogUtils.d(TAG, "测试失败:内容不一致"); | ||||
|             } | ||||
|         } catch (Exception e) { | ||||
|             LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -17,6 +17,8 @@ public class APPModel extends BaseBean { | ||||
|     // 应用是否处于正在调试状态 | ||||
|     // | ||||
|     boolean isDebuging = false; | ||||
|     // 用本机 RSA 加密后保存的令牌 | ||||
|     String rsaToken = ""; | ||||
|  | ||||
|     public APPModel() { | ||||
|         this.isDebuging = false; | ||||
| @@ -26,6 +28,14 @@ public class APPModel extends BaseBean { | ||||
|         this.isDebuging = isDebuging; | ||||
|     } | ||||
|  | ||||
|     public void setRsaToken(String rsaToken) { | ||||
|         this.rsaToken = rsaToken; | ||||
|     } | ||||
|  | ||||
|     public String getRsaToken() { | ||||
|         return rsaToken; | ||||
|     } | ||||
|  | ||||
|     public void setIsDebuging(boolean isDebuging) { | ||||
|         this.isDebuging = isDebuging; | ||||
|     } | ||||
| @@ -43,6 +53,7 @@ public class APPModel extends BaseBean { | ||||
|     public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException { | ||||
|         super.writeThisToJsonWriter(jsonWriter); | ||||
|         jsonWriter.name("isDebuging").value(isDebuging()); | ||||
|         jsonWriter.name("rsaToken").value(getRsaToken()); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -50,6 +61,8 @@ public class APPModel extends BaseBean { | ||||
|         if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else { | ||||
|             if (name.equals("isDebuging")) { | ||||
|                 setIsDebuging(jsonReader.nextBoolean()); | ||||
|             } else if (name.equals("rsaToken")) { | ||||
|                 setRsaToken(jsonReader.nextString()); | ||||
|             } else { | ||||
|                 return false; | ||||
|             } | ||||
|   | ||||
| @@ -0,0 +1,124 @@ | ||||
| package cc.winboll.studio.libappbase.utils; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen<zhangsken@188.com> | ||||
|  * @Date 2025/06/04 13:36 | ||||
|  * @Describe RSA加密工具 | ||||
|  */ | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
| import java.io.File; | ||||
| import java.io.FileOutputStream; | ||||
| import java.io.IOException; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.Paths; | ||||
| import java.security.KeyFactory; | ||||
| import java.security.KeyPair; | ||||
| import java.security.KeyPairGenerator; | ||||
| import java.security.PrivateKey; | ||||
| import java.security.PublicKey; | ||||
| import java.security.spec.PKCS8EncodedKeySpec; | ||||
| import java.security.spec.X509EncodedKeySpec; | ||||
| import javax.crypto.Cipher; | ||||
|  | ||||
| public class RSAUtils { | ||||
|     public static final String TAG = "RSAUtils"; | ||||
|     private static final RSAUtils INSTANCE = new RSAUtils(); | ||||
|     private static final int KEY_SIZE = 2048; | ||||
|     private static final String KEY_ALGORITHM = "RSA"; | ||||
|     private static final String PUBLIC_KEY_FILE = "public.key"; | ||||
|     private static final String PRIVATE_KEY_FILE = "private.key"; | ||||
|  | ||||
|     private RSAUtils() {} | ||||
|  | ||||
|     public static RSAUtils getInstance() { | ||||
|         return INSTANCE; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 检查密钥文件是否存在 | ||||
|      */ | ||||
|     public boolean keysExist(String path) { | ||||
|         File publicKeyFile = new File(path + PUBLIC_KEY_FILE); | ||||
|         File privateKeyFile = new File(path + PRIVATE_KEY_FILE); | ||||
|         return publicKeyFile.exists() && privateKeyFile.exists(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 生成密钥对并保存为字节文件 | ||||
|      */ | ||||
|     public void generateAndSaveKeys(String path) throws Exception { | ||||
|         LogUtils.d(TAG, "正在生成密钥对"); | ||||
|         // 生成密钥对 | ||||
|         KeyPairGenerator generator = KeyPairGenerator.getInstance(KEY_ALGORITHM); | ||||
|         generator.initialize(KEY_SIZE); | ||||
|         KeyPair keyPair = generator.generateKeyPair(); | ||||
|  | ||||
|         // 保存公钥(X.509字节) | ||||
|         saveKey(path, PUBLIC_KEY_FILE, keyPair.getPublic().getEncoded()); | ||||
|         // 保存私钥(PKCS#8字节) | ||||
|         saveKey(path, PRIVATE_KEY_FILE, keyPair.getPrivate().getEncoded()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 读取密钥对(存在则读取,不存在则生成) | ||||
|      */ | ||||
|     public KeyPair getOrGenerateKeys(String path) throws Exception { | ||||
|         if (keysExist(path)) { | ||||
|             LogUtils.d(TAG, "密钥对已存在"); | ||||
|             return readKeysFromFile(path); | ||||
|         } else { | ||||
|             LogUtils.d(TAG, "未生成密钥对"); | ||||
|             generateAndSaveKeys(path); | ||||
|             return readKeysFromFile(path); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 从文件读取密钥对 | ||||
|      */ | ||||
|     private KeyPair readKeysFromFile(String path) throws Exception { | ||||
|         LogUtils.d(TAG, "正在读取密钥对"); | ||||
|         byte[] publicKeyBytes = Files.readAllBytes(Paths.get(path + PUBLIC_KEY_FILE)); | ||||
|         byte[] privateKeyBytes = Files.readAllBytes(Paths.get(path + PRIVATE_KEY_FILE)); | ||||
|  | ||||
|         X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(publicKeyBytes); | ||||
|         PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec(privateKeyBytes); | ||||
|  | ||||
|         KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM); | ||||
|         PublicKey publicKey = factory.generatePublic(publicSpec); | ||||
|         PrivateKey privateKey = factory.generatePrivate(privateSpec); | ||||
|  | ||||
|         return new KeyPair(publicKey, privateKey); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 通用保存密钥字节方法 | ||||
|      */ | ||||
|     private void saveKey(String path, String fileName, byte[] keyBytes) throws IOException { | ||||
|         File dir = new File(path); | ||||
|         if (!dir.exists()) dir.mkdirs(); // 自动创建目录 | ||||
|         try (FileOutputStream fos = new FileOutputStream(path + fileName)) { | ||||
|             fos.write(keyBytes); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * 公钥加密 | ||||
|      */ | ||||
|     public byte[] encryptWithPublicKey(String plainText, PublicKey publicKey) throws Exception { | ||||
|         Cipher cipher = Cipher.getInstance(KEY_ALGORITHM + "/ECB/PKCS1Padding"); | ||||
|         cipher.init(Cipher.ENCRYPT_MODE, publicKey); | ||||
|         return cipher.doFinal(plainText.getBytes("UTF-8")); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 私钥解密 | ||||
|      */ | ||||
|     public String decryptWithPrivateKey(byte[] encryptedData, PrivateKey privateKey) throws Exception { | ||||
|         Cipher cipher = Cipher.getInstance(KEY_ALGORITHM + "/ECB/PKCS1Padding"); | ||||
|         cipher.init(Cipher.DECRYPT_MODE, privateKey); | ||||
|         byte[] decryptedBytes = cipher.doFinal(encryptedData); | ||||
|         return new String(decryptedBytes, "UTF-8"); | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										37
									
								
								libappbase/src/main/res/layout/activity_logon.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								libappbase/src/main/res/layout/activity_logon.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <LinearLayout | ||||
| 	xmlns:android="http://schemas.android.com/apk/res/android" | ||||
| 	xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
| 	android:orientation="vertical" | ||||
| 	android:layout_width="match_parent" | ||||
| 	android:layout_height="match_parent"> | ||||
|  | ||||
| 	<LinearLayout | ||||
| 		android:orientation="horizontal" | ||||
| 		android:layout_width="match_parent" | ||||
| 		android:layout_height="wrap_content" | ||||
| 		android:gravity="right"> | ||||
|  | ||||
| 		<Button | ||||
| 			android:layout_width="wrap_content" | ||||
| 			android:layout_height="wrap_content" | ||||
| 			android:text="Test RSA" | ||||
|             android:onClick="onTestRSA"/> | ||||
|  | ||||
| 	</LinearLayout> | ||||
|  | ||||
| 	<LinearLayout | ||||
| 		android:orientation="vertical" | ||||
| 		android:layout_width="match_parent" | ||||
| 		android:layout_height="0dp" | ||||
| 		android:layout_weight="1.0"> | ||||
|  | ||||
| 		<cc.winboll.studio.libappbase.LogView | ||||
| 			android:layout_width="match_parent" | ||||
| 			android:layout_height="match_parent" | ||||
| 			android:id="@+id/logview"/> | ||||
|  | ||||
| 	</LinearLayout> | ||||
|  | ||||
| </LinearLayout> | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 ZhanGSKen
					ZhanGSKen