17 changed files with 789 additions and 25 deletions
@ -0,0 +1,20 @@ |
|||
package com.ccsens.util.bean.wx.po; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnore; |
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author 逗 |
|||
*/ |
|||
@Data |
|||
public class WxApiTicket { |
|||
@JsonProperty("errcode") |
|||
private Integer errcode; |
|||
@JsonProperty("errmsg") |
|||
private String errmsg; |
|||
@JsonProperty("ticket") |
|||
private String ticket; |
|||
@JsonProperty("expires_in") |
|||
private Long expiresIn; |
|||
} |
@ -0,0 +1,16 @@ |
|||
package com.ccsens.util.bean.wx.po; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnore; |
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author __zHangSan |
|||
*/ |
|||
@Data |
|||
public class WxGetIp extends WxBaseEntity{ |
|||
@JsonProperty("ipList") |
|||
private List<String> ip_list; |
|||
} |
@ -0,0 +1,145 @@ |
|||
package com.ccsens.wechatutil.bean.dto; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
/** |
|||
* @author 逗 |
|||
*/ |
|||
@Data |
|||
public class NoticeResourceDecode { |
|||
|
|||
public enum TRADE_STATE { |
|||
/*** 支付成功 */ |
|||
SUCCESS("SUCCESS","支付成功"), |
|||
/*** 转入退款 */ |
|||
REFUND("REFUND","转入退款"), |
|||
/*** 未支付 */ |
|||
NOTPAY("NOTPAY","未支付"), |
|||
/*** 已关闭 */ |
|||
CLOSED("CLOSED","已关闭"), |
|||
/*** 已撤销 */ |
|||
REVOKED("REVOKED","已撤销"), |
|||
/*** 用户支付中 */ |
|||
USERPAYING("USERPAYING","用户支付中"), |
|||
/*** 支付失败 */ |
|||
PAYERROR("PAYERROR","支付失败"); |
|||
|
|||
public String value; |
|||
public String phase; |
|||
TRADE_STATE(String value,String thePhase){ |
|||
this.value =value; |
|||
this.phase = thePhase; |
|||
} |
|||
} |
|||
|
|||
/*** 微信支付订单号 微信支付系统生成的订单号 */ |
|||
private String transaction_id; |
|||
/*** 订单金额 */ |
|||
private AmountBean amount; |
|||
/*** 应用ID */ |
|||
private String mchid; |
|||
/*** |
|||
* 交易状态,枚举值: |
|||
* SUCCESS:支付成功 |
|||
* REFUND:转入退款 |
|||
* NOTPAY:未支付 |
|||
* CLOSED:已关闭 |
|||
* REVOKED:已撤销(付款码支付) |
|||
* USERPAYING:用户支付中(付款码支付) |
|||
* PAYERROR:支付失败(其他原因,如银行返回失败) |
|||
*/ |
|||
private String trade_state; |
|||
/*** 银行类型,采用字符串类型的银行标识 */ |
|||
private String bank_type; |
|||
/*** 支付完成时间 yyyy-MM-DDTHH:mm:ss+TIMEZONE*/ |
|||
private String success_time; |
|||
/*** 支付者 */ |
|||
private PayerBean payer; |
|||
/*** 商户订单号 商户系统内部订单号 */ |
|||
private String out_trade_no; |
|||
/*** 应用ID */ |
|||
private String appid; |
|||
/*** 交易状态描述*/ |
|||
private String trade_state_desc; |
|||
/*** |
|||
* 交易类型,枚举值: |
|||
* JSAPI:公众号支付 |
|||
* NATIVE:扫码支付 |
|||
* APP:APP支付 |
|||
* MICROPAY:付款码支付 |
|||
* MWEB:H5支付 |
|||
* FACEPAY:刷脸支付 |
|||
*/ |
|||
private String trade_type; |
|||
/*** 附加数据 */ |
|||
private String attach; |
|||
/*** 场景信息 */ |
|||
private SceneInfoBean scene_info; |
|||
/*** 优惠功能 */ |
|||
private List<PromotionDetailBean> promotion_detail; |
|||
|
|||
@Data |
|||
public static class AmountBean { |
|||
/*** 用户支付金额,单位为分 */ |
|||
private int payer_total; |
|||
/*** 订单总金额,单位为分 */ |
|||
private int total; |
|||
/*** 货币类型 */ |
|||
private String currency; |
|||
/*** 用户支付币种 */ |
|||
private String payer_currency; |
|||
} |
|||
|
|||
@Data |
|||
public static class PayerBean { |
|||
/*** 用户标识 */ |
|||
private String openid; |
|||
} |
|||
|
|||
@Data |
|||
public static class SceneInfoBean { |
|||
/*** 终端设备号(门店号或收银设备ID) */ |
|||
private String device_id; |
|||
} |
|||
|
|||
@Data |
|||
public static class PromotionDetailBean { |
|||
/*** 优惠券面额 */ |
|||
private int amount; |
|||
/*** 微信出资 */ |
|||
private int wechatpay_contribute; |
|||
/*** 券ID */ |
|||
private String coupon_id; |
|||
/*** 优惠范围 */ |
|||
private String scope; |
|||
/*** 商户出资 */ |
|||
private int merchant_contribute; |
|||
/*** 优惠名称 */ |
|||
private String name; |
|||
/*** 优惠类型 */ |
|||
private String type; |
|||
/*** 其他出资 */ |
|||
private int other_contribute; |
|||
/*** 优惠币种 */ |
|||
private String currency; |
|||
/*** 活动ID */ |
|||
private String stock_id; |
|||
/*** 单品列表 */ |
|||
private List<GoodsDetailBean> goods_detail; |
|||
|
|||
@Data |
|||
public static class GoodsDetailBean { |
|||
/*** 商品备注 */ |
|||
private String goods_remark; |
|||
/*** 商品数量 */ |
|||
private int quantity; |
|||
/*** 商品优惠金额 */ |
|||
private int discount_amount; |
|||
/*** 商品编码 */ |
|||
private String goods_id; |
|||
/*** 商品单价 */ |
|||
private int unit_price; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,43 @@ |
|||
package com.ccsens.wechatutil.bean.dto; |
|||
|
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author 逗 |
|||
*/ |
|||
@Data |
|||
public class WxNativePayNotice { |
|||
|
|||
/**"消息id"*/ |
|||
private String id; |
|||
/**"通知创建的时间,格式为yyyy-MM-DDTHH:mm:ss+TIMEZONE"*/ |
|||
private String create_time; |
|||
/**"通知的类型 SUCCESS"*/ |
|||
private String event_type; |
|||
/**"通知的资源数据类型 encrypt-resource"*/ |
|||
private String resource_type; |
|||
/**"回调摘要"*/ |
|||
private String summary; |
|||
/**"微信支付回调消息"*/ |
|||
private NoticeResource resource; |
|||
|
|||
@Data |
|||
public static class NoticeResource { |
|||
//"对开启结果数据进行加密的加密算法,目前只支持AEAD_AES_256_GCM ")
|
|||
private String algorithm; |
|||
//"Base64编码后的开启/停用结果数据密文")
|
|||
private String ciphertext; |
|||
//"附加数据")
|
|||
private String associated_data; |
|||
//"原始回调类型 为transaction")
|
|||
private String original_type; |
|||
//"加密使用的随机串")
|
|||
private String nonce; |
|||
} |
|||
|
|||
@Data |
|||
public static class NoticeResourceDecode { |
|||
|
|||
} |
|||
|
|||
} |
@ -0,0 +1,22 @@ |
|||
package com.ccsens.wechatutil.bean.vo; |
|||
|
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author 逗 |
|||
*/ |
|||
@Data |
|||
public class NativePayNoticeVo { |
|||
/**"code"*/ |
|||
private String code; |
|||
/**消息*/ |
|||
private String message; |
|||
|
|||
public NativePayNoticeVo() { |
|||
} |
|||
|
|||
public NativePayNoticeVo(String code, String message) { |
|||
this.code = code; |
|||
this.message = message; |
|||
} |
|||
} |
@ -0,0 +1,120 @@ |
|||
package com.ccsens.wechatutil.bean.vo; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author 逗 |
|||
*/ |
|||
@Data |
|||
public class WxNativePay { |
|||
/** |
|||
* 应用ID 是 |
|||
*/ |
|||
private String appid; |
|||
/** |
|||
* 直连商户号 是 |
|||
*/ |
|||
private String mchid; |
|||
/** |
|||
* 商品描述 是 |
|||
*/ |
|||
private String description; |
|||
/** |
|||
* 商户订单号 是 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一 |
|||
*/ |
|||
private String out_trade_no; |
|||
/** |
|||
* 交易结束时间 否 |
|||
*/ |
|||
private String time_expire; |
|||
/** |
|||
* 附加数据 否 |
|||
*/ |
|||
private String attach; |
|||
/** |
|||
* 通知地址 是 |
|||
*/ |
|||
private String notify_url; |
|||
/** |
|||
* 订单优惠标记 否 |
|||
*/ |
|||
private String goods_tag; |
|||
/** |
|||
* 订单金额 是 |
|||
*/ |
|||
private Amount amount; |
|||
/** |
|||
* 优惠功能 |
|||
*/ |
|||
private Detail detail; |
|||
/** |
|||
* 场景信息 |
|||
*/ |
|||
private SceneInfo scene_info; |
|||
/** |
|||
* 结算信息 |
|||
*/ |
|||
private SceneInfo settle_info; |
|||
|
|||
@Data |
|||
public static class Amount{ |
|||
//总金额 是 单位为分。
|
|||
private int total; |
|||
//货币类型
|
|||
private String currency; |
|||
} |
|||
|
|||
@Data |
|||
public static class Detail{ |
|||
//订单原价 单位为分。
|
|||
private int cost_price; |
|||
//商品小票ID
|
|||
private String invoice_id; |
|||
//单品列表
|
|||
private List<GoodsDetail> goods_detail; |
|||
} |
|||
|
|||
@Data |
|||
public static class GoodsDetail{ |
|||
//商户侧商品编码 是
|
|||
private String merchant_goods_id; |
|||
//微信支付商品编码 否
|
|||
private String wechatpay_goods_id; |
|||
//商品名称
|
|||
private String goods_name; |
|||
//商品数量 是
|
|||
private int quantity; |
|||
//商品单价 是
|
|||
private int unit_price; |
|||
} |
|||
|
|||
@Data |
|||
public static class SceneInfo{ |
|||
//用户终端IP
|
|||
private String payer_client_ip; |
|||
//商户端设备号
|
|||
private String device_id; |
|||
//商户门店信息
|
|||
private StoreInfo store_info; |
|||
} |
|||
|
|||
@Data |
|||
public static class StoreInfo{ |
|||
//门店编号 是
|
|||
private String id; |
|||
//门店名称
|
|||
private String name; |
|||
//地区编码
|
|||
private String area_code; |
|||
//详细地址
|
|||
private String address; |
|||
} |
|||
|
|||
@Data |
|||
public static class SettleInfo{ |
|||
//是否指定分账
|
|||
private boolean profit_sharing; |
|||
} |
|||
} |
@ -0,0 +1,47 @@ |
|||
package com.ccsens.wechatutil.payutil.wxnative; |
|||
|
|||
import cn.hutool.core.util.StrUtil; |
|||
import com.alibaba.fastjson.JSON; |
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.ccsens.util.QrCodeUtil; |
|||
import com.ccsens.wechatutil.bean.vo.WxNativePay; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import okhttp3.HttpUrl; |
|||
|
|||
/** |
|||
* @author 逗 |
|||
*/ |
|||
@Slf4j |
|||
public class NativePay { |
|||
|
|||
/** |
|||
* 生成订单,返回收款二维码 |
|||
* @param serialNo 证书序列号 |
|||
* @param pathPem 秘钥文件地址 |
|||
* @param wxNativePay 订单信息 |
|||
* @param filePath 二维码存放地址(前缀路径) |
|||
* @return 返回二维码访问路径(不包含前缀) |
|||
*/ |
|||
public static String generateOrder( String serialNo, String pathPem, WxNativePay wxNativePay, String filePath){ |
|||
String path = ""; |
|||
try { |
|||
HttpUrl httpurl = HttpUrl.parse(NativeUtils.NATIVE_URL); |
|||
String body = String.valueOf(JSON.parseObject(JSON.toJSONString(wxNativePay))); |
|||
String authorization = NativeUtils.schema + " " + |
|||
NativeUtils.getToken("POST", httpurl, body, wxNativePay.getOut_trade_no(), wxNativePay.getMchid(), serialNo, pathPem); |
|||
//下单调用的接口,JSON格式
|
|||
String codeUrl = NativeUtils.nativePostBody(NativeUtils.NATIVE_URL, body, authorization); |
|||
log.info("调用微信下单返回:{}", codeUrl); |
|||
JSONObject jsonObject = JSONObject.parseObject(codeUrl); |
|||
String url = jsonObject.getString("code_url"); |
|||
if(StrUtil.isNotBlank(url)){ |
|||
path = QrCodeUtil.getQrCodeWithUtf8(url,filePath); |
|||
}else { |
|||
path = String.valueOf(jsonObject); |
|||
} |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return path; |
|||
} |
|||
} |
@ -0,0 +1,224 @@ |
|||
package com.ccsens.wechatutil.payutil.wxnative; |
|||
|
|||
import cn.hutool.core.util.StrUtil; |
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.google.zxing.BarcodeFormat; |
|||
import com.google.zxing.EncodeHintType; |
|||
import com.google.zxing.MultiFormatWriter; |
|||
import com.google.zxing.WriterException; |
|||
import com.google.zxing.client.j2se.MatrixToImageWriter; |
|||
import com.google.zxing.common.BitMatrix; |
|||
import okhttp3.HttpUrl; |
|||
import org.apache.commons.lang3.RandomStringUtils; |
|||
|
|||
import javax.crypto.Cipher; |
|||
import javax.crypto.NoSuchPaddingException; |
|||
import javax.crypto.spec.GCMParameterSpec; |
|||
import javax.crypto.spec.SecretKeySpec; |
|||
import java.io.*; |
|||
import java.net.HttpURLConnection; |
|||
import java.net.URL; |
|||
import java.nio.file.FileSystems; |
|||
import java.nio.file.Files; |
|||
import java.nio.file.Path; |
|||
import java.nio.file.Paths; |
|||
import java.security.*; |
|||
import java.security.spec.InvalidKeySpecException; |
|||
import java.security.spec.PKCS8EncodedKeySpec; |
|||
import java.util.Base64; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
import java.util.UUID; |
|||
|
|||
|
|||
/** |
|||
* @author 逗 |
|||
*/ |
|||
public class NativeUtils { |
|||
|
|||
static String schema = "WECHATPAY2-SHA256-RSA2048"; |
|||
/** |
|||
* 请求地址 |
|||
*/ |
|||
static String NATIVE_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/native"; |
|||
|
|||
|
|||
/** |
|||
* 获取签名信息所有值 |
|||
* |
|||
* @param method 请求方法 |
|||
* @param url URL地址 |
|||
* @param body BOdy参数 |
|||
* @return |
|||
*/ |
|||
static String getToken(String method, HttpUrl url, String body, String orderId, String mchId, String serialNo, String pathPem) throws Exception { |
|||
//获得系统时间,把毫秒换算成秒 /1000
|
|||
long timestamp = System.currentTimeMillis() / 1000; |
|||
String message = buildMessage(method, url, timestamp, orderId, body); |
|||
String signature = sign(message.getBytes("utf-8"), pathPem); |
|||
return "mchid=\"" + mchId + "\"," |
|||
+ "nonce_str=\"" + orderId + "\"," |
|||
+ "timestamp=\"" + timestamp + "\"," |
|||
+ "serial_no=\"" + serialNo + "\"," |
|||
+ "signature=\"" + signature + "\""; |
|||
} |
|||
|
|||
/** |
|||
* 拼接明文数值 |
|||
* |
|||
* @param method 请求方法 GET or POST |
|||
* @param url 网络请求方法地址 取除域名项 |
|||
* @param timestamp 时间戳 |
|||
* @param nonceStr 随机数 |
|||
* @param body GET请求不需要Body参数,POST需要Body |
|||
* @return |
|||
*/ |
|||
static String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) { |
|||
String canonicalUrl = url.encodedPath(); |
|||
//get请求自动做了校验,会把空字符串进行识别,注意是空字符串!!!!!
|
|||
if (url.encodedQuery() != null) { |
|||
canonicalUrl += "?" + url.encodedQuery(); |
|||
} |
|||
//官方的方法自动做了换行的所有动作,注意唤起支付的参数不一样需要更换(这里是统一下单所以直接照搬即可)
|
|||
return method + "\n" |
|||
+ canonicalUrl + "\n" |
|||
+ timestamp + "\n" |
|||
+ nonceStr + "\n" |
|||
+ body + "\n"; |
|||
} |
|||
|
|||
/** |
|||
* 签名加密 |
|||
* |
|||
* @param message |
|||
* @return |
|||
* @throws NoSuchAlgorithmException |
|||
* @throws SignatureException |
|||
* @throws IOException |
|||
* @throws InvalidKeyException |
|||
*/ |
|||
static String sign(byte[] message, String pathPem) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException { |
|||
//加密方式
|
|||
Signature sign = Signature.getInstance("SHA256withRSA"); |
|||
//私钥,通过getPrivateKey来获取,这是个方法可以接调用 ,需要的是_key.pem文件的绝对路径配上文件名
|
|||
sign.initSign(getPrivateKey(pathPem)); |
|||
sign.update(message); |
|||
|
|||
return Base64.getEncoder().encodeToString(sign.sign()); |
|||
} |
|||
|
|||
/** |
|||
* 获取私钥。 |
|||
* |
|||
* @param filename 私钥文件路径 (required) |
|||
* @return 私钥对象 |
|||
* <p> |
|||
* 完全不需要修改,注意此方法也是去掉了头部和尾部,注意文件路径名 |
|||
*/ |
|||
public static PrivateKey getPrivateKey(String filename) throws IOException { |
|||
|
|||
String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8"); |
|||
try { |
|||
String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "") |
|||
.replace("-----END PRIVATE KEY-----", "") |
|||
.replaceAll("\\s+", ""); |
|||
|
|||
KeyFactory kf = KeyFactory.getInstance("RSA"); |
|||
return kf.generatePrivate( |
|||
new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey))); |
|||
} catch (NoSuchAlgorithmException e) { |
|||
throw new RuntimeException("当前Java环境不支持RSA", e); |
|||
} catch (InvalidKeySpecException e) { |
|||
throw new RuntimeException("无效的密钥格式"); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Post请求带Body参数 |
|||
* |
|||
* @param actionUrl |
|||
* @param params |
|||
* @param requestString |
|||
* @return |
|||
* @throws IOException |
|||
*/ |
|||
public static String nativePostBody(String actionUrl, String params, String requestString) |
|||
throws IOException { |
|||
String serverURL = actionUrl; |
|||
StringBuffer sbf = new StringBuffer(); |
|||
String strRead = null; |
|||
URL url = new URL(serverURL); |
|||
HttpURLConnection connection = (HttpURLConnection) url.openConnection(); |
|||
//请求post方式
|
|||
connection.setRequestMethod("POST"); |
|||
connection.setDoInput(true); |
|||
connection.setDoOutput(true); |
|||
//header内的的参数在这里set
|
|||
connection.setRequestProperty("Content-Type", "application/json"); |
|||
//Native支付需要的参数表头参数
|
|||
connection.setRequestProperty("Authorization", requestString); |
|||
connection.connect(); |
|||
OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream(), "UTF-8"); |
|||
//body参数放这里
|
|||
writer.write(params); |
|||
writer.flush(); |
|||
InputStream is = connection.getInputStream(); |
|||
BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); |
|||
while ((strRead = reader.readLine()) != null) { |
|||
sbf.append(strRead); |
|||
sbf.append("\r\n"); |
|||
} |
|||
reader.close(); |
|||
connection.disconnect(); |
|||
String results = sbf.toString(); |
|||
return results; |
|||
} |
|||
|
|||
// public static void main(String[] args) throws Exception {
|
|||
// String apiV3 = "Bb507Ek1mr95aAvvv9OMp2i9eWxZSv1G";
|
|||
// String nonce = "bexg7pfYD50D";
|
|||
// String ciphertext="i01zjaHjODHIkNkIXal6lKrfxrYtp2IZFe2NeUL/tU7si3p/G52XXqxTRQLuM03D1MTImmzLXocxVOgjNmAT3wb5/2RB6ty0HT2RJzGDnAkR0OxMQseUyiOUNRW3skJtj2/somJ/O1yHI2uktJLd1hitfu2TqoFe18ueBZdFYFHqnSKoayc3qTCKFsNLQ/gArvM0ODG3qCa2x9PypmcpDEx2U0KKdNmn/kLqDjIE6RdW9RpAhG+5wqOw3717sD5uvIg66PvUp7MdGMDiDi1SdcUqRm8x7Fl43uSIq9bAcsJxW4IT877tzkDHNjSY37iOFr9vuFpjMwLnhkZfClCfivh2R9ShrmFVlR1sXikr7rYQQGn0coSQZJ09UVd0UuwSxd5SNiLISkB/MzuLfvFSe2YTFvM/MR4iJy+JLQIEBZc7ZNhv4qrcRUOnjQlWO7G9+iUpFrviItzC7eN1KTkyOBs6QPI28uUDOt/OV548i+0Pf6xmD+rOS+6vvl1WR9F4LFUvCWr3w5uMmejyCRJmWoQY12boiwT+FxsdF0NJnoSheiY+bBZfcDJ/udpo2PBHuryJ";
|
|||
// String associated_data = "transaction";
|
|||
//
|
|||
// byte[] key = apiV3.getBytes("UTF-8");
|
|||
// WxAPIV3AesUtil aesUtil = new WxAPIV3AesUtil(key);
|
|||
// String decryptToString = aesUtil.decryptToString(associated_data.getBytes("UTF-8"),nonce.getBytes("UTF-8"),ciphertext);
|
|||
// System.out.println(decryptToString);
|
|||
// }
|
|||
|
|||
/** |
|||
* 解密微信ApiV3消息 |
|||
*/ |
|||
public static class WxAPIV3AesUtil { |
|||
|
|||
static final int KEY_LENGTH_BYTE = 32; |
|||
static final int TAG_LENGTH_BIT = 128; |
|||
private final byte[] aesKey; |
|||
|
|||
public WxAPIV3AesUtil(byte[] key) { |
|||
if (key.length != KEY_LENGTH_BYTE) { |
|||
throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节"); |
|||
} |
|||
this.aesKey = key; |
|||
} |
|||
|
|||
public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext) |
|||
throws GeneralSecurityException, IOException { |
|||
try { |
|||
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); |
|||
|
|||
SecretKeySpec key = new SecretKeySpec(aesKey, "AES"); |
|||
GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce); |
|||
|
|||
cipher.init(Cipher.DECRYPT_MODE, key, spec); |
|||
cipher.updateAAD(associatedData); |
|||
|
|||
return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8"); |
|||
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) { |
|||
throw new IllegalStateException(e); |
|||
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) { |
|||
throw new IllegalArgumentException(e); |
|||
} |
|||
} |
|||
} |
|||
} |
Loading…
Reference in new issue