diff --git a/demo-athenaopt_backend/develop/conf/application.properties b/demo-athenaopt_backend/develop/conf/application.properties index 9756e34..edf9499 100644 --- a/demo-athenaopt_backend/develop/conf/application.properties +++ b/demo-athenaopt_backend/develop/conf/application.properties @@ -9,6 +9,7 @@ configGroup=@configGroup@ #SMALLVERSION=@SMALLVERSION@ #springConfigFileNames=xxx.xml,xxx.xml iamApToken=@iamApToken@ +iamApTokenForLogin=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MzczMjY2ODk0NjEsInNpZCI6NDA3MTI4ODI1NTM0NDY0MSwiaWQiOiJEaWdpd2luQ2xvdWQifQ.XGPl3brNeNTCivWN_bIYj8TfcxqlkQ0sFV2woPOr0TY dmcUserName=@dmcUserName@ dmcPwd=@dmcPwd@ dmcBucketName=@dmcBucketName@ diff --git a/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/infra/constant/GlobalConstants.java b/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/infra/constant/GlobalConstants.java new file mode 100644 index 0000000..9e54c18 --- /dev/null +++ b/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/infra/constant/GlobalConstants.java @@ -0,0 +1,103 @@ +package com.digiwin.athena.app.infra.constant; + +/** + * 全局常量 + * + * @author fobgochod + * @date 2020/4/27 + */ +public class GlobalConstants { + + /** + * 访问凭证 + */ + public static final String DIGI_MIDDLEWARE_AUTH_ACCESS = "digi-middleware-auth-access"; + /** + * 在HttpHeader中传递的User信息 + */ + public static final String AUTH_USER = "digi-middleware-auth-user-data"; + /** + * 在HttpHeader中传递的App信息 + */ + public static final String APP = "digi-middleware-auth-app-data"; + /** + * 在HttpHeader中传递的OTA Token信息 + */ + public static final String HTTP_HEADER_OTA_TOKEN_KEY = "digi-middleware-auth-ota"; + /** + * 在HttpHeader中传递的Access Token信息 + */ + public static final String HTTP_HEADER_ACCESS_TOKEN_KEY = "digi-middleware-auth-access"; + /** + * 在HttpHeader中传递的User Token信息 + */ + public static final String HTTP_HEADER_USER_TOKEN_KEY = "digi-middleware-auth-user"; + /** + * 在HttpHeader中传递的APP Token信息 + */ + public static final String HTTP_HEADER_APP_TOKEN_KEY = "digi-middleware-auth-app"; + /** + * 在HttpHeader中传递的Driver Token信息 + */ + public static final String HTTP_HEADER_DRIVE_TOKEN_KEY = "digi-middleware-drive-access"; + /** + * 在HttpHeader中传递 是都需要加密 + */ + public static final String HTTP_HEADER_DATA_MASK = "digi-middleware-data-mask"; + /** + * 在HttpHeader中传递 租户ID + */ + public static final String HTTP_HEADER_TENANT_ID = "tenantId"; + /** + * 在HttpHeader中传递 设备信息 + */ + public static final String HTTP_HEADER_CLIENT_AGENT = "Client-Agent"; + + /** + * 查询时,若為歸戶資料,請填入固定值: iam-mapping + */ + public static final String QUERY_PARAMETER_MAPPING = "iam-mapping"; + public static final String ZONE_OFF_SET = "+8"; + public static final String DEV_ACTIVE = "dev"; + + public static final String EMPTY_STR = "[empty]"; + + public static final String UPDATE_ALL = "all"; + public static final String UPDATE_ONLY_APPEND = "onlyAppend"; + + public static final String ADMINISTRATORS = "administrators"; + public static final String ADMINISTRATOR = "administrator"; + public static final String INTEGRATION = "integration"; + public static final String SUPERADMIN = "superadmin"; + public static final String GUEST = "guest"; + + public static final String ERROR_MESSAGE = "Server internal error"; + public static final String ENV_DEV = "dev"; + public static final String ENV_PROD = "prod"; + public static final String ENV_TEST = "test"; + public static final char AT = '@'; + public static final char DOT = '.'; + public static final char TAB = '\t'; + public static final char DASH = '-'; + public static final char COLON = ':'; + public static final char COMMA = ','; + public static final char DOLLAR = '$'; + public static final char PERCENT = '%'; + public static final char ESCAPE = '\\'; + public static final char ASTERISK = '*'; + public static final char SEMICOLON = ';'; + public static final char CURLY_LEFT = '{'; + public static final char CURLY_RIGHT = '}'; + public static final char DOUBLE_QUOTE = '"'; + public static final char SINGLE_QUOTE = '\''; + public static final char LEFT_PARENTHESIS = '('; + public static final char RIGHT_PARENTHESIS = ')'; + public static final String EMPTY = ""; + public static final String TRUE = "true"; + public static final String FALSE = "false"; + public static final String DEFAULT_VALUE_SEPARATOR = ":-"; + public static final String DEFAULT_CONTEXT_NAME = "default"; + public static final String LINE_SEPARATOR = System.getProperty("line.separator"); + public static final String ACCEPT_LANGUAGE = "Accept-Language"; + public static final String LOCALE = "Locale"; +} diff --git a/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/infra/entity/DigiwinSummit.java b/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/infra/entity/DigiwinSummit.java new file mode 100644 index 0000000..2d1859f --- /dev/null +++ b/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/infra/entity/DigiwinSummit.java @@ -0,0 +1,39 @@ +/* + * Author: DONGSK + * Datetime: 2024/4/25 16:43 + * Description: + * History: + * 作者姓名 --修改时间 --版本号--描述 + */ +package com.digiwin.athena.app.infra.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; + +import java.io.Serializable; + +/** + * DigiwinSummit + * + * @author DONGSK 2024/4/25 16:43 + * @since 1.0.0 + */ +@Data +@TableName(value = "digiwin_summit", autoResultMap = true) +public class DigiwinSummit extends Model implements Serializable { + Long id; + @TableField(value = "user_id") + String userId; + @TableField(value = "tenant_id") + String tenantId; + @TableField(value = "ref_prod") + String refProd; + @TableField(value = "ref_user_id") + String refUserId; + @TableField(value = "ref_tenant_sid") + String refTenantSid; + @TableField(value = "ref_user_password") + String refUserPassword; +} diff --git a/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/infra/repository/DigiwinSummitRepository.java b/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/infra/repository/DigiwinSummitRepository.java new file mode 100644 index 0000000..fa96455 --- /dev/null +++ b/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/infra/repository/DigiwinSummitRepository.java @@ -0,0 +1,20 @@ +/* + * Author: DONGSK + * Datetime: 2024/4/25 16:49 + * Description: + * History: + * 作者姓名 --修改时间 --版本号--描述 + */ +package com.digiwin.athena.app.infra.repository; + +import com.digiwin.athena.app.infra.entity.DigiwinSummit; +import com.digiwin.athena.opt.persistence.repository.BaseRepository; + +/** + * DigiwinSummitRepository + * + * @author DONGSK 2024/4/25 16:49 + * @since 1.0.0 + */ +public interface DigiwinSummitRepository extends BaseRepository { +} diff --git a/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/infra/service/DigiwinSummitService.java b/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/infra/service/DigiwinSummitService.java new file mode 100644 index 0000000..ee28292 --- /dev/null +++ b/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/infra/service/DigiwinSummitService.java @@ -0,0 +1,7 @@ +package com.digiwin.athena.app.infra.service; + +import com.digiwin.athena.app.infra.entity.DigiwinSummit; +import com.digiwin.athena.opt.persistence.service.IBaseService; + +public interface DigiwinSummitService extends IBaseService { +} diff --git a/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/infra/service/Impl/DigiwinSummitServiceImpl.java b/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/infra/service/Impl/DigiwinSummitServiceImpl.java new file mode 100644 index 0000000..60fc358 --- /dev/null +++ b/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/infra/service/Impl/DigiwinSummitServiceImpl.java @@ -0,0 +1,16 @@ +package com.digiwin.athena.app.infra.service.Impl; + +import com.digiwin.athena.app.infra.entity.DigiwinSummit; +import com.digiwin.athena.app.infra.repository.DigiwinSummitRepository; +import com.digiwin.athena.app.infra.service.DigiwinSummitService; +import com.digiwin.athena.opt.persistence.service.impl.AbsBaseService; +import org.springframework.stereotype.Service; + +/** + * @author zhenggl + * create: 2023-04-28 + * Description: + */ +@Service +public class DigiwinSummitServiceImpl extends AbsBaseService implements DigiwinSummitService { +} diff --git a/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/provider/BasicApiService.java b/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/provider/BasicApiService.java index e57a962..97e773b 100644 --- a/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/provider/BasicApiService.java +++ b/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/provider/BasicApiService.java @@ -32,4 +32,15 @@ public interface BasicApiService extends DWService { @DWRequestMapping(path = "/EAIService/{serviceName}", method = DWRequestMethod.POST) Object mockEAIExecutor(@DWPathVariable String serviceName, Map requestParameter) throws Exception; + + @AllowAnonymous + @DWRequestMapping(path = "/api/default/login", method = DWRequestMethod.POST) + Object login(String userId, String tenantId, String productKey) throws Exception; + + @AllowAnonymous + @DWRequestMapping(path = "/api/clear/login", method = DWRequestMethod.GET) + Object clearLogin() throws Exception; + + + } \ No newline at end of file diff --git a/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/provider/impl/BasicApiServiceImpl.java b/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/provider/impl/BasicApiServiceImpl.java index 5d2e172..d7a1f88 100644 --- a/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/provider/impl/BasicApiServiceImpl.java +++ b/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/provider/impl/BasicApiServiceImpl.java @@ -11,6 +11,7 @@ package com.digiwin.athena.app.provider.impl; import com.alibaba.fastjson.JSON; import com.digiwin.app.service.DWServiceContext; import com.digiwin.athena.app.provider.BasicApiService; +import com.digiwin.athena.app.service.basic.LoginService; import com.digiwin.athena.opt.common.eai.service.EAIServiceContext; import javax.annotation.Resource; @@ -28,6 +29,9 @@ public class BasicApiServiceImpl implements BasicApiService { @Resource EAIServiceContext eaiServiceContext; + @Resource + LoginService loginService; + @Override public String helloWord(String name) throws Exception { return "hello ," + name; @@ -41,10 +45,22 @@ public class BasicApiServiceImpl implements BasicApiService { }); DWServiceContext.getContext().setStandardResult(false); /** 直接返回,不再包装,入参可以体现 serviceName - Map eaiResult = new HashMap<>(); - eaiResult.put("service", serviceName); - eaiResult.put("response", eaiServiceContext.execute(serviceName, headers, JSON.toJSONString(requestParameter))); - */ - return eaiServiceContext.execute(serviceName, headers, JSON.toJSONString(requestParameter)); + Map eaiResult = new HashMap<>(); + eaiResult.put("service", serviceName); + eaiResult.put("response", eaiServiceContext.execute(serviceName, headers, JSON.toJSONString(requestParameter))); + */ + return eaiServiceContext.execute(serviceName, headers, JSON.toJSONString(requestParameter)); + } + + @Override + public Object login(String userId, String tenantId, String productKey) throws Exception { + return loginService.login(userId, tenantId, productKey); } + + @Override + public Object clearLogin() throws Exception { + loginService.clearLoginMap(); + return "ok"; + } + } diff --git a/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/service/basic/AESUtils.java b/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/service/basic/AESUtils.java new file mode 100644 index 0000000..76e3716 --- /dev/null +++ b/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/service/basic/AESUtils.java @@ -0,0 +1,86 @@ +package com.digiwin.athena.app.service.basic; + +import org.apache.commons.codec.binary.Hex; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +public class AESUtils { + + + private static final Logger logger = LoggerFactory.getLogger(AESUtils.class); + private static final String IV_STRING = "ghUb#er57HBh(u%g"; + + /** + * 加密 + * 加密失败返回原文 2021-7-21 + * + * @param src 加密字段 + * @param aesKey aesKey 长度16 + * @return 密文 string + */ + public static String aesEncryptByBase64(String src, String aesKey) { + try { + SecretKeySpec key = new SecretKeySpec(aesKey.getBytes(StandardCharsets.UTF_8), "AES"); + + byte[] initParam = IV_STRING.getBytes(StandardCharsets.UTF_8); + IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, key, ivParameterSpec); + + byte[] cleartext = src.getBytes(StandardCharsets.UTF_8); + byte[] ciphertextBytes = cipher.doFinal(cleartext); + Base64.Encoder encoder = Base64.getEncoder(); + return encoder.encodeToString(ciphertextBytes); + } catch (Exception ex) { + logger.error("AES加密失败[{}]", src); + return src; + } + + } + + public static String aesEncrypt(String src, String aesKey) { + try { + // 生成和mysql一致的加密数据 + SecretKeySpec key = generateMySQLAESKey(aesKey); + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, key); + + byte[] cleartext = src.getBytes(StandardCharsets.UTF_8); + byte[] ciphertextBytes = cipher.doFinal(cleartext); + return new String(Hex.encodeHexString(ciphertextBytes)).toUpperCase(); + } catch (Exception ex) { + logger.error("AES加密失败[{}]", src); + return src; + } + } + + public static String aesDecrypt(String content, String aesKey) { + try { + SecretKey key = generateMySQLAESKey(aesKey); + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.DECRYPT_MODE, key); + byte[] cleartext = Hex.decodeHex(content.toCharArray()); + byte[] ciphertextBytes = cipher.doFinal(cleartext); + return new String(ciphertextBytes, StandardCharsets.UTF_8); + } catch (Exception ex) { + logger.error("AES解密失败[{}]", content); + return content; + } + } + + public static SecretKeySpec generateMySQLAESKey(final String key) { + final byte[] finalKey = new byte[16]; + int i = 0; + for (byte b : key.getBytes(StandardCharsets.UTF_8)) { + finalKey[i++ % 16] ^= b; + } + return new SecretKeySpec(finalKey, "AES"); + } +} diff --git a/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/service/basic/LoginService.java b/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/service/basic/LoginService.java new file mode 100644 index 0000000..0728040 --- /dev/null +++ b/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/service/basic/LoginService.java @@ -0,0 +1,176 @@ +/* + * Author: DONGSK + * Datetime: 2024/4/25 14:08 + * Description: 登录服务 + * History: + * 作者姓名 --修改时间 --版本号--描述 + */ +package com.digiwin.athena.app.service.basic; + +import com.digiwin.athena.app.infra.constant.GlobalConstants; +import com.digiwin.athena.app.infra.entity.DigiwinSummit; +import com.digiwin.athena.app.infra.service.DigiwinSummitService; +import org.apache.commons.codec.binary.Base64; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import javax.annotation.Resource; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.Map; + +/** + * LoginService + * 登录服务 + * + * @author DONGSK 2024/4/25 14:08 + * @since 1.0.0 + */ +@Service +public class LoginService { + + private static final Logger logger = LoggerFactory.getLogger(LoginService.class); + + public static final Map loginDataMap = new HashMap<>(); + + public static final String mapKey = "%s_%s_%s"; + + @Value("${iamApTokenForLogin}") + private String iamApToken; + + @Value("${iamUrl}") + private String iamURL; + + @Resource + RestTemplate restTemplate; + + @Resource + DigiwinSummitService digiwinSummitService; + + public Object login(String userId, String tenantId, String productKey) { + String uri = iamURL + "/api/iam/v2/identity/login"; + try { + initLoginMap(); + + String mapKey = String.format(LoginService.mapKey, userId, tenantId, productKey); + DigiwinSummit digiwinSummit = loginDataMap.get(mapKey); + + if (digiwinSummit == null) { + return null; + } + // password + String password = digiwinSummit.getRefUserPassword(); + //1.客户端生成公私钥 + HashMap keyMap = getKeyPairMap(); + String clientPublicKey = keyMap.get("publicKey"); + String privateKey = keyMap.get("privateKey"); + //2.获取服务端公钥 + String serverPublicKey = getServerPublicKey(); + //3.根据服务端公钥加密客户端公钥 + String encryptPublicKey = RSAUtils.encryptByPublicKey(clientPublicKey, serverPublicKey); + //4.获取加密后的AES的key值 + String encryptAesKey = getAesPublicKey(encryptPublicKey); + //5.根据客户端私有解密加密的aes的key值 + String aesKey = new String(RSAUtils.decryptByPrivateKey(Base64.decodeBase64(encryptAesKey), privateKey)); + String passwordHash = AESUtils.aesEncryptByBase64(password, aesKey); + //6.登录 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.add(GlobalConstants.HTTP_HEADER_APP_TOKEN_KEY, iamApToken); + Map requestEntity = new HashMap<>(3); + requestEntity.put("identityType", "token"); + requestEntity.put("userId", digiwinSummit.getRefUserId()); + requestEntity.put("passwordHash", passwordHash); + requestEntity.put("clientEncryptPublicKey", encryptPublicKey); + HttpEntity> httpEntity = new HttpEntity<>(requestEntity, headers); + ResponseEntity response = restTemplate.exchange(uri, HttpMethod.POST, httpEntity, Map.class); + // 切租户 + return changeTenant(Long.valueOf(digiwinSummit.getRefTenantSid()), response.getBody().get("token").toString()); + + } catch (Exception ex) { + logger.error("登录失败:{}", ex.getMessage(), ex); + } + return null; + } + + private Object changeTenant(Long sid, String userToken) { + String uri = iamURL + "/api/iam/v2/identity/token/refresh/tenant"; + try { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.add(GlobalConstants.HTTP_HEADER_APP_TOKEN_KEY, iamApToken); + headers.add(GlobalConstants.HTTP_HEADER_USER_TOKEN_KEY, userToken); + Map requestEntity = new HashMap<>(); + requestEntity.put("tenantSid", sid); + HttpEntity> httpEntity = new HttpEntity<>(requestEntity, headers); + ResponseEntity exchange = restTemplate.exchange(uri, HttpMethod.POST, httpEntity, Map.class); + return exchange.getBody(); + } catch (Exception e) { + logger.error("切换失败:{}", e.getMessage(), e); + } + return ""; + } + + private HashMap getKeyPairMap() throws NoSuchAlgorithmException { + KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); + generator.initialize(1024); + KeyPair keyPair = generator.generateKeyPair(); + String privateKey = new String(Base64.encodeBase64(keyPair.getPrivate().getEncoded())); + String publicKey = new String(Base64.encodeBase64(keyPair.getPublic().getEncoded())); + HashMap keyMap = new HashMap<>(); + keyMap.put("privateKey", privateKey); + keyMap.put("publicKey", publicKey); + return keyMap; + } + + private String getServerPublicKey() { + String uri = iamURL + "/api/iam/v2/identity/publickey"; + try { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.add(GlobalConstants.HTTP_HEADER_APP_TOKEN_KEY, iamApToken); + HttpEntity> httpEntity = new HttpEntity<>(headers); + ResponseEntity response = restTemplate.exchange(uri, HttpMethod.GET, httpEntity, Map.class); + return String.valueOf(response.getBody().get("publicKey")); + } catch (Exception e) { + logger.error("登录失败:{}", e.getMessage(), e); + } + return ""; + } + + private String getAesPublicKey(String encryptPublicKey) { + String uri = iamURL + "/api/iam/v2/identity/aeskey"; + try { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.add(GlobalConstants.HTTP_HEADER_APP_TOKEN_KEY, iamApToken); + Map requestEntity = new HashMap<>(1); + requestEntity.put("clientEncryptPublicKey", encryptPublicKey); + HttpEntity> httpEntity = new HttpEntity<>(requestEntity, headers); + ResponseEntity response = restTemplate.exchange(uri, HttpMethod.POST, httpEntity, Map.class); + return String.valueOf(response.getBody().get("encryptAesKey")); + } catch (Exception e) { + logger.error("登录失败:{}", e.getMessage(), e); + } + return ""; + } + + private void initLoginMap() { + if (loginDataMap.isEmpty()) { + digiwinSummitService.list().forEach(item -> { + loginDataMap.put(String.format(mapKey, item.getUserId(), item.getTenantId(), item.getRefProd()), item); + }); + } + } + + public void clearLoginMap() { + loginDataMap.clear(); + } + +} diff --git a/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/service/basic/RSAUtils.java b/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/service/basic/RSAUtils.java new file mode 100644 index 0000000..a183fa5 --- /dev/null +++ b/demo-athenaopt_backend/develop/src/main/java/com/digiwin/athena/app/service/basic/RSAUtils.java @@ -0,0 +1,118 @@ +package com.digiwin.athena.app.service.basic; + + +import org.apache.commons.codec.binary.Base64; + +import javax.crypto.Cipher; +import java.io.ByteArrayOutputStream; +import java.security.Key; +import java.security.KeyFactory; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +/** + * @author zhuzcz + */ +public class RSAUtils { + /** + * 加密算法RSA + */ + public static final String KEY_ALGORITHM = "RSA"; + + /** + * RSA最大加密明文大小 + */ + private static final int MAX_ENCRYPT_BLOCK = 245; + + /** + * RSA最大解密密文大小 + */ + private static final int MAX_DECRYPT_BLOCK = 256; + + + /** */ + /** + *

