You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Java/Kotlin中结合Google Cloud KMS实现XML签名?

结合Google Cloud KMS与Java XML签名库的实现方案

你没有理解误区,确实无法直接用本地私钥的方式对接,但可以通过扩展XML签名库的签名机制,让签名操作路由到Google Cloud KMS的asymmetricSign接口,具体实现步骤如下:

核心思路

Java的XML签名API(JSR 105)支持自定义签名实现,我们可以通过实现javax.xml.crypto.dsig.Signature接口,将原本本地私钥签名的逻辑替换为调用KMS的远程签名接口。

步骤1:自定义KMS签名实现

创建一个实现Signature接口的类,在sign方法中调用Google Cloud KMS的asymmetricSign完成签名:

import com.google.cloud.kms.v1.KeyManagementServiceClient
import com.google.cloud.kms.v1.AsymmetricSignRequest
import com.google.protobuf.ByteString
import javax.xml.crypto.dsig.Signature
import javax.xml.crypto.dsig.SignatureException
import javax.xml.crypto.dsig.XMLSignContext
import java.security.spec.AlgorithmParameterSpec

class KmsSignature(private val kmsKeyName: String) : Signature {
    private lateinit var signedInfoBytes: ByteArray
    private val kmsClient = KeyManagementServiceClient.create()

    override fun initSign(signContext: XMLSignContext) {
        // 无需本地私钥,签名逻辑在sign阶段调用KMS
    }

    override fun update(bytes: ByteArray) {
        signedInfoBytes = bytes
    }

    override fun update(b: Byte) {
        throw UnsupportedOperationException("暂不支持单字节更新")
    }

    override fun update(bytes: ByteArray, off: Int, len: Int) {
        signedInfoBytes = bytes.copyOfRange(off, off + len)
    }

    override fun sign(): ByteArray {
        val request = AsymmetricSignRequest.newBuilder()
            .setName(kmsKeyName)
            .setDigest(ByteString.copyFrom(signedInfoBytes))
            .build()
        val response = kmsClient.asymmetricSign(request)
        return response.signature.toByteArray()
    }

    // 接口默认实现,按需调整
    override fun getAlgorithm(): String = "SHA256withRSA" // 需与KMS密钥算法一致
    override fun initVerify(signContext: XMLSignContext) {}
    override fun verify(signature: ByteArray): Boolean = false
    override fun setParameter(params: AlgorithmParameterSpec) {}
    override fun getParameter(params: String): Any? = null
}

步骤2:注册自定义签名到XMLSignatureFactory

将自定义签名实现注册到XML签名工厂,让工厂能识别并使用它:

import javax.xml.crypto.dsig.XMLSignatureFactory
import javax.xml.crypto.dsig.SignatureMethod

XMLSignatureFactory.getInstance("DOM").run {
    registerProvider(object : javax.xml.crypto.dsig.SignatureProvider("KMS-SHA256-RSA", arrayOf("SHA256withRSA")) {
        override fun engineGetInstance(algorithm: String): javax.xml.crypto.dsig.Signature {
            // 替换为你的KMS密钥完整路径
            return KmsSignature("projects/[项目ID]/locations/[区域]/keyRings/[密钥环]/cryptoKeys/[密钥]/versions/[版本]")
        }
    })
}

步骤3:修改原签名流程

无需传入本地私钥,直接使用DOMSignContext(可传入占位公钥或null,自定义签名会忽略),XML签名库会自动调用我们的KMS签名逻辑:

import javax.xml.crypto.dsig.*
import javax.xml.crypto.dsig.dom.DOMSignContext

val dsc = DOMSignContext(/* 传入占位公钥或null即可 */, w3cDoc.documentElement.getElementsByTagName("MY_ELEMENT_TO_INSERT").first())
val xmlSignatureFactory = XMLSignatureFactory.getInstance("DOM")

// 构建SignedInfo,指定与KMS匹配的签名算法
val signedInfo = xmlSignatureFactory.newSignedInfo(
    xmlSignatureFactory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, null),
    xmlSignatureFactory.newSignatureMethod("SHA256withRSA", null),
    listOf(xmlSignatureFactory.newReference(
        "#object-id",
        xmlSignatureFactory.newDigestMethod(DigestMethod.SHA256, null)
    ))
)

// 从KMS获取公钥构建KeyInfo,供验签方使用
val publicKey = keyManagementServiceClient.getPublicKey(kmsKeyName).publicKey
val keyInfo = xmlSignatureFactory.newKeyInfo(listOf(xmlSignatureFactory.newKeyValue(publicKey)))

val xmlSignature = xmlSignatureFactory.newXMLSignature(signedInfo, keyInfo)
xmlSignature.sign(dsc)

补充说明

  • 确保服务账号拥有KMS密钥的cloudkms.asymmetricSign权限。
  • 自定义签名类的算法必须与KMS密钥的算法完全匹配(如RSA-SHA256、EC-SHA256等)。
  • 验签时可直接使用从KMS获取的公钥,无需依赖KMS服务。

内容的提问来源于stack exchange,提问作者bink1time

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 05:52:51