このドキュメントでは、Dataphin BPMS をサードパーティの承認ワークフローと統合する方法について、利用可能なインターフェイス機能、統合プロセス、およびサンプルのユースケースを説明します。
前提条件
Dataphin システムで [承認設定] が構成済みである必要があります。手順については、「承認設定」をご参照ください。O&M テナントは、 [承認設定] > [承認システムの選択] > [その他] でこの設定を確認できます。
承認 API
{url}:サードパーティの承認ワークフローの統合エンドポイントです。承認リクエストを送信する URL はhttp(s)://{url}です。accessToken:サーバーサイド API コールの認証情報です。このトークンは、Dataphin ページで自動生成されるか、手動で生成して Dataphin に入力することができます。
接続テスト
リクエストメソッド: POST。
リクエスト URL: http(s)://{url}/dataphin/bpms/check/connect。
リクエストシーケンス図

クエリパラメーター
パラメーター
タイプ
説明
accessToken
String
サーバーサイド API コールの認証情報です。このトークンは、Dataphin ページで自動生成されるか、手動で生成して Dataphin に入力することができます。例:
6d1bxxxx。Dataphin によって自動生成されるか、ユーザーが生成して入力します。例:
6d1bxxxx。timestamp
String
メッセージのタイムスタンプ (ミリ秒単位)。例:
1654156836531。ボディパラメーター
checkEvent(String) : チェックイベント。接続テストを実行するには、これをconnectivityに設定します。レスポンスパラメーター
checkResult(String) : チェック結果。返される値がsuccessの場合にのみ、接続テストが成功したことを示します。その他の値は、テストが失敗したことを示します。例:success。承認の送信
リクエストメソッド: POST。
リクエスト URL: http(s)://{url}/dataphin/bpms/processinstance/create。
承認リクエスト送信のシーケンス図

