我根据 Signal 的文档使用 libsignal-protocol-java 在消息应用程序中实现端到端加密。
假设爱丽丝向鲍勃发送一条(成对的)序列化密文消息。 Bob 如何知道如何反序列化它?他不是首先需要知道密文消息的类型吗?我的理解是,成对密文消息可以是信号消息(
WHISPER_TYPE
)或预密钥信号消息(PREKEY_TYPE
)。那么 Bob 如何知道它是什么类型呢?
Alice 是否也应该向 Bob 发送密文消息的类型(作为明文)?
或者 Bob 是否有其他方法可以检测类型?例如,Bob 是否应该尝试将其反序列化为信号消息,如果失败,则尝试将其反序列化为预关键信号消息?
在查看了Signal Android的源代码后,我认为Alice也应该向Bob发送密文消息的类型(作为明文)。
SignalServiceCipher
:
encrypt(SignalProtocolAddress, Optional<UnidentifiedAccess>, byte[])
返回OutgoingPushMessage
的实例,其type
属性设置为密文消息的属性,其body
属性设置为Base64编码的序列化密文消息。
decrypt(SignalServiceEnvelope, byte[])
从其第一个参数中获取密文消息的类型,该参数是SignalServiceEnvelope的实例。
1 - Alice 生成identityKeyPair(长期)、signedPreKey(中期)和临时PreKeys 并将这些密钥保存到base64 中的存储中。 例如
public static String generateIdentityKeyPair() {
IdentityKeyPair identityKeyPair = KeyHelper.generateIdentityKeyPair();
return encodeToBase64(identityKeyPair.serialize());
}
2 - 发送
对于加密和解密,您首先必须进行加密会话
private void initSessionFromPreKey() throws UntrustedIdentityException, InvalidKeyException {
InMemorySignalProtocolStore protocolStore = new InMemorySignalProtocolStore(localUser.getIdentityKeyPair(), localUser.getRegistrationId());
protocolStore.storePreKey(localUser.getPreKeys().get(0).getId(), localUser.getPreKeys().get(0));
protocolStore.storeSignedPreKey(localUser.getSignedPreKey().getId(), localUser.getSignedPreKey());
this.protocolStore = protocolStore;
//Session
SessionBuilder sessionBuilder = new SessionBuilder(protocolStore, remoteUser.getSignalProtocolAddress());
PreKeyBundle preKeyBundle = new PreKeyBundle(
remoteUser.getRegistrationId(),
remoteUser.getSignalProtocolAddress().getDeviceId(),
remoteUser.getPreKeyId(),
remoteUser.getPreKeyPublicKey(),
remoteUser.getSignedPreKeyId(),
remoteUser.getSignedPreKeyPublicKey(),
remoteUser.getSignedPreKeySignature(),
remoteUser.getIdentityKeyPairPublicKey()
);
sessionBuilder.process(preKeyBundle);
mSessionCipher = new SessionCipher(protocolStore, protocolAddress);
}
加密与解密
public String encrypt(String message) throws InvalidVersionException, InvalidMessageException, UntrustedIdentityException, InvalidKeyException {
createSession(Operation.ENCRYPT);
CiphertextMessage ciphertextMessage = mSessionCipher.encrypt(message.getBytes());
PreKeySignalMessage preKeySignalMessage = new PreKeySignalMessage(ciphertextMessage.serialize());
return KeyUtils.encodeToBase64(preKeySignalMessage.serialize());
}
public String decrypt(String message) throws InvalidVersionException, InvalidMessageException, InvalidKeyException, DuplicateMessageException, InvalidKeyIdException, UntrustedIdentityException, LegacyMessageException {
createSession(Operation.DECRYPT);
byte[] bytes = KeyUtils.decodeToByteArray(message);
byte[] decryptedMessage = mSessionCipher.decrypt(new PreKeySignalMessage(bytes));
return new String(decryptedMessage, StandardCharsets.UTF_8);
}
您还可以查看 github 上提供的其他源代码 https://github.com/lvijay/DemoSignal
https://github.com/signalapp/libsignal-protocol-java/pull/21/commits/3496ed996359f6d3d8ee52dcecb8f8b0d45b3cbc(库作者使用的是信号协议的包装器,您可以将包装器库更改为信号协议)
你可以在我的 github 上找到工作示例 https://github.com/eatakishiyev/signal-protocol-example