From b2237d32705b06e5326f770ab63448fe54a0b8c1 Mon Sep 17 00:00:00 2001 From: Kane Wang Date: Sat, 15 Mar 2025 12:50:53 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=9D=E5=AD=98=E8=BF=9B=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- code/cpicxim-huixiabao/.mvn/jvm.config | 0 code/cpicxim-huixiabao/.mvn/maven.config | 0 code/cpicxim-huixiabao/.vscode/settings.json | 4 + code/cpicxim-huixiabao/pom.xml | 130 ++++++++++++++ .../web/filters/cros/CrosFilter.java | 59 ++++++ .../com/cpic/xim/utils/secrecy/AESUtils.java | 116 ++++++++++++ .../cpic/xim/utils/secrecy/Base64Utils.java | 164 +++++++++++++++++ .../com/cpic/xim/utils/secrecy/RSAUtils.java | 169 ++++++++++++++++++ .../main/webapp/WEB-INF/classes/spring.xml | 37 ++++ .../src/main/webapp/WEB-INF/web.xml | 25 +++ .../src/main/webapp/index.jsp | 5 + 11 files changed, 709 insertions(+) create mode 100644 code/cpicxim-huixiabao/.mvn/jvm.config create mode 100644 code/cpicxim-huixiabao/.mvn/maven.config create mode 100644 code/cpicxim-huixiabao/.vscode/settings.json create mode 100644 code/cpicxim-huixiabao/pom.xml create mode 100644 code/cpicxim-huixiabao/src/main/java/com/cpic/xim/huixiabao/web/filters/cros/CrosFilter.java create mode 100644 code/cpicxim-huixiabao/src/main/java/com/cpic/xim/utils/secrecy/AESUtils.java create mode 100644 code/cpicxim-huixiabao/src/main/java/com/cpic/xim/utils/secrecy/Base64Utils.java create mode 100644 code/cpicxim-huixiabao/src/main/java/com/cpic/xim/utils/secrecy/RSAUtils.java create mode 100644 code/cpicxim-huixiabao/src/main/webapp/WEB-INF/classes/spring.xml create mode 100644 code/cpicxim-huixiabao/src/main/webapp/WEB-INF/web.xml create mode 100644 code/cpicxim-huixiabao/src/main/webapp/index.jsp diff --git a/code/cpicxim-huixiabao/.mvn/jvm.config b/code/cpicxim-huixiabao/.mvn/jvm.config new file mode 100644 index 0000000..e69de29 diff --git a/code/cpicxim-huixiabao/.mvn/maven.config b/code/cpicxim-huixiabao/.mvn/maven.config new file mode 100644 index 0000000..e69de29 diff --git a/code/cpicxim-huixiabao/.vscode/settings.json b/code/cpicxim-huixiabao/.vscode/settings.json new file mode 100644 index 0000000..0be1c0c --- /dev/null +++ b/code/cpicxim-huixiabao/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic", + "java.compile.nullAnalysis.mode": "automatic" +} \ No newline at end of file diff --git a/code/cpicxim-huixiabao/pom.xml b/code/cpicxim-huixiabao/pom.xml new file mode 100644 index 0000000..4aa4c08 --- /dev/null +++ b/code/cpicxim-huixiabao/pom.xml @@ -0,0 +1,130 @@ + + + + 4.0.0 + + com.cpic.xim + cpicxim-huixiabao + 1.0-SNAPSHOT + war + + cpicxim-huixiabao Maven Webapp + + http://www.example.com + + + UTF-8 + 18 + 18 + 6.2.4 + 2.24.3 + + + + + junit + junit + 4.13.1 + test + + + + org.springframework + spring-context + ${spring.version} + + + org.springframework + spring-test + ${spring.version} + test + + + org.springframework + spring-web + ${spring.version} + + + + org.springframework + spring-webmvc + ${spring.version} + + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + + + org.apache.logging.log4j + log4j-slf4j2-impl + ${log4j.version} + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + + + + org.apache.directory.studio + org.apache.commons.codec + 1.8 + + + + + cpicxim-huixiabao + + + src/main/resources + + **/*.properties + **/*.xml + + false + + + + + + org.apache.maven.plugins + maven-war-plugin + 3.4.0 + + ${project.build.directory}/../../../../输出/back/ + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.2 + + true + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.14.0 + + 18 + 18 + UTF-8 + + + + + + \ No newline at end of file diff --git a/code/cpicxim-huixiabao/src/main/java/com/cpic/xim/huixiabao/web/filters/cros/CrosFilter.java b/code/cpicxim-huixiabao/src/main/java/com/cpic/xim/huixiabao/web/filters/cros/CrosFilter.java new file mode 100644 index 0000000..5c4b8a4 --- /dev/null +++ b/code/cpicxim-huixiabao/src/main/java/com/cpic/xim/huixiabao/web/filters/cros/CrosFilter.java @@ -0,0 +1,59 @@ +/* + * @Author: Kane + * @Date: 2025-03-15 11:56:54 + * @LastEditors: Kane + * @FilePath: /cpicxim-huixiabao/src/main/java/com/cpic/xim/huixiabao/web/filters/cros/CrosFilter.java + * @Description: + * + * Copyright (c) ${2023} by Kane, All Rights Reserved. + */ + +package com.cpic.xim.huixiabao.web.filters.cros; + +import java.io.IOException; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpMethod; + +public class CrosFilter implements Filter +{ + private static Logger logger = LoggerFactory.getLogger( CrosFilter.class ); + + @Override + public void doFilter( ServletRequest req, ServletResponse resp, FilterChain chain ) + throws ServletException, + IOException + { + HttpServletRequest request = ( HttpServletRequest ) req; + HttpServletResponse response = ( HttpServletResponse ) resp; + String method = request.getMethod(); + String originHeader = request.getHeader( "Origin" ); + + logger.info( "收到" + method + "请求,来自" + originHeader ); + + // 如果是Options请求 + if ( method.equals( HttpMethod.OPTIONS.toString() ) ) + { + originHeader = "*"; + } + + response.setHeader( "Access-Control-Allow-Origin", originHeader ); + response.setHeader( "Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT" ); + response.setHeader( "Access-Control-Max-Age", "0" ); + response.setHeader( "Access-Control-Allow-Headers", + "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token" ); + response.setHeader( "Access-Control-Allow-Credentials", "true" ); + response.setHeader( "XDomainRequestAllowed", "1" ); + response.setHeader( "XDomainRequestAllowed", "1" ); + + chain.doFilter( request, response ); + } + +} diff --git a/code/cpicxim-huixiabao/src/main/java/com/cpic/xim/utils/secrecy/AESUtils.java b/code/cpicxim-huixiabao/src/main/java/com/cpic/xim/utils/secrecy/AESUtils.java new file mode 100644 index 0000000..0260737 --- /dev/null +++ b/code/cpicxim-huixiabao/src/main/java/com/cpic/xim/utils/secrecy/AESUtils.java @@ -0,0 +1,116 @@ +/* + * @Author: Kane + * + * @Date: 2025-03-15 12:04:19 + * + * @LastEditors: Kane + * + * @FilePath: /cpicxim-huixiabao/src/main/java/com/cpic/xim/utils/secrecy/AESUtils.java + * + * @Description: + * + * Copyright (c) ${2023} by Kane, All Rights Reserved. + */ + +package com.cpic.xim.utils.secrecy; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * AESUtils + */ +public class AESUtils +{ + + /** + * 偏移量,必须是16位字符串 + */ + private static final String IV_STRING = "16-Bytes--String"; + + /** + * 产生随机密钥(这里产生密钥必须是16位) + */ + private static String generateKey() + { + String key = UUID.randomUUID().toString(); + key = key.replace( "-", "" ).substring( 0, 16 );// 替换掉-号 + + return key; + } + + /** + * 加密 + * + * @param publicKey 公钥 + * @param content 加密数据 + * @return + */ + public static Map encryptData( String publicKey, String content ) + { + byte[] encryptedBytes; + try + { + byte[] byteContent = content.getBytes( "UTF-8" ); + // 注意,为了能与 iOS 统一 + // 这里的 key 不可以使用 KeyGenerator、SecureRandom、SecretKey 生成 + String key = generateKey(); + byte[] enCodeFormat = key.getBytes(); + SecretKeySpec secretKeySpec = new SecretKeySpec( enCodeFormat, "AES" ); + byte[] initParam = IV_STRING.getBytes(); + IvParameterSpec ivParameterSpec = new IvParameterSpec( initParam ); + // 指定加密的算法、工作模式和填充方式 + Cipher cipher = Cipher.getInstance( "AES/CBC/PKCS5Padding" ); + cipher.init( Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec ); + encryptedBytes = cipher.doFinal( byteContent ); + // 同样对加密后数据进行 base64 编码 + key = RSAUtils.encryptByPublicKey( key, publicKey ); + Map result = new HashMap<>( 2 ); + result.put( "key", key ); + result.put( "content", Base64Utils.encode( encryptedBytes ) ); + return result; + } + catch ( Exception e ) + { + throw new RuntimeException( "加密失败:" + e.getMessage(), e ); + } + } + + /** + * 解密 + * + * @param key + * @param privateKey + * @param content + * @return + */ + public static String decryptData( String key, String privateKey, String content ) + { + try + { + System.out.println( "KEY:" + key ); + System.out.println( "privateKey:" + privateKey ); + System.out.println( "content:" + content ); + // base64 解码 + byte[] encryptedBytes = Base64Utils.decode( content ); + key = RSAUtils.decryptByPrivateKey( privateKey, key ); + byte[] enCodeFormat = key.getBytes(); + SecretKeySpec secretKey = new SecretKeySpec( enCodeFormat, "AES" ); + byte[] initParam = IV_STRING.getBytes(); + IvParameterSpec ivParameterSpec = new IvParameterSpec( initParam ); + Cipher cipher = Cipher.getInstance( "AES/CBC/PKCS5Padding" ); + cipher.init( Cipher.DECRYPT_MODE, secretKey, ivParameterSpec ); + byte[] result = cipher.doFinal( encryptedBytes ); + return new String( result, "UTF-8" ); + } + catch ( Exception e ) + { + throw new RuntimeException( "解密失败:" + e.getMessage(), e ); + } + } + +} diff --git a/code/cpicxim-huixiabao/src/main/java/com/cpic/xim/utils/secrecy/Base64Utils.java b/code/cpicxim-huixiabao/src/main/java/com/cpic/xim/utils/secrecy/Base64Utils.java new file mode 100644 index 0000000..8ae7e8e --- /dev/null +++ b/code/cpicxim-huixiabao/src/main/java/com/cpic/xim/utils/secrecy/Base64Utils.java @@ -0,0 +1,164 @@ +/* + * @Author: Kane + * @Date: 2025-03-15 12:04:19 + * @LastEditors: Kane + * @FilePath: /cpicxim-huixiabao/src/main/java/com/cpic/xim/utils/secrecy/Base64Utils.java + * @Description: + * + * Copyright (c) ${2023} by Kane, All Rights Reserved. + */ +package com.cpic.xim.utils.secrecy; + +import org.apache.commons.codec.binary.Base64; +import java.io.*; + +/** + * Base64Utils + */ +public class Base64Utils +{ + + /** */ + /** + * 文件读取缓冲区大小 + */ + private static final int CACHE_SIZE = 1024; + + /** */ + /** + *

