mirror of
https://codeberg.org/Mercury-IM/Mercury-IM
synced 2025-02-07 20:06:24 +01:00
Wip: Signing mechanisms and XML Canonicalization
This commit is contained in:
parent
68657367cb
commit
d9c9eff602
22 changed files with 563 additions and 81 deletions
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.jivesoftware.smackx.ikey;
|
||||
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
|
||||
public class IkeyManager {
|
||||
|
||||
private IkeySignatureVerificationMechanism getSignatureMechanismFor(EntityBareJid contactJid) {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package org.jivesoftware.smackx.ikey;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface IkeySignatureCreationMechanism {
|
||||
|
||||
byte[] createSignature(byte[] data) throws IOException;
|
||||
|
||||
IkeyType getType();
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue