google / capillary

Capillary is a library to simplify the sending of end-to-end encrypted push messages from Java-based application servers to Android clients.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Is there anybody out there that have used capillary on server and decrypted messages on iOS?

einarnot opened this issue · comments

We want to use capillary to support E2E encryption for push messages, on both android and iOS, on iOS we need to generate an RSA keypair, and provide the public key to the server. The problem is that we don't know what format Capillary expects the public key in.

We have tried to find out, and think that its an RSA 2048 in X.509 format and base64encoded. But our early attempts at this fails when Capillary tries to parse the public-key generated by our iOS app. We have used Heimdall to generate the keypair, and experimented with CryptoExportImportManager to convert the keys into DER format, but still no luck...

Anybody who can help, or at least say if this is feasible. (that is, using Capillary on server and E2E encrypt for iOS Clients.)

(Please excuse me if any of this doesn't make sense, as crypto is something Im not very familiar with. Im trying to learn, but all these key-types, formats, sizes, curves and encodings is still a jungle for me.)

commented

@einarnot it's almost one year since you asked. Did you solved it somehow? Have similar use case.

Capillary expects the RSA key wrapped in Protobuf objects. So we had to use SwiftProtobuf (Swift version of Googles Protobuf).

-Use SwiftProtobuf to generate Swift file from the capillary_internal.proto.

  1. Using AsymmetricCryptoException to create a secure keypair.

  2. Use exportRSAPublicKeyToDER in CryptoExportImportManager to export RSA key to DER format.

  3. Wrap the result of exportRSAPublicKeyToDER in the structs from capillary_internal.pb.swift (see swift function below).

  4. Provide the capillaryPublicKeyString to your backend that run capillary, it will now be able to encrypt using it.

  5. When you receive the push, handle it using Notification Service Extension (look for tutorials on how to do this. One major thing is that the push message must have mutableContent set to true.)

  6. To decrypt the encrypted string inside Notification Service Extension you need to put the encrypted string in an Capillary_Internal_CapillaryCiphertext object, split out the signature and payload data from that object.ciphertext. Length of the signature is then found in the first 4 bytes of the ciphertext. Then you need to put the payload in Capillary_Internal_HybridRsaCiphertext object, and symmetricKeyCiphertext is now the Data object to use for further decryption.

  7. We used TINK to decrypt the symmetricKeyCiphertext.

Function to wrap keydata in capillary protobuf structs (step 3):

func capillaryPublicKeyProtobufString(keyData: Data?) -> String? {
		guard let keyData = keyData else {
			return nil
		}

		var capillaryPublicKeyString = ""

		var wrappedRsaEcdsaPublicKey = Capillary_Internal_WrappedRsaEcdsaPublicKey.init()
		wrappedRsaEcdsaPublicKey.keyBytes = keyData
		wrappedRsaEcdsaPublicKey.padding = kPadding

		var capillaryPublicKey = Capillary_Internal_CapillaryPublicKey.init()
		do {
			capillaryPublicKey.keyBytes = try wrappedRsaEcdsaPublicKey.serializedData()
			capillaryPublicKeyString = try capillaryPublicKey.serializedData().base64EncodedString()
		} catch {
			Debugger.print("Failed to generate capillaryPublic Key: \(error.localizedDescription)")
		}

		return capillaryPublicKeyString
	}

I know this might be confusing, and there is alot of steps. But we made it work this way, so its possible, and this might help you figure things out.