+ * 私钥解密 + *

+ * + * @param encryptedData 已加密数据 + * @param privateKey 私钥(BASE64编码) + * @return + * @throws Exception + */ + public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception { + byte[] keyBytes = Base64.decodeBase64(privateKey); + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key privateK = keyFactory.generatePrivate(pkcs8KeySpec); + Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + 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 decryptedData; + } + + + /** */ + /** + *

+ * 公钥加密 + *

+ * + * @param data 源数据 + * @param publicKey 公钥(BASE64编码) + * @return + * @throws Exception + */ + public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception { + byte[] keyBytes = Base64.decodeBase64(publicKey); + X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key publicK = keyFactory.generatePublic(x509KeySpec); + // 对数据加密 + Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + cipher.init(Cipher.ENCRYPT_MODE, publicK); + int inputLen = data.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(data, offSet, MAX_ENCRYPT_BLOCK); + } else { + cache = cipher.doFinal(data, offSet, inputLen - offSet); + } + out.write(cache, 0, cache.length); + i++; + offSet = i * MAX_ENCRYPT_BLOCK; + } + byte[] encryptedData = out.toByteArray(); + out.close(); + return encryptedData; + } + + public static String encryptByPublicKey(String data, String clientPublicKey) throws Exception { + data = Base64.encodeBase64String(encryptByPublicKey(data.getBytes(), clientPublicKey)); + return data; + } + +}