xml signing with google-cloud-kms in java/kotlin

95 Views Asked by At

I don't understand how I would use the built in xml signing library in java with google kms. I have my pub/private key in google kms.

I do the following without google kms:

val keyPair = getKeyPair() //get the keypair from my local file system
val dsc = DOMSignContext(keyPair.private, w3cDoc.documentElement.getElementsByTagName("MY_ELEMENT_TO_INSERT").first())
val xmlSignatureFactory = XMLSignatureFactory.getInstance("DOM")
val xmlSignature = xmlSignatureFactory.newXMLSignature(signedInfo, keyInfo)
xmlSignature.sign(dsc)

If you notice I have to give DOMSignContext a private key. It also takes a keyselector, but we run into the same problem.

My problem is that google-cloud-kms does not allow to get the private key, seems like I can only encrypt using keyManagementServiceClient.asymmetricSign which allows me to pass data I want to encrypt to google kms.

Am I not understanding something or is it impossible to use google kms with the xml signing library?

1

There are 1 best solutions below

0
VonC On

I suspect you cannot directly use the private key from Google Cloud KMS as you would with a local key pair to integrate it with the Java XML signing library: Google Cloud KMS is designed to keep the private key... private, by not exposing it directly.
Instead, you might have you to perform the cryptographic operations through the KMS API, where the signing operation happens in the cloud, and only the signature is returned.

That would mean adjusting your signing process to use Google Cloud KMS's asymmetricSign method for the signing step.

Local System                           Google Cloud KMS
     |                                      |
     |---(1) Prepare data to sign --------->|
     |                                      |
     |---(2) Request signature ------------>|
     |                                      |
     |<--(3) Receive signature--------------|
     |                                      |

To implement this with your XML signing, you would:

  • generate the digest of the data you intend to sign.
  • use the asymmetricSign method of Google Cloud KMS to sign the digest.
  • insert the received signature back into your XML document.
import com.google.cloud.kms.v1.KeyManagementServiceClient
import com.google.cloud.kms.v1.AsymmetricSignRequest
import com.google.cloud.kms.v1.Digest
import com.google.protobuf.ByteString
import java.security.MessageDigest
// Import other necessary libraries

fun signXmlWithKms(w3cDoc: Document) {
    // Assume MY_ELEMENT_TO_INSERT contains the data you want to sign
    val dataToSign = // Extract the data from w3cDoc that needs to be signed
    
    // Convert data to sign to a digest
    val md = MessageDigest.getInstance("SHA-256")
    val digestBytes = md.digest(dataToSign.toByteArray(Charsets.UTF_8))
    
    // Prepare request for Google Cloud KMS
    val digest = Digest.newBuilder().setSha256(ByteString.copyFrom(digestBytes)).build()
    val asymmetricSignRequest = AsymmetricSignRequest.newBuilder()
        .setName("projects/[PROJECT_ID]/locations/[LOCATION]/keyRings/[KEY_RING]/cryptoKeys/[KEY]/cryptoKeyVersions/[VERSION]")
        .setDigest(digest)
        .build()

    // Use KeyManagementServiceClient to sign the digest
    KeyManagementServiceClient.create().use { client ->
        val response = client.asymmetricSign(asymmetricSignRequest)
        val signature = response.signature.toByteArray()
        
        // Insert the signature back into your XML in the appropriate place
        // That part depends on your XML structure and how you intend to include the signature
    }
}

But: that does modify the flow of data signing to involve a remote call to Google Cloud KMS: depending on your use case, the performance might be affected.