Wip: Signing mechanisms and XML Canonicalization
This commit is contained in:
parent
68657367cb
commit
d9c9eff602
|
@ -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.CliDatabaseModule;
|
||||||
import org.mercury_im.messenger.cli.di.module.CliSchedulersModule;
|
import org.mercury_im.messenger.cli.di.module.CliSchedulersModule;
|
||||||
import org.mercury_im.messenger.cli.di.module.X509WorkaroundConnectionFactoryModule;
|
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.RxMercuryMessageStoreFactoryModule;
|
||||||
import org.mercury_im.messenger.core.di.module.RxMercuryRosterStoreFactoryModule;
|
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.core.di.module.ViewModelModule;
|
||||||
import org.mercury_im.messenger.data.di.RepositoryModule;
|
import org.mercury_im.messenger.data.di.RepositoryModule;
|
||||||
|
|
||||||
|
@ -22,7 +24,9 @@ import dagger.Component;
|
||||||
ViewModelModule.class,
|
ViewModelModule.class,
|
||||||
X509WorkaroundConnectionFactoryModule.class,
|
X509WorkaroundConnectionFactoryModule.class,
|
||||||
RxMercuryMessageStoreFactoryModule.class,
|
RxMercuryMessageStoreFactoryModule.class,
|
||||||
RxMercuryRosterStoreFactoryModule.class
|
RxMercuryRosterStoreFactoryModule.class,
|
||||||
|
OpenPgpModule.class,
|
||||||
|
StanzaIdSourceFactoryModule.class
|
||||||
})
|
})
|
||||||
public interface CliComponent {
|
public interface CliComponent {
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,8 @@ dependencies {
|
||||||
annotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
|
annotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||||
testAnnotationProcessor "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"
|
compileOnly "org.projectlombok:lombok:$lombokVersion"
|
||||||
annotationProcessor "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.NamedElement;
|
||||||
import org.jivesoftware.smack.packet.XmlEnvironment;
|
import org.jivesoftware.smack.packet.XmlEnvironment;
|
||||||
|
import org.jivesoftware.smack.util.Objects;
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
public class SubordinateElement implements NamedElement {
|
public class SubordinateElement implements NamedElement {
|
||||||
|
|
||||||
public static final String ELEMENT = "sub";
|
public static final String ELEMENT = "sub";
|
||||||
public static final String ATTR_SUB_URI = "uri";
|
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;
|
private final String subFingerprint;
|
||||||
|
|
||||||
public SubordinateElement(String subKeyItemUri, String subKeyFingerprint) {
|
public SubordinateElement(URI subKeyItemUri, String subKeyFingerprint) {
|
||||||
this.subUri = StringUtils.requireNotNullNorEmpty(subKeyItemUri, "uri MUST NOT be null nor empty.");
|
this.subUri = Objects.requireNonNull(subKeyItemUri, "uri MUST NOT be null nor empty.");
|
||||||
this.subFingerprint = StringUtils.requireNotNullNorEmpty(subKeyFingerprint, "fingerprint 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;
|
return subUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +38,7 @@ public class SubordinateElement implements NamedElement {
|
||||||
@Override
|
@Override
|
||||||
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
||||||
XmlStringBuilder xml = new XmlStringBuilder(this)
|
XmlStringBuilder xml = new XmlStringBuilder(this)
|
||||||
.attribute(ATTR_SUB_URI, getUri())
|
.attribute(ATTR_SUB_URI, getUri().toString())
|
||||||
.attribute(ATTR_SUB_FINGERPRINT, getFingerprint())
|
.attribute(ATTR_SUB_FINGERPRINT, getFingerprint())
|
||||||
.closeEmptyElement();
|
.closeEmptyElement();
|
||||||
return xml;
|
return xml;
|
||||||
|
|
|
@ -1,27 +1,34 @@
|
||||||
package org.jivesoftware.smackx.ikey.element;
|
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.packet.XmlEnvironment;
|
||||||
import org.jivesoftware.smack.util.Objects;
|
import org.jivesoftware.smack.util.Objects;
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
import org.jivesoftware.smackx.ikey.IkeyConstants;
|
import org.jivesoftware.smackx.ikey.IkeyConstants;
|
||||||
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
|
|
||||||
import java.util.List;
|
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 NAMESPACE = IkeyConstants.NAMESPACE;
|
||||||
public static final String ELEMENT = "subordinates";
|
public static final String ELEMENT = "subordinates";
|
||||||
|
public static final String ATTR_JID = "jid";
|
||||||
|
|
||||||
private final List<SubordinateElement> subordinates;
|
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.");
|
this.subordinates = Objects.requireNonNullNorEmpty(subordinates, "List of subordinates MUST NOT be null nor empty.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public EntityBareJid getJid() {
|
||||||
public String getNamespace() {
|
return jid;
|
||||||
return NAMESPACE;
|
}
|
||||||
|
|
||||||
|
public List<SubordinateElement> getSubordinates() {
|
||||||
|
return subordinates;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -31,10 +38,10 @@ public class SubordinateListElement implements ExtensionElement {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
||||||
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment)
|
return new XmlStringBuilder(this)
|
||||||
|
.attribute(ATTR_JID, getJid())
|
||||||
.rightAngleBracket()
|
.rightAngleBracket()
|
||||||
.append(subordinates)
|
.append(getSubordinates())
|
||||||
.closeElement(this);
|
.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;
|
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}.
|
* Parse a {@link PubSubUri}.
|
||||||
* This method ignores unknown parameters.
|
* This method ignores unknown parameters.
|
||||||
|
@ -43,14 +52,7 @@ public class PubSubUri {
|
||||||
public static PubSubUri from(String s)
|
public static PubSubUri from(String s)
|
||||||
throws XmppStringprepException {
|
throws XmppStringprepException {
|
||||||
URI uri = URI.create(s);
|
URI uri = URI.create(s);
|
||||||
throwIfNoXmppUri(uri);
|
return new PubSubUri(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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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;
|
package org.jivesoftware.smackx.ikey.element;
|
||||||
|
|
||||||
import org.junit.Test;
|
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;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class SubordinateListElementTest {
|
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 org.junit.Test;
|
||||||
|
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
import io.reactivex.subjects.BehaviorSubject;
|
import io.reactivex.subjects.BehaviorSubject;
|
||||||
import io.reactivex.subjects.Subject;
|
import io.reactivex.subjects.Subject;
|
||||||
|
|
||||||
|
@ -15,8 +16,12 @@ public class BehaviourSubjectSubscriptionTest {
|
||||||
Observable<String> observable = Observable.just("One", "Two", "Three");
|
Observable<String> observable = Observable.just("One", "Two", "Three");
|
||||||
BehaviorSubject<String> behaviorSubject = BehaviorSubject.create();
|
BehaviorSubject<String> behaviorSubject = BehaviorSubject.create();
|
||||||
|
|
||||||
observable.subscribe(behaviorSubject);
|
observable
|
||||||
behaviorSubject.subscribe(System.out::println);
|
.subscribeOn(Schedulers.io())
|
||||||
|
.subscribe(behaviorSubject);
|
||||||
|
behaviorSubject
|
||||||
|
//.subscribeOn(Schedulers.io())
|
||||||
|
.subscribe(System.out::println);
|
||||||
|
|
||||||
Thread.sleep(100);
|
Thread.sleep(100);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue