Wip: Signing mechanisms and XML Canonicalization

This commit is contained in:
Paul Schaub 2020-08-28 14:33:53 +02:00
parent 68657367cb
commit d9c9eff602
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
22 changed files with 563 additions and 81 deletions

View File

@ -4,8 +4,10 @@ import org.mercury_im.messenger.cli.MercuryCli;
import org.mercury_im.messenger.cli.di.module.CliDatabaseModule;
import org.mercury_im.messenger.cli.di.module.CliSchedulersModule;
import org.mercury_im.messenger.cli.di.module.X509WorkaroundConnectionFactoryModule;
import org.mercury_im.messenger.core.di.module.OpenPgpModule;
import org.mercury_im.messenger.core.di.module.RxMercuryMessageStoreFactoryModule;
import org.mercury_im.messenger.core.di.module.RxMercuryRosterStoreFactoryModule;
import org.mercury_im.messenger.core.di.module.StanzaIdSourceFactoryModule;
import org.mercury_im.messenger.core.di.module.ViewModelModule;
import org.mercury_im.messenger.data.di.RepositoryModule;
@ -22,7 +24,9 @@ import dagger.Component;
ViewModelModule.class,
X509WorkaroundConnectionFactoryModule.class,
RxMercuryMessageStoreFactoryModule.class,
RxMercuryRosterStoreFactoryModule.class
RxMercuryRosterStoreFactoryModule.class,
OpenPgpModule.class,
StanzaIdSourceFactoryModule.class
})
public interface CliComponent {

View File

@ -29,6 +29,8 @@ dependencies {
annotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
testAnnotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
api "org.apache.santuario:xmlsec:2.2.0"
compileOnly "org.projectlombok:lombok:$lombokVersion"
annotationProcessor "org.projectlombok:lombok:$lombokVersion"

View File

@ -0,0 +1,21 @@
package org.jivesoftware.smackx.ikey;
import org.apache.xml.security.c14n.CanonicalizationException;
import org.apache.xml.security.parser.XMLParserException;
import org.jivesoftware.smack.packet.Element;
import java.io.IOException;
public interface ElementCanonicalizer {
default byte[] canonicalize(Element e) throws XMLParserException, IOException, CanonicalizationException {
String xml = e.toXML().toString();
return canonicalize(xml);
}
default byte[] canonicalize(CharSequence xml) throws XMLParserException, IOException, CanonicalizationException {
return canonicalize(xml.toString().getBytes());
}
byte[] canonicalize(byte[] xml) throws XMLParserException, IOException, CanonicalizationException;
}

View File

@ -0,0 +1,10 @@
package org.jivesoftware.smackx.ikey;
import org.jxmpp.jid.EntityBareJid;
public class IkeyManager {
private IkeySignatureVerificationMechanism getSignatureMechanismFor(EntityBareJid contactJid) {
return null;
}
}

View File

@ -0,0 +1,11 @@
package org.jivesoftware.smackx.ikey;
import java.io.IOException;
public interface IkeySignatureCreationMechanism {
byte[] createSignature(byte[] data) throws IOException;
IkeyType getType();
}

View File

@ -0,0 +1,27 @@
package org.jivesoftware.smackx.ikey;
import org.apache.xml.security.c14n.CanonicalizationException;
import org.apache.xml.security.parser.XMLParserException;
import org.bouncycastle.util.encoders.Base64;
import org.jivesoftware.smackx.ikey.element.ProofElement;
import org.jivesoftware.smackx.ikey.element.SubordinateListElement;
import java.io.IOException;
public class IkeySignatureCreator {
private final IkeySignatureCreationMechanism signatureCreationMechanism;
private final XmlSecElementCanonicalizer elementCanonicalizer;
public IkeySignatureCreator(IkeySignatureCreationMechanism signingMechanism, XmlSecElementCanonicalizer elementCanonicalizer) {
this.signatureCreationMechanism = signingMechanism;
this.elementCanonicalizer = elementCanonicalizer;
}
public ProofElement createProofFor(SubordinateListElement subordinateListElement) throws XMLParserException, IOException, CanonicalizationException {
byte[] canonicalized = elementCanonicalizer.canonicalize(subordinateListElement);
byte[] signature = signatureCreationMechanism.createSignature(canonicalized);
return new ProofElement(Base64.toBase64String(signature));
}
}

View File

@ -0,0 +1,10 @@
package org.jivesoftware.smackx.ikey;
import java.io.IOException;
public interface IkeySignatureVerificationMechanism {
boolean isSignatureValid(byte[] data, byte[] signature) throws IOException;
IkeyType getType();
}

View File

@ -0,0 +1,44 @@
package org.jivesoftware.smackx.ikey;
import org.apache.xml.security.c14n.CanonicalizationException;
import org.apache.xml.security.parser.XMLParserException;
import org.bouncycastle.util.encoders.Base64;
import org.jivesoftware.smackx.ikey.element.IkeyElement;
import org.jxmpp.jid.EntityBareJid;
import java.io.IOException;
public class IkeySignatureVerifier {
private final IkeySignatureVerificationMechanism signatureVerificationMechanism;
private final XmlSecElementCanonicalizer elementCanonicalizer;
public IkeySignatureVerifier(IkeySignatureVerificationMechanism signatureVerificationMechanism, XmlSecElementCanonicalizer elementCanonicalizer) {
this.signatureVerificationMechanism = signatureVerificationMechanism;
this.elementCanonicalizer = elementCanonicalizer;
}
public boolean verify(IkeyElement element, EntityBareJid owner)
throws XMLParserException, IOException, CanonicalizationException {
throwIfMismatchingMechanism(element);
throwIfMismatchingOwnerJid(element, owner);
byte[] canonicalizedXml = elementCanonicalizer.canonicalize(element.getSubordinates());
byte[] signature = Base64.decode(element.getProof().getBase64Signature());
return signatureVerificationMechanism.isSignatureValid(canonicalizedXml, signature);
}
private void throwIfMismatchingOwnerJid(IkeyElement element, EntityBareJid owner) {
if (!element.getSubordinates().getJid().equals(owner)) {
throw new IllegalArgumentException("Provided ikey element does not contain jid of " + owner);
}
}
private void throwIfMismatchingMechanism(IkeyElement element) {
if (element.getType() != signatureVerificationMechanism.getType()) {
throw new IllegalArgumentException("Element was created using mechanism " + element.getType() +
" but this is a verifier for " + signatureVerificationMechanism.getType() + " ikey elements.");
}
}
}

View File

@ -0,0 +1,27 @@
package org.jivesoftware.smackx.ikey;
import org.apache.xml.security.c14n.CanonicalizationException;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.parser.XMLParserException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.inject.Inject;
public class XmlSecElementCanonicalizer implements ElementCanonicalizer {
private final Canonicalizer canonicalizer;
@Inject
public XmlSecElementCanonicalizer(Canonicalizer canonicalizer) {
this.canonicalizer = canonicalizer;
}
@Override
public byte[] canonicalize(byte[] xml) throws XMLParserException, IOException, CanonicalizationException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
canonicalizer.canonicalize(xml, out, true);
return out.toByteArray();
}
}

View File

@ -0,0 +1,60 @@
package org.jivesoftware.smackx.ikey.element;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.ikey.IkeyConstants;
import org.jivesoftware.smackx.ikey.IkeyType;
import javax.xml.namespace.QName;
public class IkeyElement implements ExtensionElement {
public static final String NAMESPACE = IkeyConstants.NAMESPACE;
public static final String ELEMENT = "ikey";
public static final String ATTR_IKEY_TYPE = "type";
private static final QName QNAME = new QName(IkeyConstants.NAMESPACE, ELEMENT);
private final IkeyType type;
private final SubordinateListElement subordinates;
private final ProofElement proof;
public IkeyElement(IkeyType type, SubordinateListElement subordinates, ProofElement proof) {
this.type = type;
this.subordinates = subordinates;
this.proof = proof;
}
public IkeyType getType() {
return type;
}
public SubordinateListElement getSubordinates() {
return subordinates;
}
public ProofElement getProof() {
return proof;
}
@Override
public String getNamespace() {
return NAMESPACE;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
return new XmlStringBuilder(this, xmlEnvironment)
.attribute(ATTR_IKEY_TYPE, getType())
.rightAngleBracket()
.append(getSubordinates())
.append(getProof())
.closeElement(this);
}
}

View File

@ -1,40 +0,0 @@
package org.jivesoftware.smackx.ikey.element;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.ikey.IkeyConstants;
import org.jivesoftware.smackx.ikey.IkeyType;
import javax.xml.namespace.QName;
public class PlainIKeyElement implements ExtensionElement {
public static final String NAMESPACE = IkeyConstants.NAMESPACE;
public static final String ELEMENT = "ikey";
public static final String ATTR_IKEY_TYPE = "type";
private final IkeyType type;
private final QName QNAME = new QName(IkeyConstants.NAMESPACE, ELEMENT);
public PlainIKeyElement() {
this.type = IkeyType.OX;
}
@Override
public String getNamespace() {
return NAMESPACE;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
return xml;
}
}

View File

@ -0,0 +1,32 @@
package org.jivesoftware.smackx.ikey.element;
import org.jivesoftware.smack.packet.NamedElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.XmlStringBuilder;
public class ProofElement implements NamedElement {
public static final String ELEMENT = "proof";
private final String base64Sig;
public ProofElement(String base64Sig) {
this.base64Sig = base64Sig;
}
public String getBase64Signature() {
return base64Sig;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
return new XmlStringBuilder(this).rightAngleBracket()
.append(getBase64Signature())
.closeElement(this);
}
}

View File

@ -2,24 +2,27 @@ package org.jivesoftware.smackx.ikey.element;
import org.jivesoftware.smack.packet.NamedElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder;
import java.net.URI;
public class SubordinateElement implements NamedElement {
public static final String ELEMENT = "sub";
public static final String ATTR_SUB_URI = "uri";
public static final String ATTR_SUB_FINGERPRINT = "fingerprint";
public static final String ATTR_SUB_FINGERPRINT = "fpr";
private final String subUri;
private final URI subUri;
private final String subFingerprint;
public SubordinateElement(String subKeyItemUri, String subKeyFingerprint) {
this.subUri = StringUtils.requireNotNullNorEmpty(subKeyItemUri, "uri MUST NOT be null nor empty.");
this.subFingerprint = StringUtils.requireNotNullNorEmpty(subKeyFingerprint, "fingerprint MUST NOT be null nor empty.");
public SubordinateElement(URI subKeyItemUri, String subKeyFingerprint) {
this.subUri = Objects.requireNonNull(subKeyItemUri, "uri MUST NOT be null nor empty.");
this.subFingerprint = StringUtils.requireNotNullNorEmpty(subKeyFingerprint, "fpr MUST NOT be null nor empty.");
}
public String getUri() {
public URI getUri() {
return subUri;
}
@ -35,7 +38,7 @@ public class SubordinateElement implements NamedElement {
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
XmlStringBuilder xml = new XmlStringBuilder(this)
.attribute(ATTR_SUB_URI, getUri())
.attribute(ATTR_SUB_URI, getUri().toString())
.attribute(ATTR_SUB_FINGERPRINT, getFingerprint())
.closeEmptyElement();
return xml;

View File

@ -1,27 +1,34 @@
package org.jivesoftware.smackx.ikey.element;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.NamedElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.ikey.IkeyConstants;
import org.jxmpp.jid.EntityBareJid;
import java.util.List;
public class SubordinateListElement implements ExtensionElement {
public class SubordinateListElement implements NamedElement {
public static final String NAMESPACE = IkeyConstants.NAMESPACE;
public static final String ELEMENT = "subordinates";
public static final String ATTR_JID = "jid";
private final List<SubordinateElement> subordinates;
private final EntityBareJid jid;
public SubordinateListElement(List<SubordinateElement> subordinates) {
public SubordinateListElement(EntityBareJid jid, List<SubordinateElement> subordinates) {
this.jid = jid;
this.subordinates = Objects.requireNonNullNorEmpty(subordinates, "List of subordinates MUST NOT be null nor empty.");
}
@Override
public String getNamespace() {
return NAMESPACE;
public EntityBareJid getJid() {
return jid;
}
public List<SubordinateElement> getSubordinates() {
return subordinates;
}
@Override
@ -31,10 +38,10 @@ public class SubordinateListElement implements ExtensionElement {
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment)
return new XmlStringBuilder(this)
.attribute(ATTR_JID, getJid())
.rightAngleBracket()
.append(subordinates)
.append(getSubordinates())
.closeElement(this);
return xml;
}
}

View File

@ -0,0 +1,57 @@
package org.jivesoftware.smackx.ikey_ox;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.util.io.Streams;
import org.jivesoftware.smackx.ikey.IkeySignatureCreationMechanism;
import org.jivesoftware.smackx.ikey.IkeyType;
import org.pgpainless.PGPainless;
import org.pgpainless.encryption_signing.EncryptionStream;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class OxIkeySignatureCreationMechanism implements IkeySignatureCreationMechanism {
private final PGPSecretKeyRing ourIdentityKey;
private final SecretKeyRingProtector secretKeyRingProtector;
public OxIkeySignatureCreationMechanism(PGPSecretKeyRing ourIdentityKey,
SecretKeyRingProtector secretKeyRingProtector) {
this.ourIdentityKey = ourIdentityKey;
this.secretKeyRingProtector = secretKeyRingProtector;
}
@Override
public byte[] createSignature(byte[] data) throws IOException {
try {
ByteArrayOutputStream dummyOutputStream = new ByteArrayOutputStream();
EncryptionStream encryptionStream = PGPainless.createEncryptor()
.onOutputStream(dummyOutputStream)
.doNotEncrypt()
.createDetachedSignature()
.signWith(secretKeyRingProtector, ourIdentityKey)
.noArmor();
ByteArrayInputStream dataIn = new ByteArrayInputStream(data);
Streams.pipeAll(dataIn, encryptionStream);
encryptionStream.close();
dataIn.close();
PGPSignature signature = encryptionStream.getResult().getSignatures()
.iterator().next();
return signature.getEncoded();
} catch (PGPException e) {
throw new IOException(e);
}
}
@Override
public IkeyType getType() {
return IkeyType.OX;
}
}

View File

@ -0,0 +1,53 @@
package org.jivesoftware.smackx.ikey_ox;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.util.io.Streams;
import org.jivesoftware.smackx.ikey.IkeySignatureVerificationMechanism;
import org.jivesoftware.smackx.ikey.IkeyType;
import org.pgpainless.PGPainless;
import org.pgpainless.decryption_verification.DecryptionStream;
import org.pgpainless.decryption_verification.OpenPgpMetadata;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collections;
public class OxIkeySignatureVerificationMechanism implements IkeySignatureVerificationMechanism {
private PGPPublicKeyRing contactIdentityKey;
public OxIkeySignatureVerificationMechanism(PGPPublicKeyRing contactIdentityKey) {
this.contactIdentityKey = contactIdentityKey;
}
@Override
public boolean isSignatureValid(byte[] data, byte[] signature) throws IOException {
try {
DecryptionStream decryptionInputStream = PGPainless.createDecryptor()
.onInputStream(new ByteArrayInputStream(data))
.doNotDecrypt()
.verifyDetachedSignature(new ByteArrayInputStream(signature))
.verifyWith(Collections.singleton(contactIdentityKey))
.ignoreMissingPublicKeys()
.build();
ByteArrayOutputStream decryptionOutputStream = new ByteArrayOutputStream(data.length);
Streams.pipeAll(decryptionInputStream, decryptionOutputStream);
decryptionInputStream.close();
decryptionOutputStream.close();
OpenPgpMetadata metadata = decryptionInputStream.getResult();
return !metadata.getVerifiedSignatures().isEmpty();
} catch (PGPException e) {
throw new IOException(e);
}
}
@Override
public IkeyType getType() {
return IkeyType.OX;
}
}

View File

@ -32,6 +32,15 @@ public class PubSubUri {
this.action = action;
}
public PubSubUri(URI uri) throws XmppStringprepException {
throwIfNoXmppUri(uri);
String ssp = uri.getSchemeSpecificPart();
service = getService(ssp);
node = getNodeOrNull(ssp);
item = getItemOrNull(ssp);
action = getActionOrNull(ssp);
}
/**
* Parse a {@link PubSubUri}.
* This method ignores unknown parameters.
@ -43,14 +52,7 @@ public class PubSubUri {
public static PubSubUri from(String s)
throws XmppStringprepException {
URI uri = URI.create(s);
throwIfNoXmppUri(uri);
String ssp = uri.getSchemeSpecificPart();
BareJid service = getService(ssp);
String node = getNodeOrNull(ssp);
String item = getItemOrNull(ssp);
Action action = getActionOrNull(ssp);
return new PubSubUri(service, node, item, action);
return new PubSubUri(uri);
}
/**

View File

@ -0,0 +1,36 @@
package org.jivesoftware.smackx.ikey.element;
import org.jivesoftware.smackx.ikey.IkeyType;
import org.junit.Test;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.impl.JidCreate;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
public class IkeyElementTest {
@Test
public void elementTest() throws URISyntaxException {
IkeyType type = IkeyType.OX;
SubordinateListElement subordinates = buildSubListElement(
JidCreate.entityBareFromOrThrowUnchecked("hamlet@denmark.lit"),
new SubordinateElement(
new URI("xmpp:hamlet@denmark.lit?;node=urn:xmpp:openpgp:0:public-keys:1357B01865B2503C18453D208CAC2A9678548E35;item=2020-01-21T10:46:21Z"),
"1357B01865B2503C18453D208CAC2A9678548E35"),
new SubordinateElement(
new URI("xmpp:hamlet@denmark.lit?;node=urn:xmpp:omemo:1:bundles;item=123456"),
"e64dc9166dd34db64c9247bd502c5969e365a98f3aa41c87247d120487fdd32f")
);
ProofElement proof = new ProofElement("d2hpbGUgdGhpcyBpcyBub3QgYSB2YWxpZCBwcm9vZiwgaXQgaXMgc3VmZmljaWVudCBmb3IgdGVzdGluZy4=");
IkeyElement ikeyElement = new IkeyElement(type, subordinates, proof);
System.out.println(ikeyElement.toXML().toString());
}
private SubordinateListElement buildSubListElement(EntityBareJid jid, SubordinateElement... subordinateElements) {
return new SubordinateListElement(jid, Arrays.asList(subordinateElements));
}
}

View File

@ -0,0 +1,71 @@
package org.jivesoftware.smackx.ikey.element;
import org.apache.xml.security.Init;
import org.apache.xml.security.c14n.CanonicalizationException;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.c14n.InvalidCanonicalizerException;
import org.apache.xml.security.parser.XMLParserException;
import org.bouncycastle.openpgp.PGPException;
import org.jivesoftware.smackx.ikey.XmlSecElementCanonicalizer;
import org.jivesoftware.smackx.ikey.IkeySignatureCreationMechanism;
import org.jivesoftware.smackx.ikey.IkeySignatureCreator;
import org.jivesoftware.smackx.ikey.IkeySignatureVerificationMechanism;
import org.jivesoftware.smackx.ikey.IkeySignatureVerifier;
import org.jivesoftware.smackx.ikey.IkeyType;
import org.jivesoftware.smackx.ikey_ox.OxIkeySignatureCreationMechanism;
import org.jivesoftware.smackx.ikey_ox.OxIkeySignatureVerificationMechanism;
import org.junit.BeforeClass;
import org.junit.Test;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.impl.JidCreate;
import org.pgpainless.PGPainless;
import org.pgpainless.key.OpenPgpV4Fingerprint;
import org.pgpainless.key.collection.PGPKeyRing;
import org.pgpainless.key.protection.UnprotectedKeysProtector;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import static junit.framework.TestCase.assertTrue;
public class IkeySignatureCreatorAndVerifierTest {
@BeforeClass
public static void initialize() {
if (!Init.isInitialized()) {
Init.init();
}
}
@Test
public void createIkeyElementAndVerifySignature() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, URISyntaxException, XMLParserException, IOException, CanonicalizationException, InvalidCanonicalizerException {
EntityBareJid jid = JidCreate.entityBareFromOrThrowUnchecked("alice@wonderland.lit");
PGPKeyRing keyRing = PGPainless.generateKeyRing().simpleEcKeyRing("xmpp:" + jid);
XmlSecElementCanonicalizer elementCanonicalizer = new XmlSecElementCanonicalizer(Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS));
IkeySignatureCreationMechanism signingMechanism = new OxIkeySignatureCreationMechanism(
keyRing.getSecretKeys(), new UnprotectedKeysProtector());
IkeySignatureCreator creator = new IkeySignatureCreator(signingMechanism, elementCanonicalizer);
IkeySignatureVerificationMechanism verificationMechanism = new OxIkeySignatureVerificationMechanism(keyRing.getPublicKeys());
IkeySignatureVerifier verifier = new IkeySignatureVerifier(verificationMechanism, elementCanonicalizer);
OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(keyRing.getMasterKey());
List<SubordinateElement> subList = new ArrayList<>();
subList.add(new SubordinateElement(
new URI("xmpp:" + jid + "?;node=urn:xmpp:openpgp:0:public-keys:" + fingerprint + ";item=2020-01-21T10:46:21Z"),
fingerprint.toString()));
SubordinateListElement subs = new SubordinateListElement(jid, subList);
ProofElement proofElement = creator.createProofFor(subs);
IkeyElement ikeyElement = new IkeyElement(IkeyType.OX, subs, proofElement);
System.out.println(ikeyElement.toXML().toString());
assertTrue(verifier.verify(ikeyElement, jid));
}
}

View File

@ -1,24 +1,14 @@
package org.jivesoftware.smackx.ikey.element;
import org.junit.Test;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.impl.JidCreate;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
public class SubordinateListElementTest {
@Test
public void test() {
SubordinateListElement listElement = buildElement(
new SubordinateElement("xmpp:hamlet@denmark.lit?;node=urn:xmpp:openpgp:0:public-keys:1357B01865B2503C18453D208CAC2A9678548E35" +
";item=2020-01-21T10:46:21Z", "1357B01865B2503C18453D208CAC2A9678548E35"),
new SubordinateElement("xmpp:hamlet@denmark.lit?;node=urn:xmpp:omemo:1:bundles" +
";item=123456", "e64dc9166dd34db64c9247bd502c5969e365a98f3aa41c87247d120487fdd32f")
);
System.out.println(listElement.toXML());
}
private SubordinateListElement buildElement(SubordinateElement... subordinateElements) {
return new SubordinateListElement(Arrays.asList(subordinateElements));
}
}

View File

@ -0,0 +1,50 @@
package org.jivesoftware.smackx.ikey.element;
import org.apache.xml.security.Init;
import org.apache.xml.security.c14n.CanonicalizationException;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.c14n.InvalidCanonicalizerException;
import org.apache.xml.security.parser.XMLParserException;
import org.jivesoftware.smackx.ikey.XmlSecElementCanonicalizer;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import java.io.IOException;
import static junit.framework.TestCase.assertEquals;
public class XmlSecElementCanonicalizerTest {
@BeforeClass
public static void initialize() {
if (!Init.isInitialized()) {
Init.init();
}
}
@Test
@Ignore
public void test() throws InvalidCanonicalizerException, XMLParserException, IOException, CanonicalizationException {
XmlSecElementCanonicalizer canonicalizer = new XmlSecElementCanonicalizer(Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS));
String element = "<ikey xmlns='urn:xmpp:ikey:0' type='OX'><subordinates jid='alice@wonderland.lit'>" +
"<sub uri='xmpp:alice@wonderland.lit?;node=urn:xmpp:openpgp:0:public-keys:34669E842617A0D38A96016B359160D0B0868569;item=2020-01-21T10:46:21Z' fpr='34669E842617A0D38A96016B359160D0B0868569'/>" +
"</subordinates>" +
"<proof>iF4EABMIAAYFAl9I3esACgkQNZFg0LCGhWntNAD+LDO/Q+WQ5TrQOt4vBcqnUarCOZ6Ev4Wp4QgsIjs2BHcA/2BOIC6FBqkx80zB8NZsZu4H1fvn+gWgrscXhgf9+f+h</proof>" +
"</ikey>";
String elementWithInsignificantWhitespace = "" +
"<ikey xmlns='urn:xmpp:ikey:0'" +
" type='OX'>" +
" <subordinates jid='alice@wonderland.lit'>" +
" <sub uri=\"xmpp:alice@wonderland.lit?;node=urn:xmpp:openpgp:0:public-keys:34669E842617A0D38A96016B359160D0B0868569;item=2020-01-21T10:46:21Z\"\n" +
" fpr='34669E842617A0D38A96016B359160D0B0868569'/>" +
" </subordinates>" +
" <proof>" +
" iF4EABMIAAYFAl9I3esACgkQNZFg0LCGhWntNAD+LDO/Q+WQ5TrQOt4vBcqnUarCOZ6Ev4Wp4QgsIjs2BHcA/2BOIC6FBqkx80zB8NZsZu4H1fvn+gWgrscXhgf9+f+h" +
" </proof>" +
"</ikey>";
String can1 = new String(canonicalizer.canonicalize(element));
String can2 = new String(canonicalizer.canonicalize(elementWithInsignificantWhitespace));
assertEquals(can1, can2);
}
}

View File

@ -3,6 +3,7 @@ package org.mercury_im.messenger.learning_tests.rx;
import org.junit.Test;
import io.reactivex.Observable;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.BehaviorSubject;
import io.reactivex.subjects.Subject;
@ -15,8 +16,12 @@ public class BehaviourSubjectSubscriptionTest {
Observable<String> observable = Observable.just("One", "Two", "Three");
BehaviorSubject<String> behaviorSubject = BehaviorSubject.create();
observable.subscribe(behaviorSubject);
behaviorSubject.subscribe(System.out::println);
observable
.subscribeOn(Schedulers.io())
.subscribe(behaviorSubject);
behaviorSubject
//.subscribeOn(Schedulers.io())
.subscribe(System.out::println);
Thread.sleep(100);