+ * BASE64字符串解码为二进制数据 + *

+ * + * @param base64 + * @return + * @throws Exception + */ + public static byte[] decode( String base64 ) + throws Exception + { + return Base64.decodeBase64( base64.getBytes() ); + } + + public static String decode( byte[] b ) + throws Exception + { + return new String( Base64.decodeBase64( b ) ); + } + + /** */ + /** + *

+ * 二进制数据编码为BASE64字符串 + *

+ * + * @param bytes + * @return + */ + public static String encode( byte[] bytes ) + { + return new String( Base64.encodeBase64( bytes ) ); + } + + /** */ + /** + *

+ * 将文件编码为BASE64字符串 + *

+ *

+ * 大文件慎用,可能会导致内存溢出 + *

+ * + * @param filePath 文件绝对路径 + * @return + * @throws Exception + */ + public static String encodeFile( String filePath ) + throws Exception + { + byte[] bytes = fileToByte( filePath ); + return encode( bytes ); + } + + /** */ + /** + *

+ * BASE64字符串转回文件 + *

+ * + * @param filePath 文件绝对路径 + * @param base64 编码字符串 + * @throws Exception + */ + public static void decodeToFile( String filePath, String base64 ) + throws Exception + { + byte[] bytes = decode( base64 ); + byteArrayToFile( bytes, filePath ); + } + + /** */ + /** + *

+ * 文件转换为二进制数组 + *

+ * + * @param filePath 文件路径 + * @return + * @throws Exception + */ + public static byte[] fileToByte( String filePath ) + throws Exception + { + byte[] data = new byte[0]; + File file = new File( filePath ); + if ( file.exists() ) + { + FileInputStream in = new FileInputStream( file ); + ByteArrayOutputStream out = new ByteArrayOutputStream( 2048 ); + byte[] cache = new byte[CACHE_SIZE]; + int nRead = 0; + while ( (nRead = in.read( cache )) != -1 ) + { + out.write( cache, 0, nRead ); + out.flush(); + } + out.close(); + in.close(); + data = out.toByteArray(); + } + return data; + } + + /** */ + /** + *

+ * 二进制数据写文件 + *

+ * + * @param bytes 二进制数据 + * @param filePath 文件生成目录 + */ + public static void byteArrayToFile( byte[] bytes, String filePath ) + throws Exception + { + InputStream in = new ByteArrayInputStream( bytes ); + File destFile = new File( filePath ); + if ( !destFile.getParentFile().exists() ) + { + destFile.getParentFile().mkdirs(); + } + destFile.createNewFile(); + OutputStream out = new FileOutputStream( destFile ); + byte[] cache = new byte[CACHE_SIZE]; + int nRead = 0; + while ( (nRead = in.read( cache )) != -1 ) + { + out.write( cache, 0, nRead ); + out.flush(); + } + out.close(); + in.close(); + } + +} diff --git a/code/cpicxim-huixiabao/src/main/java/com/cpic/xim/utils/secrecy/RSAUtils.java b/code/cpicxim-huixiabao/src/main/java/com/cpic/xim/utils/secrecy/RSAUtils.java new file mode 100644 index 0000000..8da1a96 --- /dev/null +++ b/code/cpicxim-huixiabao/src/main/java/com/cpic/xim/utils/secrecy/RSAUtils.java @@ -0,0 +1,169 @@ +package com.cpic.xim.utils.secrecy; + +import javax.crypto.Cipher; +import java.io.ByteArrayOutputStream; +import java.math.BigInteger; +import java.security.*; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.RSAPrivateKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.HashMap; +import java.util.Map; + +/** + * RSAUtils + */ +public class RSAUtils { + + /** + * RSA最大加密明文大小 + */ + private static final int MAX_ENCRYPT_BLOCK = 117; + + /** + * RSA最大解密密文大小 + */ + private static final int MAX_DECRYPT_BLOCK = 128; + + /** + * 加密算法RSA + */ + private static final String KEY_ALGORITHM = "RSA"; + + /** + * 生成公钥和私钥 + * + * @throws Exception + */ + public static Map getKeys() throws Exception { + KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); + keyPairGen.initialize(1024); + KeyPair keyPair = keyPairGen.generateKeyPair(); + RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); + RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); + + String publicKeyStr = getPublicKeyStr(publicKey); + String privateKeyStr = getPrivateKeyStr(privateKey); + + Map result = new HashMap<>(2); + result.put("publicKeyStr", publicKeyStr); + result.put("privateKeyStr", privateKeyStr); + + return result; + } + + public static void main(String[] args) throws Exception { + Map keys = getKeys(); + System.out.println("publicKeyStr=" + keys.get("publicKeyStr")); + System.out.println("privateKeyStr=" + keys.get("privateKeyStr")); + } + + /** + * 使用模和指数生成RSA私钥 + * 注意:【此代码用了默认补位方式,为RSA/None/PKCS1Padding,不同JDK默认的补位方式可能不同,如Android默认是RSA + * /None/NoPadding】 + * + * @param modulus 模 + * @param exponent 指数 + * @return + */ + public static RSAPrivateKey getPrivateKey(String modulus, String exponent) { + try { + BigInteger b1 = new BigInteger(modulus); + BigInteger b2 = new BigInteger(exponent); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(b1, b2); + return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 公钥加密 + * + * @param data 加密数据 + * @param publicKey 加密key + * @return + * @throws Exception + */ + public static String encryptByPublicKey(String data, String publicKey) throws Exception { + byte[] dataByte = data.getBytes(); + byte[] keyBytes = Base64Utils.decode(publicKey); + X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key publicK = keyFactory.generatePublic(x509KeySpec); + // 对数据加密 + Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + cipher.init(Cipher.ENCRYPT_MODE, publicK); + int inputLen = dataByte.length; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int offSet = 0; + byte[] cache; + int i = 0; + // 对数据分段加密 + while (inputLen - offSet > 0) { + if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { + cache = cipher.doFinal(dataByte, offSet, MAX_ENCRYPT_BLOCK); + } else { + cache = cipher.doFinal(dataByte, offSet, inputLen - offSet); + } + out.write(cache, 0, cache.length); + i++; + offSet = i * MAX_ENCRYPT_BLOCK; + } + byte[] encryptedData = out.toByteArray(); + out.close(); + return Base64Utils.encode(encryptedData); + } + + /** + * 私钥解密 + * + * @param privateKey 私钥 + * @param data 加密数据 + * @return + * @throws Exception + */ + public static String decryptByPrivateKey(String privateKey, String data) throws Exception { + byte[] encryptedData = Base64Utils.decode(data); + byte[] keyBytes = Base64Utils.decode(privateKey); + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key privateK = keyFactory.generatePrivate(pkcs8KeySpec); + // Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + + cipher.init(Cipher.DECRYPT_MODE, privateK); + int inputLen = encryptedData.length; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int offSet = 0; + byte[] cache; + int i = 0; + // 对数据分段解密 + while (inputLen - offSet > 0) { + if (inputLen - offSet > MAX_DECRYPT_BLOCK) { + cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK); + } else { + cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet); + } + out.write(cache, 0, cache.length); + i++; + offSet = i * MAX_DECRYPT_BLOCK; + } + byte[] decryptedData = out.toByteArray(); + out.close(); + return new String(decryptedData); + } + + public static String getPrivateKeyStr(PrivateKey privateKey) { + return Base64Utils.encode(privateKey.getEncoded()); + } + + public static String getPublicKeyStr(PublicKey publicKey) { + return Base64Utils.encode(publicKey.getEncoded()); + } +} \ No newline at end of file diff --git a/code/cpicxim-huixiabao/src/main/webapp/WEB-INF/classes/spring.xml b/code/cpicxim-huixiabao/src/main/webapp/WEB-INF/classes/spring.xml new file mode 100644 index 0000000..a349f68 --- /dev/null +++ b/code/cpicxim-huixiabao/src/main/webapp/WEB-INF/classes/spring.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/code/cpicxim-huixiabao/src/main/webapp/WEB-INF/web.xml b/code/cpicxim-huixiabao/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..c7b2872 --- /dev/null +++ b/code/cpicxim-huixiabao/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,25 @@ + + + Archetype Created Web Application + + + springmvc + org.springframework.web.servlet.DispatcherServlet + + contextConfigLocation + classpath:spring.xml + + 1 + + + springmvc + /huixibao + + + \ No newline at end of file diff --git a/code/cpicxim-huixiabao/src/main/webapp/index.jsp b/code/cpicxim-huixiabao/src/main/webapp/index.jsp new file mode 100644 index 0000000..ee89d62 --- /dev/null +++ b/code/cpicxim-huixiabao/src/main/webapp/index.jsp @@ -0,0 +1,5 @@ + + +

<%= "Hello World!" %>

+ +