Java
更新时间:2025.05.27一、概述
本工具类 WXPayUtility 为使用 Java 接入微信支付的开发者提供了一系列实用的功能,包括 JSON 处理、密钥加载、加密签名、请求头构建、响应验证等。通过使用这个工具类,开发者可以更方便地完成与微信支付相关的开发工作。
二、安装(引入依赖的第三方库)
本工具类依赖以下第三方库:
Google Gson:用于 JSON 数据的序列化和反序列化。
OkHttp:用于 HTTP 请求处理。
你可以通过 Maven 或 Gradle 来引入这些依赖。
如果你使用的 Gradle,请在 build.gradle 中加入:
1implementation 'com.google.code.gson:gson:${VERSION}' 2implementation 'com.squareup.okhttp3:okhttp:${VERSION}'
如果你使用的 Maven,请在 pom.xml 中加入:
1<!-- Google Gson --> 2<dependency> 3 <groupId>com.google.code.gson</groupId> 4 <artifactId>gson</artifactId> 5 <version>${VERSION}</version> 6</dependency> 7<!-- OkHttp --> 8<dependency> 9 <groupId>com.squareup.okhttp3</groupId> 10 <artifactId>okhttp</artifactId> 11 <version>${VERSION}</version> 12</dependency>
三、必需的证书和密钥
运行 SDK 必需以下的商户身份信息,用于构造请求的签名和验证应答的签名:
四、工具类代码
1package com.java.utils; 2 3import com.google.gson.ExclusionStrategy; 4import com.google.gson.FieldAttributes; 5import com.google.gson.Gson; 6import com.google.gson.GsonBuilder; 7import com.google.gson.JsonElement; 8import com.google.gson.JsonObject; 9import com.google.gson.JsonSyntaxException; 10import com.google.gson.annotations.Expose; 11import com.google.gson.annotations.SerializedName; 12import okhttp3.Headers; 13import okhttp3.Response; 14import okio.BufferedSource; 15 16import javax.crypto.BadPaddingException; 17import javax.crypto.Cipher; 18import javax.crypto.IllegalBlockSizeException; 19import javax.crypto.NoSuchPaddingException; 20import javax.crypto.spec.GCMParameterSpec; 21import javax.crypto.spec.SecretKeySpec; 22import java.io.IOException; 23import java.io.UncheckedIOException; 24import java.io.UnsupportedEncodingException; 25import java.net.URLEncoder; 26import java.nio.charset.StandardCharsets; 27import java.nio.file.Files; 28import java.nio.file.Paths; 29import java.security.InvalidAlgorithmParameterException; 30import java.security.InvalidKeyException; 31import java.security.KeyFactory; 32import java.security.NoSuchAlgorithmException; 33import java.security.PrivateKey; 34import java.security.PublicKey; 35import java.security.SecureRandom; 36import java.security.Signature; 37import java.security.SignatureException; 38import java.security.spec.InvalidKeySpecException; 39import java.security.spec.PKCS8EncodedKeySpec; 40import java.security.spec.X509EncodedKeySpec; 41import java.time.DateTimeException; 42import java.time.Duration; 43import java.time.Instant; 44import java.util.Base64; 45import java.util.Map; 46import java.util.Objects; 47 48public class WXPayUtility { 49 private static final Gson gson = new GsonBuilder() 50 .disableHtmlEscaping() 51 .addSerializationExclusionStrategy(new ExclusionStrategy() { 52 @Override 53 public boolean shouldSkipField(FieldAttributes fieldAttributes) { 54 final Expose expose = fieldAttributes.getAnnotation(Expose.class); 55 return expose != null && !expose.serialize(); 56 } 57 58 @Override 59 public boolean shouldSkipClass(Class<?> aClass) { 60 return false; 61 } 62 }) 63 .addDeserializationExclusionStrategy(new ExclusionStrategy() { 64 @Override 65 public boolean shouldSkipField(FieldAttributes fieldAttributes) { 66 final Expose expose = fieldAttributes.getAnnotation(Expose.class); 67 return expose != null && !expose.deserialize(); 68 } 69 70 @Override 71 public boolean shouldSkipClass(Class<?> aClass) { 72 return false; 73 } 74 }) 75 .create(); 76 private static final char[] SYMBOLS = 77 "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); 78 private static final SecureRandom random = new SecureRandom(); 79 80 /** 81 * 将 Object 转换为 JSON 字符串 82 */ 83 public static String toJson(Object object) { 84 return gson.toJson(object); 85 } 86 87 /** 88 * 将 JSON 字符串解析为特定类型的实例 89 */ 90 public static <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException { 91 return gson.fromJson(json, classOfT); 92 } 93 94 /** 95 * 从公私钥文件路径中读取文件内容 96 * 97 * @param keyPath 文件路径 98 * @return 文件内容 99 */ 100 private static String readKeyStringFromPath(String keyPath) { 101 try { 102 return new String(Files.readAllBytes(Paths.get(keyPath)), StandardCharsets.UTF_8); 103 } catch (IOException e) { 104 throw new UncheckedIOException(e); 105 } 106 } 107 108 /** 109 * 读取 PKCS#8 格式的私钥字符串并加载为私钥对象 110 * 111 * @param keyString 私钥文件内容,以 -----BEGIN PRIVATE KEY----- 开头 112 * @return PrivateKey 对象 113 */ 114 public static PrivateKey loadPrivateKeyFromString(String keyString) { 115 try { 116 keyString = keyString.replace("-----BEGIN PRIVATE KEY-----", "") 117 .replace("-----END PRIVATE KEY-----", "") 118 .replaceAll("\\s+", ""); 119 return KeyFactory.getInstance("RSA").generatePrivate( 120 new PKCS8EncodedKeySpec(Base64.getDecoder().decode(keyString))); 121 } catch (NoSuchAlgorithmException e) { 122 throw new UnsupportedOperationException(e); 123 } catch (InvalidKeySpecException e) { 124 throw new IllegalArgumentException(e); 125 } 126 } 127 128 /** 129 * 从 PKCS#8 格式的私钥文件中加载私钥 130 * 131 * @param keyPath 私钥文件路径 132 * @return PrivateKey 对象 133 */ 134 public static PrivateKey loadPrivateKeyFromPath(String keyPath) { 135 return loadPrivateKeyFromString(readKeyStringFromPath(keyPath)); 136 } 137 138 /** 139 * 读取 PKCS#8 格式的公钥字符串并加载为公钥对象 140 * 141 * @param keyString 公钥文件内容,以 -----BEGIN PUBLIC KEY----- 开头 142 * @return PublicKey 对象 143 */ 144 public static PublicKey loadPublicKeyFromString(String keyString) { 145 try { 146 keyString = keyString.replace("-----BEGIN PUBLIC KEY-----", "") 147 .replace("-----END PUBLIC KEY-----", "") 148 .replaceAll("\\s+", ""); 149 return KeyFactory.getInstance("RSA").generatePublic( 150 new X509EncodedKeySpec(Base64.getDecoder().decode(keyString))); 151 } catch (NoSuchAlgorithmException e) { 152 throw new UnsupportedOperationException(e); 153 } catch (InvalidKeySpecException e) { 154 throw new IllegalArgumentException(e); 155 } 156 } 157 158 /** 159 * 从 PKCS#8 格式的公钥文件中加载公钥 160 * 161 * @param keyPath 公钥文件路径 162 * @return PublicKey 对象 163 */ 164 public static PublicKey loadPublicKeyFromPath(String keyPath) { 165 return loadPublicKeyFromString(readKeyStringFromPath(keyPath)); 166 } 167 168 /** 169 * 创建指定长度的随机字符串,字符集为[0-9a-zA-Z],可用于安全相关用途 170 */ 171 public static String createNonce(int length) { 172 char[] buf = new char[length]; 173 for (int i = 0; i < length; ++i) { 174 buf[i] = SYMBOLS[random.nextInt(SYMBOLS.length)]; 175 } 176 return new String(buf); 177 } 178 179 /** 180 * 使用公钥按照 RSA_PKCS1_OAEP_PADDING 算法进行加密 181 * 182 * @param publicKey 加密用公钥对象 183 * @param plaintext 待加密明文 184 * @return 加密后密文 185 */ 186 public static String encrypt(PublicKey publicKey, String plaintext) { 187 final String transformation = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding"; 188 189 try { 190 Cipher cipher = Cipher.getInstance(transformation); 191 cipher.init(Cipher.ENCRYPT_MODE, publicKey); 192 return Base64.getEncoder().encodeToString(cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8))); 193 } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { 194 throw new IllegalArgumentException("The current Java environment does not support " + transformation, e); 195 } catch (InvalidKeyException e) { 196 throw new IllegalArgumentException("RSA encryption using an illegal publicKey", e); 197 } catch (BadPaddingException | IllegalBlockSizeException e) { 198 throw new IllegalArgumentException("Plaintext is too long", e); 199 } 200 } 201 202 public static String aesAeadDecrypt(byte[] key, byte[] associatedData, byte[] nonce, 203 byte[] ciphertext) { 204 final String transformation = "AES/GCM/NoPadding"; 205 final String algorithm = "AES"; 206 final int tagLengthBit = 128; 207 208 try { 209 Cipher cipher = Cipher.getInstance(transformation); 210 cipher.init( 211 Cipher.DECRYPT_MODE, 212 new SecretKeySpec(key, algorithm), 213 new GCMParameterSpec(tagLengthBit, nonce)); 214 if (associatedData != null) { 215 cipher.updateAAD(associatedData); 216 } 217 return new String(cipher.doFinal(ciphertext), StandardCharsets.UTF_8); 218 } catch (InvalidKeyException 219 | InvalidAlgorithmParameterException 220 | BadPaddingException 221 | IllegalBlockSizeException 222 | NoSuchAlgorithmException 223 | NoSuchPaddingException e) { 224 throw new IllegalArgumentException(String.format("AesAeadDecrypt with %s Failed", 225 transformation), e); 226 } 227 } 228 229 /** 230 * 使用私钥按照指定算法进行签名 231 * 232 * @param message 待签名串 233 * @param algorithm 签名算法,如 SHA256withRSA 234 * @param privateKey 签名用私钥对象 235 * @return 签名结果 236 */ 237 public static String sign(String message, String algorithm, PrivateKey privateKey) { 238 byte[] sign; 239 try { 240 Signature signature = Signature.getInstance(algorithm); 241 signature.initSign(privateKey); 242 signature.update(message.getBytes(StandardCharsets.UTF_8)); 243 sign = signature.sign(); 244 } catch (NoSuchAlgorithmException e) { 245 throw new UnsupportedOperationException("The current Java environment does not support " + algorithm, e); 246 } catch (InvalidKeyException e) { 247 throw new IllegalArgumentException(algorithm + " signature uses an illegal privateKey.", e); 248 } catch (SignatureException e) { 249 throw new RuntimeException("An error occurred during the sign process.", e); 250 } 251 return Base64.getEncoder().encodeToString(sign); 252 } 253 254 /** 255 * 使用公钥按照特定算法验证签名 256 * 257 * @param message 待签名串 258 * @param signature 待验证的签名内容 259 * @param algorithm 签名算法,如:SHA256withRSA 260 * @param publicKey 验签用公钥对象 261 * @return 签名验证是否通过 262 */ 263 public static boolean verify(String message, String signature, String algorithm, 264 PublicKey publicKey) { 265 try { 266 Signature sign = Signature.getInstance(algorithm); 267 sign.initVerify(publicKey); 268 sign.update(message.getBytes(StandardCharsets.UTF_8)); 269 return sign.verify(Base64.getDecoder().decode(signature)); 270 } catch (SignatureException e) { 271 return false; 272 } catch (InvalidKeyException e) { 273 throw new IllegalArgumentException("verify uses an illegal publickey.", e); 274 } catch (NoSuchAlgorithmException e) { 275 throw new UnsupportedOperationException("The current Java environment does not support" + algorithm, e); 276 } 277 } 278 279 /** 280 * 根据微信支付APIv3请求签名规则构造 Authorization 签名 281 * 282 * @param mchid 商户号 283 * @param certificateSerialNo 商户API证书序列号 284 * @param privateKey 商户API证书私钥 285 * @param method 请求接口的HTTP方法,请使用全大写表述,如 GET、POST、PUT、DELETE 286 * @param uri 请求接口的URL 287 * @param body 请求接口的Body 288 * @return 构造好的微信支付APIv3 Authorization 头 289 */ 290 public static String buildAuthorization(String mchid, String certificateSerialNo, 291 PrivateKey privateKey, 292 String method, String uri, String body) { 293 String nonce = createNonce(32); 294 long timestamp = Instant.now().getEpochSecond(); 295 296 String message = String.format("%s\n%s\n%d\n%s\n%s\n", method, uri, timestamp, nonce, 297 body == null ? "" : body); 298 299 String signature = sign(message, "SHA256withRSA", privateKey); 300 301 return String.format( 302 "WECHATPAY2-SHA256-RSA2048 mchid=\"%s\",nonce_str=\"%s\",signature=\"%s\"," + 303 "timestamp=\"%d\",serial_no=\"%s\"", 304 mchid, nonce, signature, timestamp, certificateSerialNo); 305 } 306 307 /** 308 * 对参数进行 URL 编码 309 * 310 * @param content 参数内容 311 * @return 编码后的内容 312 */ 313 public static String urlEncode(String content) { 314 try { 315 return URLEncoder.encode(content, StandardCharsets.UTF_8.name()); 316 } catch (UnsupportedEncodingException e) { 317 throw new RuntimeException(e); 318 } 319 } 320 321 /** 322 * 对参数Map进行 URL 编码,生成 QueryString 323 * 324 * @param params Query参数Map 325 * @return QueryString 326 */ 327 public static String urlEncode(Map<String, Object> params) { 328 if (params == null || params.isEmpty()) { 329 return ""; 330 } 331 332 int index = 0; 333 StringBuilder result = new StringBuilder(); 334 for (Map.Entry<String, Object> entry : params.entrySet()) { 335 if (entry.getValue() == null) { 336 continue; 337 } 338 result.append(entry.getKey()) 339 .append("=") 340 .append(urlEncode(entry.getValue().toString())); 341 index++; 342 if (index < params.size()) { 343 result.append("&"); 344 } 345 } 346 return result.toString(); 347 } 348 349 /** 350 * 从应答中提取 Body 351 * 352 * @param response HTTP 请求应答对象 353 * @return 应答中的Body内容,Body为空时返回空字符串 354 */ 355 public static String extractBody(Response response) { 356 if (response.body() == null) { 357 return ""; 358 } 359 360 try { 361 BufferedSource source = response.body().source(); 362 return source.readUtf8(); 363 } catch (IOException e) { 364 throw new RuntimeException(String.format("An error occurred during reading response body. " + 365 "Status: %d", response.code()), e); 366 } 367 } 368 369 /** 370 * 根据微信支付APIv3应答验签规则对应答签名进行验证,验证不通过时抛出异常 371 * 372 * @param wechatpayPublicKeyId 微信支付公钥ID 373 * @param wechatpayPublicKey 微信支付公钥对象 374 * @param headers 微信支付应答 Header 列表 375 * @param body 微信支付应答 Body 376 */ 377 public static void validateResponse(String wechatpayPublicKeyId, PublicKey wechatpayPublicKey, 378 Headers headers, 379 String body) { 380 String timestamp = headers.get("Wechatpay-Timestamp"); 381 String requestId = headers.get("Request-ID"); 382 try { 383 Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestamp)); 384 // 拒绝过期请求 385 if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= 5) { 386 throw new IllegalArgumentException( 387 String.format("Validate response failed, timestamp[%s] is expired, request-id[%s]", 388 timestamp, requestId)); 389 } 390 } catch (DateTimeException | NumberFormatException e) { 391 throw new IllegalArgumentException( 392 String.format("Validate response failed, timestamp[%s] is invalid, request-id[%s]", 393 timestamp, requestId)); 394 } 395 String serialNumber = headers.get("Wechatpay-Serial"); 396 if (!Objects.equals(serialNumber, wechatpayPublicKeyId)) { 397 throw new IllegalArgumentException( 398 String.format("Validate response failed, Invalid Wechatpay-Serial, Local: %s, Remote: " + 399 "%s", wechatpayPublicKeyId, serialNumber)); 400 } 401 402 String signature = headers.get("Wechatpay-Signature"); 403 String message = String.format("%s\n%s\n%s\n", timestamp, headers.get("Wechatpay-Nonce"), 404 body == null ? "" : body); 405 406 boolean success = verify(message, signature, "SHA256withRSA", wechatpayPublicKey); 407 if (!success) { 408 throw new IllegalArgumentException( 409 String.format("Validate response failed,the WechatPay signature is incorrect.%n" 410 + "Request-ID[%s]\tresponseHeader[%s]\tresponseBody[%.1024s]", 411 headers.get("Request-ID"), headers, body)); 412 } 413 } 414 415 /** 416 * 根据微信支付APIv3通知验签规则对通知签名进行验证,验证不通过时抛出异常 417 * @param wechatpayPublicKeyId 微信支付公钥ID 418 * @param wechatpayPublicKey 微信支付公钥对象 419 * @param headers 微信支付通知 Header 列表 420 * @param body 微信支付通知 Body 421 */ 422 public static void validateNotification(String wechatpayPublicKeyId, 423 PublicKey wechatpayPublicKey, Headers headers, 424 String body) { 425 String timestamp = headers.get("Wechatpay-Timestamp"); 426 try { 427 Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestamp)); 428 // 拒绝过期请求 429 if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= 5) { 430 throw new IllegalArgumentException( 431 String.format("Validate notification failed, timestamp[%s] is expired", timestamp)); 432 } 433 } catch (DateTimeException | NumberFormatException e) { 434 throw new IllegalArgumentException( 435 String.format("Validate notification failed, timestamp[%s] is invalid", timestamp)); 436 } 437 String serialNumber = headers.get("Wechatpay-Serial"); 438 if (!Objects.equals(serialNumber, wechatpayPublicKeyId)) { 439 throw new IllegalArgumentException( 440 String.format("Validate notification failed, Invalid Wechatpay-Serial, Local: %s, " + 441 "Remote: %s", 442 wechatpayPublicKeyId, 443 serialNumber)); 444 } 445 446 String signature = headers.get("Wechatpay-Signature"); 447 String message = String.format("%s\n%s\n%s\n", timestamp, headers.get("Wechatpay-Nonce"), 448 body == null ? "" : body); 449 450 boolean success = verify(message, signature, "SHA256withRSA", wechatpayPublicKey); 451 if (!success) { 452 throw new IllegalArgumentException( 453 String.format("Validate notification failed, WechatPay signature is incorrect.\n" 454 + "responseHeader[%s]\tresponseBody[%.1024s]", 455 headers, body)); 456 } 457 } 458 459 /** 460 * 对微信支付通知进行签名验证、解析,同时将业务数据解密。验签名失败、解析失败、解密失败时抛出异常 461 * @param apiv3Key 商户的 APIv3 Key 462 * @param wechatpayPublicKeyId 微信支付公钥ID 463 * @param wechatpayPublicKey 微信支付公钥对象 464 * @param headers 微信支付应答 Header 列表 465 * @param body 微信支付应答 Body 466 * @return 解析后的通知内容,解密后的业务数据可以使用 Notification.getPlaintext() 访问 467 */ 468 public static Notification parseNotification(String apiv3Key, String wechatpayPublicKeyId, 469 PublicKey wechatpayPublicKey, Headers headers, 470 String body) { 471 validateNotification(wechatpayPublicKeyId, wechatpayPublicKey, headers, body); 472 Notification notification = gson.fromJson(body, Notification.class); 473 notification.decrypt(apiv3Key); 474 return notification; 475 } 476 477 /** 478 * 微信支付API错误异常,发送HTTP请求成功,但返回状态码不是 2XX 时抛出本异常 479 */ 480 public static class ApiException extends RuntimeException { 481 private static final long serialVersionUID = 2261086748874802175L; 482 483 private final int statusCode; 484 private final String body; 485 private final Headers headers; 486 private final String errorCode; 487 private final String errorMessage; 488 489 public ApiException(int statusCode, String body, Headers headers) { 490 super(String.format("微信支付API访问失败,StatusCode: [%s], Body: [%s], Headers: [%s]", statusCode, 491 body, headers)); 492 this.statusCode = statusCode; 493 this.body = body; 494 this.headers = headers; 495 496 if (body != null && !body.isEmpty()) { 497 JsonElement code; 498 JsonElement message; 499 500 try { 501 JsonObject jsonObject = gson.fromJson(body, JsonObject.class); 502 code = jsonObject.get("code"); 503 message = jsonObject.get("message"); 504 } catch (JsonSyntaxException ignored) { 505 code = null; 506 message = null; 507 } 508 this.errorCode = code == null ? null : code.getAsString(); 509 this.errorMessage = message == null ? null : message.getAsString(); 510 } else { 511 this.errorCode = null; 512 this.errorMessage = null; 513 } 514 } 515 516 /** 517 * 获取 HTTP 应答状态码 518 */ 519 public int getStatusCode() { 520 return statusCode; 521 } 522 523 /** 524 * 获取 HTTP 应答包体内容 525 */ 526 public String getBody() { 527 return body; 528 } 529 530 /** 531 * 获取 HTTP 应答 Header 532 */ 533 public Headers getHeaders() { 534 return headers; 535 } 536 537 /** 538 * 获取 错误码 (错误应答中的 code 字段) 539 */ 540 public String getErrorCode() { 541 return errorCode; 542 } 543 544 /** 545 * 获取 错误消息 (错误应答中的 message 字段) 546 */ 547 public String getErrorMessage() { 548 return errorMessage; 549 } 550 } 551 552 public static class Notification { 553 @SerializedName("id") 554 private String id; 555 @SerializedName("create_time") 556 private String createTime; 557 @SerializedName("event_type") 558 private String eventType; 559 @SerializedName("resource_type") 560 private String resourceType; 561 @SerializedName("summary") 562 private String summary; 563 @SerializedName("resource") 564 private Resource resource; 565 private String plaintext; 566 567 public String getId() { 568 return id; 569 } 570 571 public String getCreateTime() { 572 return createTime; 573 } 574 575 public String getEventType() { 576 return eventType; 577 } 578 579 public String getResourceType() { 580 return resourceType; 581 } 582 583 public String getSummary() { 584 return summary; 585 } 586 587 public Resource getResource() { 588 return resource; 589 } 590 591 /** 592 * 获取解密后的业务数据(JSON字符串,需要自行解析) 593 */ 594 public String getPlaintext() { 595 return plaintext; 596 } 597 598 private void validate() { 599 if (resource == null) { 600 throw new IllegalArgumentException("Missing required field `resource` in notification"); 601 } 602 resource.validate(); 603 } 604 605 /** 606 * 使用 APIv3Key 对通知中的业务数据解密,解密结果可以通过 getPlainText 访问。 607 * 外部拿到的 Notification 一定是解密过的,因此本方法没有设置为 public 608 * @param apiv3Key 商户APIv3 Key 609 */ 610 private void decrypt(String apiv3Key) { 611 validate(); 612 613 plaintext = aesAeadDecrypt( 614 apiv3Key.getBytes(StandardCharsets.UTF_8), 615 resource.associatedData.getBytes(StandardCharsets.UTF_8), 616 resource.nonce.getBytes(StandardCharsets.UTF_8), 617 Base64.getDecoder().decode(resource.ciphertext) 618 ); 619 } 620 621 public static class Resource { 622 @SerializedName("algorithm") 623 private String algorithm; 624 625 @SerializedName("ciphertext") 626 private String ciphertext; 627 628 @SerializedName("associated_data") 629 private String associatedData; 630 631 @SerializedName("nonce") 632 private String nonce; 633 634 @SerializedName("original_type") 635 private String originalType; 636 637 public String getAlgorithm() { 638 return algorithm; 639 } 640 641 public String getCiphertext() { 642 return ciphertext; 643 } 644 645 public String getAssociatedData() { 646 return associatedData; 647 } 648 649 public String getNonce() { 650 return nonce; 651 } 652 653 public String getOriginalType() { 654 return originalType; 655 } 656 657 private void validate() { 658 if (algorithm == null || algorithm.isEmpty()) { 659 throw new IllegalArgumentException("Missing required field `algorithm` in Notification" + 660 ".Resource"); 661 } 662 if (!Objects.equals(algorithm, "AEAD_AES_256_GCM")) { 663 throw new IllegalArgumentException(String.format("Unsupported `algorithm`[%s] in " + 664 "Notification.Resource", algorithm)); 665 } 666 667 if (ciphertext == null || ciphertext.isEmpty()) { 668 throw new IllegalArgumentException("Missing required field `ciphertext` in Notification" + 669 ".Resource"); 670 } 671 672 if (associatedData == null || associatedData.isEmpty()) { 673 throw new IllegalArgumentException("Missing required field `associatedData` in " + 674 "Notification.Resource"); 675 } 676 677 if (nonce == null || nonce.isEmpty()) { 678 throw new IllegalArgumentException("Missing required field `nonce` in Notification" + 679 ".Resource"); 680 } 681 682 if (originalType == null || originalType.isEmpty()) { 683 throw new IllegalArgumentException("Missing required field `originalType` in " + 684 "Notification.Resource"); 685 } 686 } 687 } 688 } 689}
文档是否有帮助