クエリパラメーター
パラメーター
タイプ
説明
accessToken
String
サーバーサイド API コールの認証情報です。このトークンは、Dataphin ページで自動生成されるか、手動で生成して Dataphin に入力することができます。例:
6d1bxxxx。Dataphin によって自動生成されるか、ユーザーが生成して入力します。例:
6d1bxxxx。timestamp
String
メッセージのタイムスタンプ (ミリ秒単位)。例:
1654156836531。ボディパラメーター
パラメーター
タイプ
説明
applyId
String
Dataphin 承認フォームの ID。例:
1223。title
String
Dataphin 承認フォームのタイトル。例:
dataphin bpms。content
String
Dataphin 承認フォームの内容。例:
bpms content。「承認メッセージのコンテンツメタデータ」をご参照ください。type
String
承認リクエストタイプ: APPROVAL_DOC_TYPE。
コードレビュー: CODE_REVIEW。
公開コントロール: PUBLISH。
ビジネスプランニング: BIZ_PLANNING。
権限承認: AUTH。
デフォルト: DEFAULT。
templateCode
String
承認テンプレートのコード。このパラメーターは空にすることができ、ページの設定に依存します。
承認メッセージのコンテンツメタデータ
パラメーター
タイプ
説明
resourceType
String
承認タスクのタイプ。列挙値。有効な値は次のとおりです:
PhysicalTable: 物理テーブル。LogicTable: 論理テーブル。MetaTable: リアルタイムメタテーブル。MirrorTable: ミラーテーブル。Fun: 関数。DataSource: データソース。FeatureConfig: 機能権限設定。OSAPP: データサービスアプリ。OSAPI: データサービス API。OSLogicUnit: データサービスユニット。OSDS: データサービスデータソース。SECRET_KEY: シークレットキー。GLOBAL_PARAM: グローバル変数。
grantToUsers
List<GrantToUser>
権限を付与するユーザーのリスト。「GrantToUser」をご参照ください。
bpmsEnvironment
BpmsEnvironment
承認システムの環境。「BpmsEnvironment」をご参照ください。
operates
List<String>
要求された権限タイプ。有効な値:
SYNC_READ: データソースからの同期読み取り。SYNC_WRITE: データソースへの同期書き込み。SQL_QUERY: 物理テーブルおよび論理テーブルでの SQL クエリ。SQL_WRITE: 物理テーブルへの SQL 書き込み。SQL_ALTER: 物理テーブルの変更。SQL_DROP: 物理テーブルおよび論理テーブルの削除。SELECT: データサービス API のクエリ。WRITE: 書き込み権限。DEV: 開発権限。USE: 使用権限。UPDATE: テーブルデータの更新。PIPELINE_ENCRY: 統合暗号化。PIPELINE_DECRY: 統合復号化。
levels
List<String>
権限レベル。有効な値は
HIGH、MIDDLE、LOWです。operations
List<String>
権限タイプ。有効な値:
SELECT: クエリ。DESCRIBE: テーブル構造の記述。UPDATE: テーブルデータの更新。ALTER: テーブル構造の変更。DELETE: テーブルの削除。COPY_TASK: コピー。
resources
List<BpmsResource>
リソースの内容。「BpmsResource」をご参照ください。
applyObject
ApplyObject
リクエストされたオブジェクトに関する情報。「ApplyObject」をご参照ください。
reason
String
リクエストの理由。
GrantToUser
パラメーター
タイプ
説明
account
Account
認可されるアカウント。「Account」をご参照ください。
period
Period
有効期間。「Period」をご参照ください。
Account
パラメーター
タイプ
説明
accountType
String
アカウントタイプ。有効な値は、個人アカウント、本番アカウント、アプリケーションアカウントです。
PERSONAL: 個人アカウント。PRODUCTION: 本番アカウント。APPLICATION: アプリケーションアカウント。
userName
String
ユーザー名。
Period
パラメーター
タイプ
説明
periodType
String
有効期間タイプ。長期 (
LONG_TERM) のみがサポートされます。periodEnd
String
権限の有効期限。形式: yyyy-mm-dd。例:
2022-09-11。BpmsEnvironment
パラメーター
タイプ
説明
projectName
String
プロジェクト名。
bizUnitName
String
業務部門名。
resourceEnv
String
環境。有効な値:
PROD: 本番環境。DEV: 開発環境。
BpmsResource
パラメーター
タイプ
説明
resourceType
String
「承認メッセージのコンテンツメタデータ」をご参照ください。
resourceName
String
リソース名。
children
List<Children>
フィールドのリスト。「Children」をご参照ください。
operations
List<String>
「承認メッセージのコンテンツメタデータ」の
operatesパラメーターをご参照ください。authTypes
String
リクエストされた権限のタイプ。
Children
パラメーター
タイプ
説明
resourceName
String
フィールド名。
resourceProperties
String
フィールドのプロパティ。
ResourceProperties
パラメーター
タイプ
説明
columnType
String
フィールドタイプ。
columnIsPartition
String
フィールドがパーティションフィールドであるかを示します。
columnIsPk
String
フィールドがプライマリキーであるかを示します。
ApplyObject
パラメーター
タイプ
説明
objectName
String
オブジェクト名。
codeContent
String
コードの内容。
name
String
ビジネスアクティビティの英語名。
bizObjectType
String
オブジェクトタイプ。
bizObjectChangeType
String
変更タイプ。
bizObjectPkField
String
コンポーネントフィールド。
bizObjectParent
String
親オブジェクト。
bizObjectChildren
String
ダウンストリームサブオブジェクト。
bizProcessCn
String
ビジネスプロセスの中国語名。
bizProcessType
String
アクティビティタイプ。
bizProcessChangeType
String
変更タイプ。
bizProcessNodes
String
プロセスノード。
atomicIndexCn
String
メトリック名。
dataType
String
データタイプ。
unit
String
測定単位。
bizProcess
String
ビジネスプロセス名。
des
String
アクティビティのスコープ。
derivedLogic
String
派生ロジック。
protoLogics
String
計算ロジック。
レスポンスパラメーター
processInstanceId(String) : サードパーティの承認インスタンスの ID。例:6d1bxxxx。承認の取り消し
説明送信した承認リクエストの内容が正しくない場合、または不要になった場合は、取り消すことができます。
リクエストメソッド: POST。
リクエスト URL: http(s)://{url}/dataphin/bpms/processinstance/revoke。
クエリパラメーター
パラメーター
タイプ
説明
accessToken
String
サーバーサイド API コールの認証情報です。このトークンは、Dataphin ページで自動生成されるか、手動で生成して Dataphin に入力することができます。例:
6d1bxxxx。Dataphin によって自動生成されるか、ユーザーが生成して入力します。例:
6d1bxxxx。timestamp
String
メッセージのタイムスタンプ (ミリ秒単位)。例:
1654156836531。ボディパラメーター
パラメーター
タイプ
説明
applyId
String
Dataphin 承認フォームの ID。例:
1223。processInstanceId
String
サードパーティの承認インスタンスの ID。例:
6d1bxxxx。operatingUserId
String
操作者の従業員 ID。例:
6d1bxxxx。レスポンスパラメーター
result(String) : 取り消しの結果。返される値がsuccessの場合にのみ、操作が成功したことを示します。インスタンス URL のクエリ
リクエストメソッド: GET。
リクエスト URL: http(s)://{url}/dataphin/bpms/processinstance/apply。
クエリパラメーター
パラメーター
タイプ
説明
accessToken
String
サーバーサイド API コールの認証情報です。このトークンは、Dataphin ページで自動生成されるか、手動で生成して Dataphin に入力することができます。例:
6d1bxxxx。Dataphin によって自動生成されるか、ユーザーが生成して入力します。例:
6d1bxxxx。timestamp
String
メッセージのタイムスタンプ (ミリ秒単位)。例:
1654156836531。
コールバック API
https://{callbackUrl}は、[Other] の [承認設定] で設定するコールバック URL です。accessToken:サーバーサイド API を呼び出すためのアクセス トークンです。Dataphin が自動で生成するか、ユーザーが手動で生成して Dataphin に入力します。callBackAesKey:認証に使用するコールバック AES キーです。Dataphin が自動で生成するか、ユーザーが手動で生成して Dataphin に入力します。
コールバックメソッド
リクエストメソッド: POST
リクエスト URL: http://{callbackUrl}/api/oa/bpms/integration/thirdparty/callback
クエリパラメーター
パラメーター
タイプ
説明
signature
文字列
メッセージ認証のための署名です。 詳細については、暗号化と復号のメソッドをご参照ください。
timestamp
文字列
メッセージのタイムスタンプ (ミリ秒単位) です。 例:
1654156836531。nonce
文字列
一意のランダムな文字列です。 詳細については、暗号化と復号のメソッドをご参照ください。
ボディパラメーター
encrypt(文字列):暗号化されたメッセージです。 例:ajls384k。レスポンスパラメーター
encrypt(文字列):コールバックステータスを含む暗号化されたメッセージです。 例:djsl495l。コールバックの暗号化されたペイロード
コールバック接続チェック
受信する暗号化パラメーター:
applyStatus(文字列):接続チェックに使用します。 例:CHECK。応答の暗号化パラメーター:
success:コールバックが成功した場合に返される値です。
BPMS インスタンスの承認結果のコールバック
受信する暗号化パラメーター:
パラメーター
タイプ
説明
applyId
String
Dataphin アプリケーション ID です。 例:
1223。processInstanceId
String
サードパーティの承認インスタンスの ID です。 例:
6d1bxxxx。comment
String
承認インスタンスのコメントです。
applyStatus
String
accept:承認済み。reject:拒否済み。revoke:取り消し済み。
nodes
List<ApplyCallBackNode>
サードパーティの承認システムにおける承認ノードのオプションリストです。統合メソッドが [Display all nodes of the third-party approval] に設定されている場合にのみ必要です。
ApplyCallBackNode
パラメーター
タイプ
説明
nodeId
String
ノード ID です。 ノードは 1 から n の順に並んでいます。
nodeStatus
String
ノードステータス (ApproveNodeStatusEnum) です。
APPROVING:処理中。ACCEPTED:承認済み。REJECTED:拒否済み。REVOKED:取り消し済み。
nodeName
String
ノードの名前です。
approverRelation
String
承認関係 (ApproveOperatorEnum) です。
AND:すべての承認者がリクエストを承認する必要があります。OR:いずれかの承認者がリクエストを承認できます。
pendingApprovers
List<ExternalApproverInfo>
現在の保留中の承認者のリストです。 関連するすべての承認者がアクションを実行すると、このリストは空になります。
records
List<OperationRecord>
承認者が実行したアクションのレコードを、時系列順に含みます。
ExternalApproverInfo
パラメーター
タイプ
説明
userId
String
サードパーティの承認システムにおけるユーザー ID です。 例:
1223。userName
String
サードパーティの承認システムにおけるユーザー名です。 例:
Zhang San。OperationRecord
パラメーター
タイプ
説明
operateUser
ExternalApproverInfo
オペレーターの情報です。 例:
{"userId":"1111", "userName":"Zhang San"}。action
String
操作タイプ (ApproveNodeActionType) です。
ASSIGN_APPROVE:権限リクエストの転送。COUNTER_SIGN_APPROVE:権限リクエストにカウンターサインを追加。REVOKE_APPLY:権限リクエストの取り消し。APPROVE:権限リクエストの承認。
comment
String
操作のコメントです。
operatedTime
String
操作のタイムスタンプです。
transferApprovers
List<ExternalApproverInfo>
転送先のオプションリストです。カウンターサインまたは転送操作を実行する場合に必要です。 例:
[{"userId":"2222", "userName":"Li Si"}]。応答の暗号化パラメーター
success:コールバックが成功した場合に返される値です。
承認結果のコールバックペイロード
サードパーティの承認システムでは、2 つの統合方法 (最終的な承認結果のみを表示、すべての承認ノードを表示) をサポートしています。コールバックペイロードの内容は、選択した統合方法によって異なります。
最終結果のみ
この統合方法では、承認プロセスの最終結果のみを返す必要があります。Dataphin では、開始ノードと終了ノードのみが表示されます。サードパーティシステムで承認プロセスが完了した後にのみ、コールバックインターフェイスを呼び出してください。以下はペイロード構造の例です。
{ "applyId": "123456", "applyStatus": "accept", "comment": "承認済み", "processInstanceId": "qwqwqqq" }説明この統合方法では、
nodesフィールドは不要です。applyStatusフィールドはaccept、reject、またはrevokeに設定できます。値approvingは、承認が進行中であることを示します。これは、サードパーティ承認用のすべてのノード統合方法において、表示にのみ使用される中間状態です。
すべての承認ノード
この統合方法では、各承認ノードの詳細を返す必要があります。これにより、Dataphin はすべての承認ノードを表示して同期できます。承認者が操作を実行したら、このコールバックインターフェイスを呼び出して、現在のノード情報で Dataphin を更新してください。
フィールド参照
この統合方法では、
applyId、applyStatus、processInstanceId、およびnodesフィールドを渡す必要があります。申請全体の承認ステータス (
applyStatus)。有効な値:approving、accept、reject、revoke。いずれかの承認ノードが最終状態でない場合は、
applyStatusにapprovingを設定します。すべての承認ノードが承認された場合は、
applyStatusにacceptを設定します。いずれかの承認ノードが却下された場合は、
applyStatusにrejectを設定します。いずれかの承認ノードが取り消された場合は、
applyStatusにrevokeを設定します。
承認ノードのリストは内部の
nodeIdに従ってソートされます。nodeIdは連番 (例:1、2、3) にする必要があります。ノードステータス (
nodeStatus)。有効な値:APPROVING、ACCEPTED、REJECTED、REVOKED。現在の承認ノードが進行中の場合は、
nodeStatusにAPPROVINGを設定します。現在の承認ノードが承認された場合は、
nodeStatusにACCEPTEDを設定します。現在の承認ノードが却下された場合は、
nodeStatusにREJECTEDを設定します。現在の承認ノードが取り消された場合は、
nodeStatusにREVOKEDを設定します。
ノードの表示名 (
nodeName)。ノードの承認関係 (
approverRelation)。有効な値:OR、AND。OR:いずれか 1 人の承認者が承認すると、そのノードは承認済みになります。AND:すべての承認者が承認した後にのみ、そのノードは承認済みになります。
承認待ちの承認者 (
pendingApprovers)。まだ操作していない承認者のリストです。
承認者が操作した場合は、次回のコールバックでこのリストから削除して、その操作を
recordsリストに追加する必要があります。
操作記録 (
records)。操作を実行したユーザー (
operateUser)。操作のコメント (
comment)。操作時刻 (
operatedTime)。recordsリストは、operatedTimeの昇順にソートする必要があります。操作タイプ (
action)。有効な値:ASSIGN_APPROVE、COUNTER_SIGN_APPROVE、REVOKE_APPLY、APPROVE。approverRelationがORで、承認者が承認した場合は、actionをAPPROVEに設定し、nodeStatusをACCEPTEDに設定します。approverRelationがORで、承認者が却下した場合は、actionをAPPROVEに設定し、nodeStatusをREJECTEDに設定します。approverRelationがORで、承認者が取り消した場合は、actionをREVOKE_APPLYに設定し、nodeStatusをREVOKEDに設定します。承認者がタスクを転送した場合は、
actionをASSIGN_APPROVEに設定し、nodeStatusをACCEPTEDに設定します。承認者がカウンターサインを追加した場合は、
actionをCOUNTER_SIGN_APPROVEに設定し、nodeStatusをACCEPTEDに設定します。approverRelationがANDで承認者が承認した場合は、actionをAPPROVEに設定します。ほかの承認者がまだ操作していない場合、nodeStatusはAPPROVINGのままです。すべての承認者が承認したら、nodeStatusをACCEPTEDに設定します。approverRelationがANDで承認者が却下した場合は、actionをAPPROVEに設定し、nodeStatusをREJECTEDに設定します。approverRelationがANDで承認者が取り消した場合は、actionをREVOKE_APPLYに設定し、nodeStatusをREVOKEDに設定します。
転送先の承認者 (
transferApprovers)。このフィールドには、承認の転送またはカウンターサインの追加を受け取るユーザーを指定します。
承認シナリオ
暗号化と復号
暗号化(例:BPMS がコールバックを受信する場合):
Map<String, String> callBackJson = Maps.newHashMap(); callBackJson.put("applyId", "1"); callBackJson.put("applyStatus", "accept"); callBackJson.put("processInstanceId", "2"); callBackJson.put("comment", "test"); // 統合方法として "Display all nodes of the third-party approval" を選択した場合は、nodes フィールドに値を設定します。 String nodesJson = "[{\"approverRelation\":\"OR\",\"nodeId\":\"1\",\"nodeName\":\"Execute Pass Node Test\",\"nodeStatus\":\"ACCEPTED\",\"pendingApprovers\":[],\"records\":[{\"action\":\"APPROVE\",\"comment\":\"Approval granted\",\"operateUser\":{\"userId\":\"22222\",\"userName\":\"Zhang San\"},\"operatedTime\":\"2026-03-18 15:14:23\"}]}]"; callBackJson.put("nodes", nodesJson) String callBackResult = JSON.toJSONString(callBackJson); ThirdPartyCrypto callbackCrypto = new ThirdPartyCrypto(token, aesKey); String timestamp = String.valueOf(System.currentTimeMillis()); String nonce = ThirdPartyCrypto.Utils.getRandomStr(16); String encrypt = callbackCrypto.encrypt(nonce, callBackResult); String signature = callbackCrypto.getSignature(token, timestamp, nonce, encrypt);復号(例:BPMS が success メッセージを返す場合):
String encryptMsg = encrypt; String decryptMsg = callbackCrypto.getDecryptMsg(signature, timestamp, nonce, encryptMsg); if ("success".equalsIgnoreCase(decryptMsg)) { log.error("call back success"); }暗号化と復号のユーティリティ
import com.alibaba.fastjson.JSON; import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.io.ByteArrayOutputStream; import java.lang.reflect.Field; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.Permission; import java.security.PermissionCollection; import java.security.Security; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Random; /** * Dataphin オープンプラットフォーム向けの暗号化と復号の方法です。 */ public class ThirdPartyCrypto { private static final Charset CHARSET = StandardCharsets.UTF_8; private static final Base64 BASE_64 = new Base64(); private final byte[] AES_KEY; private final String TOKEN; /** * エンコーディング AES キーの固定長。 **/ private static final Integer AES_ENCODE_KEY_LENGTH = 43; /** * 暗号化に使用するランダム文字列のバイト長。 **/ private static final Integer RANDOM_LENGTH = 16; /** * コンストラクター。 * * @param token 開発者が設定するトークン。 * @param encodingAesKey 開発者が設定する EncodingAESKey。 * * @throws ThirdPartyEncryptException 操作に失敗した場合にスローされます。エラーコードとメッセージについては、例外を参照してください。 */ public ThirdPartyCrypto(String token, String encodingAesKey) throws ThirdPartyEncryptException { if (null == encodingAesKey || encodingAesKey.length() != AES_ENCODE_KEY_LENGTH) { throw new ThirdPartyEncryptException(ThirdPartyEncryptException.AES_KEY_ILLEGAL); } this.TOKEN = token; AES_KEY = Base64.decodeBase64(encodingAesKey + "="); } public Map<String, String> getEncryptedMap(String plaintext) throws ThirdPartyEncryptException { return getEncryptedMap(plaintext, System.currentTimeMillis(), Utils.getRandomStr(16)); } /** * Dataphin と同期するためにメッセージ本文を暗号化し、暗号化されたマップを返します。 * * @param plaintext メッセージ本文のプレーンテキスト。 * @param timeStamp リクエストのタイムスタンプ。 * @param nonce ランダム文字列(ノンス)。 * @return * @throws ThirdPartyEncryptException */ public Map<String, String> getEncryptedMap(String plaintext, Long timeStamp, String nonce) throws ThirdPartyEncryptException { if (null == plaintext) { throw new ThirdPartyEncryptException(ThirdPartyEncryptException.ENCRYPTION_PLAINTEXT_ILLEGAL); } if (null == timeStamp) { throw new ThirdPartyEncryptException(ThirdPartyEncryptException.ENCRYPTION_TIMESTAMP_ILLEGAL); } if (null == nonce) { throw new ThirdPartyEncryptException(ThirdPartyEncryptException.ENCRYPTION_NONCE_ILLEGAL); } // 暗号化 String encrypt = encrypt(Utils.getRandomStr(RANDOM_LENGTH), plaintext); String signature = getSignature(TOKEN, String.valueOf(timeStamp), nonce, encrypt); Map<String, String> resultMap = new HashMap<String, String>(); resultMap.put("msg_signature", signature); resultMap.put("encrypt", encrypt); resultMap.put("timeStamp", String.valueOf(timeStamp)); resultMap.put("nonce", nonce); return resultMap; } /** * 暗号文を復号します。 * * @param msgSignature 署名文字列。 * @param timeStamp タイムスタンプ。 * @param nonce ランダム文字列。 * @param encryptMsg Base64 エンコードされた暗号文。 * @return 復号後のプレーンテキスト。 * @throws ThirdPartyEncryptException */ public String getDecryptMsg(String msgSignature, String timeStamp, String nonce, String encryptMsg) throws ThirdPartyEncryptException { // 署名を検証します。 String signature = getSignature(TOKEN, timeStamp, nonce, encryptMsg); if (!signature.equals(msgSignature)) { throw new ThirdPartyEncryptException(ThirdPartyEncryptException.SIGNATURE_NOT_MATCH); } // 復号します。 return decrypt(encryptMsg); } /** * プレーンテキストを暗号化します。 * @param plaintext 暗号化するプレーンテキスト。 * @return Base64 エンコードされた暗号文。 */ public String encrypt(String random, String plaintext) throws ThirdPartyEncryptException { try { byte[] randomBytes = random.getBytes(CHARSET); byte[] plainTextBytes = plaintext.getBytes(CHARSET); byte[] lengthByte = Utils.int2Bytes(plainTextBytes.length); ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); byteStream.write(randomBytes); byteStream.write(lengthByte); byteStream.write(plainTextBytes); byte[] padBytes = PKCS7Padding.getPaddingBytes(byteStream.size()); byteStream.write(padBytes); byte[] unencrypted = byteStream.toByteArray(); byteStream.close(); Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); SecretKeySpec keySpec = new SecretKeySpec(AES_KEY, "AES"); IvParameterSpec iv = new IvParameterSpec(AES_KEY, 0, 16); cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); byte[] encrypted = cipher.doFinal(unencrypted); String result = BASE_64.encodeToString(encrypted); return result; } catch (Exception e) { throw new ThirdPartyEncryptException(ThirdPartyEncryptException.COMPUTE_ENCRYPT_TEXT_ERROR); } } /** * 暗号文を復号します。 * @param text 復号する暗号文。 * @return 復号後のプレーンテキスト。 */ private String decrypt(String text) throws ThirdPartyEncryptException { byte[] originalArr; try { // 復号モードを AES/CBC に設定します。 Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); SecretKeySpec keySpec = new SecretKeySpec(AES_KEY, "AES"); IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(AES_KEY, 0, 16)); cipher.init(Cipher.DECRYPT_MODE, keySpec, iv); // Base64 を使用して暗号文をデコードします。 byte[] encrypted = Base64.decodeBase64(text); // 復号します。 originalArr = cipher.doFinal(encrypted); } catch (Exception e) { throw new ThirdPartyEncryptException(ThirdPartyEncryptException.COMPUTE_DECRYPT_TEXT_ERROR); } String plainText; try { // パディングバイトを削除します。 byte[] bytes = PKCS7Padding.removePaddingBytes(originalArr); // 16 バイトのランダム文字列とネットワークバイトオーダーを抽出します。 byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20); int plainTextLegth = Utils.bytes2int(networkOrder); plainText = new String(Arrays.copyOfRange(bytes, 20, 20 + plainTextLegth), CHARSET); } catch (Exception e) { throw new ThirdPartyEncryptException(ThirdPartyEncryptException.COMPUTE_DECRYPT_TEXT_LENGTH_ERROR); } return plainText; } /** * 署名を計算します。 * * @param token ISV トークン。 * @param timestamp タイムスタンプ。 * @param nonce ランダム文字列。 * @param encrypt Base64 エンコードされた暗号文。 * @return * @throws ThirdPartyEncryptException */ public String getSignature(String token, String timestamp, String nonce, String encrypt) throws ThirdPartyEncryptException { try { String[] array = new String[] {token, timestamp, nonce, encrypt}; Arrays.sort(array); System.out.println(JSON.toJSONString(array)); StringBuffer sb = new StringBuffer(); for (int i = 0; i < 4; i++) { sb.append(array[i]); } String str = sb.toString(); System.out.println(str); MessageDigest md = MessageDigest.getInstance("SHA-1"); md.update(str.getBytes()); byte[] digest = md.digest(); StringBuffer hexstr = new StringBuffer(); String shaHex = ""; for (int i = 0; i < digest.length; i++) { shaHex = Integer.toHexString(digest[i] & 0xFF); if (shaHex.length() < 2) { hexstr.append(0); } hexstr.append(shaHex); } return hexstr.toString(); } catch (Exception e) { throw new ThirdPartyEncryptException(ThirdPartyEncryptException.COMPUTE_SIGNATURE_ERROR); } } public static class Utils { public Utils() { } public static String getRandomStr(int count) { String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; Random random = new Random(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < count; ++i) { int number = random.nextInt(base.length()); sb.append(base.charAt(number)); } return sb.toString(); } public static byte[] int2Bytes(int count) { byte[] byteArr = new byte[] {(byte)(count >> 24 & 255), (byte)(count >> 16 & 255), (byte)(count >> 8 & 255), (byte)(count & 255)}; return byteArr; } public static int bytes2int(byte[] byteArr) { int count = 0; for (int i = 0; i < 4; ++i) { count <<= 8; count |= byteArr[i] & 255; } return count; } } public static class PKCS7Padding { private static final Charset CHARSET = StandardCharsets.UTF_8; private static final int BLOCK_SIZE = 32; public PKCS7Padding() { } public static byte[] getPaddingBytes(int count) { int amountToPad = 32 - count % 32; if (amountToPad == 0) { amountToPad = 32; } char padChr = chr(amountToPad); String tmp = new String(); for (int index = 0; index < amountToPad; ++index) { tmp = tmp + padChr; } return tmp.getBytes(CHARSET); } public static byte[] removePaddingBytes(byte[] decrypted) { int pad = decrypted[decrypted.length - 1]; if (pad < 1 || pad > 32) { pad = 0; } return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); } private static char chr(int a) { byte target = (byte)(a & 255); return (char)target; } } public static class ThirdPartyEncryptException extends Exception { public static final int SUCCESS = 0; public static final int ENCRYPTION_PLAINTEXT_ILLEGAL = 900001; public static final int ENCRYPTION_TIMESTAMP_ILLEGAL = 900002; public static final int ENCRYPTION_NONCE_ILLEGAL = 900003; public static final int AES_KEY_ILLEGAL = 900004; public static final int SIGNATURE_NOT_MATCH = 900005; public static final int COMPUTE_SIGNATURE_ERROR = 900006; public static final int COMPUTE_ENCRYPT_TEXT_ERROR = 900007; public static final int COMPUTE_DECRYPT_TEXT_ERROR = 900008; public static final int COMPUTE_DECRYPT_TEXT_LENGTH_ERROR = 900009; public static final int COMPUTE_DECRYPT_TEXT_CORPID_ERROR = 900010; private static Map<Integer, String> msgMap = new HashMap(); private Integer code; static { msgMap.put(0, "Success"); msgMap.put(900001, "Invalid plaintext for encryption"); msgMap.put(900002, "Invalid timestamp parameter for encryption"); msgMap.put(900003, "Invalid nonce parameter for encryption"); msgMap.put(900005, "Signature mismatch"); msgMap.put(900006, "Failed to compute signature"); msgMap.put(900004, "Invalid AES key"); msgMap.put(900007, "Failed to compute ciphertext"); msgMap.put(900008, "Failed to decrypt text"); msgMap.put(900009, "Plaintext length mismatch"); msgMap.put(900010, "CorpId mismatch in decrypted plaintext"); } public Integer getCode() { return this.code; } public ThirdPartyEncryptException(Integer exceptionCode) { super((String)msgMap.get(exceptionCode)); this.code = exceptionCode; } } static { try { Security.setProperty("crypto.policy", "limited"); RemoveCryptographyRestrictions(); } catch (Exception var1) { } } private static void RemoveCryptographyRestrictions() throws Exception { Class<?> jceSecurity = getClazz("javax.crypto.JceSecurity"); Class<?> cryptoPermissions = getClazz("javax.crypto.CryptoPermissions"); Class<?> cryptoAllPermission = getClazz("javax.crypto.CryptoAllPermission"); if (jceSecurity != null) { setFinalStaticValue(jceSecurity, "isRestricted", false); PermissionCollection defaultPolicy = (PermissionCollection)getFieldValue(jceSecurity, "defaultPolicy", (Object)null, PermissionCollection.class); if (cryptoPermissions != null) { Map<?, ?> map = (Map)getFieldValue(cryptoPermissions, "perms", defaultPolicy, Map.class); map.clear(); } if (cryptoAllPermission != null) { Permission permission = (Permission)getFieldValue(cryptoAllPermission, "INSTANCE", (Object)null, Permission.class); defaultPolicy.add(permission); } } } private static Class<?> getClazz(String className) { Class clazz = null; try { clazz = Class.forName(className); } catch (Exception var3) { } return clazz; } private static void setFinalStaticValue(Class<?> srcClazz, String fieldName, Object newValue) throws Exception { Field field = srcClazz.getDeclaredField(fieldName); field.setAccessible(true); Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(field, field.getModifiers() & -17); field.set((Object)null, newValue); } private static <T> T getFieldValue(Class<?> srcClazz, String fieldName, Object owner, Class<T> dstClazz) throws Exception { Field field = srcClazz.getDeclaredField(fieldName); field.setAccessible(true); return dstClazz.cast(field.get(owner)); } }