Migrate ikey away from xmlsec

This commit is contained in:
Paul Schaub 2020-09-25 12:58:27 +02:00
parent 58740c4a69
commit be59c09b15
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
30 changed files with 736 additions and 269 deletions

View File

@ -21,9 +21,6 @@ dependencies {
api "org.jxmpp:jxmpp-core:1.0.1"
api "org.jxmpp:jxmpp-jid:1.0.1"
api "org.apache.santuario:xmlsec:2.2.0"
testImplementation "org.igniterealtime.smack:smack-java7:$smackJava7Version"
// RxJava2

View File

@ -1,22 +1,16 @@
package org.jivesoftware.smackx.ikey;
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.PGPPublicKeyRing;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.util.Async;
import org.jivesoftware.smackx.ikey.element.IkeyElement;
import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureVerificationMechanism;
import org.jivesoftware.smackx.ikey.record.IkeyStore;
import org.jivesoftware.smackx.ikey.util.IkeyConstants;
import org.jivesoftware.smackx.ikey.util.UnsupportedSignatureAlgorithmException;
import org.jivesoftware.smackx.ikey.util.canonicalization.ElementCanonicalizer;
import org.jivesoftware.smackx.ikey.util.canonicalization.XmlSecElementCanonicalizer;
import org.jivesoftware.smackx.ikey_ox.OxIkeySignatureVerificationMechanism;
import org.jivesoftware.smackx.pep.PepEventListener;
import org.jivesoftware.smackx.pep.PepManager;
@ -41,15 +35,9 @@ public final class IkeyManager extends Manager {
private static final Map<XMPPConnection, IkeyManager> INSTANCES = new WeakHashMap<>();
private IkeyStore store;
private final ElementCanonicalizer canonicalizer;
private IkeyManager(XMPPConnection connection) {
super(connection);
try {
this.canonicalizer = new XmlSecElementCanonicalizer(Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS));
} catch (InvalidCanonicalizerException e) {
throw new AssertionError(e);
}
}
public static synchronized IkeyManager getInstanceFor(XMPPConnection connection) {
@ -106,9 +94,9 @@ public final class IkeyManager extends Manager {
}
private void processIkeyElement(EntityBareJid from, IkeyElement element)
throws XMLParserException, IOException, CanonicalizationException, UnsupportedSignatureAlgorithmException {
throws IOException, UnsupportedSignatureAlgorithmException {
if (isFromTheFuture(element)) {
LOGGER.log(Level.WARNING, "Received ikey element appears to be from the future: " + element.getSubordinates().getTimestamp());
LOGGER.log(Level.WARNING, "Received ikey element appears to be from the future: " + element.getSignedElement().getChildElement().getTimestamp());
return;
}
@ -126,9 +114,9 @@ public final class IkeyManager extends Manager {
}
private boolean verifyIkeyElement(EntityBareJid from, IkeyElement element)
throws XMLParserException, IOException, CanonicalizationException, UnsupportedSignatureAlgorithmException {
throws IOException, UnsupportedSignatureAlgorithmException {
IkeySignatureVerificationMechanism verificationMechanism = getSignatureVerificationMechanismFor(element);
IkeySignatureVerifier verifier = new IkeySignatureVerifier(verificationMechanism, canonicalizer);
IkeySignatureVerifier verifier = new IkeySignatureVerifier(verificationMechanism);
return verifier.verify(element, from);
}
@ -146,18 +134,18 @@ public final class IkeyManager extends Manager {
}
private static boolean isFromTheFuture(IkeyElement element) {
Date elementTimestamp = element.getSubordinates().getTimestamp();
Date elementTimestamp = element.getSignedElement().getChildElement().getTimestamp();
Date now = new Date();
return elementTimestamp.after(now);
}
private boolean existsSameOrNewerRecord(IkeyElement ikeyElement) throws IOException {
IkeyElement existingRecord = store.loadIkeyRecord(ikeyElement.getSubordinates().getJid());
IkeyElement existingRecord = store.loadIkeyRecord(ikeyElement.getSignedElement().getChildElement().getJid());
if (existingRecord == null) {
return false;
}
Date latestTimestamp = existingRecord.getSubordinates().getTimestamp();
Date eventTimestamp = ikeyElement.getSubordinates().getTimestamp();
Date latestTimestamp = existingRecord.getSignedElement().getChildElement().getTimestamp();
Date eventTimestamp = ikeyElement.getSignedElement().getChildElement().getTimestamp();
return latestTimestamp.equals(eventTimestamp) // same
|| latestTimestamp.after(eventTimestamp); // newer
}
@ -168,7 +156,7 @@ public final class IkeyManager extends Manager {
public void onPepEvent(EntityBareJid from, IkeyElement event, String id, Message carrierMessage) {
try {
processIkeyElement(from, event);
} catch (XMLParserException | CanonicalizationException | IOException | UnsupportedSignatureAlgorithmException e) {
} catch (IOException | UnsupportedSignatureAlgorithmException e) {
LOGGER.log(Level.WARNING, "Error:", e);
}
}

View File

@ -1,29 +1,26 @@
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.smack.util.stringencoder.Base64;
import org.jivesoftware.smackx.ikey.element.ProofElement;
import org.jivesoftware.smackx.ikey.element.SignedElement;
import org.jivesoftware.smackx.ikey.element.SubordinateListElement;
import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureCreationMechanism;
import org.jivesoftware.smackx.ikey.util.canonicalization.XmlSecElementCanonicalizer;
import java.io.IOException;
public class IkeySignatureCreator {
private final IkeySignatureCreationMechanism signatureCreationMechanism;
private final XmlSecElementCanonicalizer elementCanonicalizer;
public IkeySignatureCreator(IkeySignatureCreationMechanism signingMechanism, XmlSecElementCanonicalizer elementCanonicalizer) {
public IkeySignatureCreator(IkeySignatureCreationMechanism signingMechanism) {
this.signatureCreationMechanism = signingMechanism;
this.elementCanonicalizer = elementCanonicalizer;
}
public ProofElement createProofFor(SubordinateListElement subordinateListElement) throws XMLParserException, IOException, CanonicalizationException {
byte[] canonicalized = elementCanonicalizer.canonicalize(subordinateListElement);
public ProofElement createProofFor(SubordinateListElement subordinateListElement)
throws IOException {
byte[] canonicalized = new SignedElement(subordinateListElement).getUtf8Bytes();
byte[] signature = signatureCreationMechanism.createSignature(canonicalized);
return new ProofElement(Base64.toBase64String(signature));
return new ProofElement(Base64.encodeToString(signature));
}
}

View File

@ -1,11 +1,8 @@
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.jivesoftware.smackx.ikey.mechanism.IkeySignatureVerificationMechanism;
import org.jivesoftware.smackx.ikey.util.canonicalization.ElementCanonicalizer;
import org.jxmpp.jid.EntityBareJid;
import java.io.IOException;
@ -13,26 +10,24 @@ import java.io.IOException;
public class IkeySignatureVerifier {
private final IkeySignatureVerificationMechanism signatureVerificationMechanism;
private final ElementCanonicalizer elementCanonicalizer;
public IkeySignatureVerifier(IkeySignatureVerificationMechanism signatureVerificationMechanism, ElementCanonicalizer elementCanonicalizer) {
public IkeySignatureVerifier(IkeySignatureVerificationMechanism signatureVerificationMechanism) {
this.signatureVerificationMechanism = signatureVerificationMechanism;
this.elementCanonicalizer = elementCanonicalizer;
}
public boolean verify(IkeyElement element, EntityBareJid owner)
throws XMLParserException, IOException, CanonicalizationException {
throws IOException {
throwIfMismatchingMechanism(element);
throwIfMismatchingOwnerJid(element, owner);
byte[] canonicalizedXml = elementCanonicalizer.canonicalize(element.getSubordinates());
byte[] canonicalizedXml = element.getSignedElement().getUtf8Bytes();
byte[] signature = Base64.decode(element.getProof().getBase64Signature());
return signatureVerificationMechanism.isSignatureValid(canonicalizedXml, signature);
}
private static void throwIfMismatchingOwnerJid(IkeyElement element, EntityBareJid owner) {
if (!element.getSubordinates().getJid().equals(owner)) {
if (!element.getSignedElement().getChildElement().getJid().equals(owner)) {
throw new IllegalArgumentException("Provided ikey element does not contain jid of " + owner);
}
}

View File

@ -21,13 +21,13 @@ public class IkeyElement implements ExtensionElement {
private final IkeyType type;
private final SuperordinateElement superordinate;
private final SubordinateListElement subordinates;
private final SignedElement signedElement;
private final ProofElement proof;
public IkeyElement(IkeyType type, SuperordinateElement superordinate, SubordinateListElement subordinates, ProofElement proof) {
public IkeyElement(IkeyType type, SuperordinateElement superordinate, SignedElement signedElement, ProofElement proof) {
this.type = type;
this.superordinate = superordinate;
this.subordinates = subordinates;
this.signedElement = signedElement;
this.proof = proof;
}
@ -39,8 +39,8 @@ public class IkeyElement implements ExtensionElement {
return superordinate;
}
public SubordinateListElement getSubordinates() {
return subordinates;
public SignedElement getSignedElement() {
return signedElement;
}
public ProofElement getProof() {
@ -63,7 +63,7 @@ public class IkeyElement implements ExtensionElement {
.attribute(ATTR_IKEY_TYPE, getType())
.rightAngleBracket()
.append(getSuperordinate())
.append(getSubordinates())
.append(getSignedElement())
.append(getProof())
.closeElement(this);
}
@ -74,7 +74,7 @@ public class IkeyElement implements ExtensionElement {
.append(getElementName())
.append(getType())
.append(getSuperordinate())
.append(getSubordinates())
.append(getSignedElement())
.append(getProof())
.build();
}
@ -85,7 +85,7 @@ public class IkeyElement implements ExtensionElement {
.append(getElementName(), o.getElementName())
.append(getType(), o.getType())
.append(getSuperordinate(), o.getSuperordinate())
.append(getSubordinates(), o.getSubordinates())
.append(getSignedElement(), o.getSignedElement())
.append(getProof(), o.getProof()));
}
}

View File

@ -0,0 +1,80 @@
package org.jivesoftware.smackx.ikey.element;
import org.jivesoftware.smack.packet.NamedElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.util.EqualsUtil;
import org.jivesoftware.smack.util.HashCode;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smack.util.stringencoder.Base64;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.ikey.provider.SubordinateListElementProvider;
import java.io.IOException;
public class SignedElement implements NamedElement {
public static final String ELEMENT = "signed";
private final String base64Encoded;
private final SubordinateListElement childElement;
private final byte[] utf8Bytes;
public SignedElement(SubordinateListElement childElement) {
this(childElement, null);
}
public SignedElement(String base64Encoded) throws IOException, XmlPullParserException, SmackParsingException {
this(SubordinateListElementProvider.INSTANCE.parse(
PacketParserUtils.getParserFor(Base64.decodeToString(base64Encoded))),
base64Encoded);
}
private SignedElement(SubordinateListElement childElement, String base64Encoded) {
this.childElement = Objects.requireNonNull(childElement);
this.base64Encoded = base64Encoded == null ? childElement.toBase64EncodedString() : base64Encoded;
this.utf8Bytes = Base64.decode(this.base64Encoded);
}
public String getBase64Encoded() {
return base64Encoded;
}
public byte[] getUtf8Bytes() {
return utf8Bytes;
}
public SubordinateListElement getChildElement() {
return childElement;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
return new XmlStringBuilder(this)
.rightAngleBracket()
.append(getBase64Encoded())
.closeElement(this);
}
@Override
public int hashCode() {
return HashCode.builder()
.append(getElementName())
.append(getBase64Encoded())
.build();
}
@Override
public boolean equals(Object other) {
return EqualsUtil.equals(this, other, (e, o) -> e
.append(getElementName(), o.getElementName())
.append(getBase64Encoded(), o.getBase64Encoded()));
}
}

View File

@ -1,23 +1,27 @@
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.EqualsUtil;
import org.jivesoftware.smack.util.HashCode;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smack.util.stringencoder.Base64;
import org.jivesoftware.smackx.ikey.util.IkeyConstants;
import org.jxmpp.jid.EntityBareJid;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
public class SubordinateListElement implements NamedElement {
public class SubordinateListElement implements ExtensionElement {
public static final String NAMESPACE = IkeyConstants.NAMESPACE;
public static final String ELEMENT = "subordinates";
public static final String ATTR_STAMP = "stamp";
public static final String ATTR_JID = "jid";
public static final String ATTR_STAMP = "stamp";
private final List<SubordinateElement> subordinates;
private final EntityBareJid jid;
@ -41,6 +45,10 @@ public class SubordinateListElement implements NamedElement {
return subordinates;
}
public String toBase64EncodedString() {
return Base64.encodeToString(toXML().toString().getBytes(StandardCharsets.UTF_8));
}
@Override
public String getElementName() {
return ELEMENT;
@ -74,4 +82,9 @@ public class SubordinateListElement implements NamedElement {
.append(getTimestamp(), o.getTimestamp())
.append(getSubordinates(), o.getSubordinates()));
}
@Override
public String getNamespace() {
return NAMESPACE;
}
}

View File

@ -3,10 +3,13 @@ package org.jivesoftware.smackx.ikey.provider;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.util.stringencoder.Base64;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.ikey.element.IkeyElement;
import org.jivesoftware.smackx.ikey.element.SignedElement;
import org.jivesoftware.smackx.ikey.element.ProofElement;
import org.jivesoftware.smackx.ikey.element.SubordinateElement;
import org.jivesoftware.smackx.ikey.element.SubordinateListElement;
@ -30,9 +33,7 @@ public class IkeyElementProvider extends ExtensionElementProvider<IkeyElement> {
String typeString = ParserUtils.getRequiredAttribute(parser, IkeyElement.ATTR_IKEY_TYPE);
IkeyType type = IkeyType.valueOf(typeString);
SuperordinateElement superordinate = null;
List<SubordinateElement> subordinates = new ArrayList<>();
EntityBareJid jid = null;
Date timestamp = null;
SignedElement signedElement = null;
ProofElement proofElement = null;
do {
@ -44,17 +45,8 @@ public class IkeyElementProvider extends ExtensionElementProvider<IkeyElement> {
superordinate = new SuperordinateElement(parser.nextText());
break;
case SubordinateListElement.ELEMENT:
jid = ParserUtils.getBareJidAttribute(parser);
timestamp = ParserUtils.getDateFromXep82String(
ParserUtils.getRequiredAttribute(parser, SubordinateListElement.ATTR_STAMP));
break;
case SubordinateElement.ELEMENT:
String uriString = ParserUtils.getRequiredAttribute(parser, SubordinateElement.ATTR_SUB_URI);
URI uri = URI.create(uriString);
String fingerprint = ParserUtils.getRequiredAttribute(parser, SubordinateElement.ATTR_SUB_FINGERPRINT);
subordinates.add(new SubordinateElement(uri, fingerprint));
case SignedElement.ELEMENT:
signedElement = new SignedElement(parser.nextText());
break;
case ProofElement.ELEMENT:
@ -66,6 +58,6 @@ public class IkeyElementProvider extends ExtensionElementProvider<IkeyElement> {
break;
}
} while (parser.getDepth() != initialDepth);
return new IkeyElement(type, superordinate, new SubordinateListElement(jid, timestamp, subordinates), proofElement);
return new IkeyElement(type, superordinate, signedElement, proofElement);
}
}

View File

@ -0,0 +1,45 @@
package org.jivesoftware.smackx.ikey.provider;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.ikey.element.SubordinateElement;
import org.jivesoftware.smackx.ikey.element.SubordinateListElement;
import org.jxmpp.jid.EntityBareJid;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class SubordinateListElementProvider extends ExtensionElementProvider<SubordinateListElement> {
public static final SubordinateListElementProvider INSTANCE = new SubordinateListElementProvider();
@Override
public SubordinateListElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
throws XmlPullParserException, IOException, SmackParsingException {
Date timestamp = ParserUtils.getDateFromXep82String(ParserUtils.getRequiredAttribute(parser, SubordinateListElement.ATTR_STAMP));
EntityBareJid jid = ParserUtils.getBareJidAttribute(parser);
List<SubordinateElement> subordinates = new ArrayList<>();
do {
switch (parser.nextTag()) {
case START_ELEMENT:
if (SubordinateElement.ELEMENT.equals(parser.getName())) {
String uriString = ParserUtils.getRequiredAttribute(parser, SubordinateElement.ATTR_SUB_URI);
URI uri = URI.create(uriString);
String fingerprint = ParserUtils.getRequiredAttribute(parser, SubordinateElement.ATTR_SUB_FINGERPRINT);
subordinates.add(new SubordinateElement(uri, fingerprint));
}
break;
case END_ELEMENT:
break;
}
} while (parser.getDepth() != initialDepth);
return new SubordinateListElement(jid, timestamp, subordinates);
}
}

View File

@ -1,4 +1,4 @@
package org.jivesoftware.smackx.ikey;
package org.jivesoftware.smackx.ikey.record;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.util.Objects;
@ -7,6 +7,7 @@ import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.ikey.element.IkeyElement;
import org.jivesoftware.smackx.ikey.provider.IkeyElementProvider;
import org.jivesoftware.smackx.ikey.record.IkeyStore;
import org.jxmpp.jid.EntityBareJid;
import java.io.BufferedReader;

View File

@ -1,4 +1,4 @@
package org.jivesoftware.smackx.ikey;
package org.jivesoftware.smackx.ikey.record;
import org.jivesoftware.smackx.ikey.element.IkeyElement;
import org.jxmpp.jid.EntityBareJid;

View File

@ -1,26 +0,0 @@
package org.jivesoftware.smackx.ikey.util.canonicalization;
import org.apache.xml.security.c14n.CanonicalizationException;
import org.apache.xml.security.parser.XMLParserException;
import org.jivesoftware.smack.packet.Element;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
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(StandardCharsets.UTF_8));
}
byte[] canonicalize(byte[] xml) throws XMLParserException, IOException, CanonicalizationException;
default String removeInterElementWhitespace(String xml) {
return xml.replaceAll(">\\s*<", "><");
}
}

View File

@ -1,25 +0,0 @@
package org.jivesoftware.smackx.ikey.util.canonicalization;
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;
public class XmlSecElementCanonicalizer implements ElementCanonicalizer {
private final Canonicalizer canonicalizer;
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,20 @@
package org.jivesoftware.smackx.signed.xep0285;
import org.bouncycastle.openpgp.PGPSignature;
import org.jivesoftware.smackx.signed.xep0285.element.DataElement;
import org.jivesoftware.smackx.signed.xep0285.element.PlainElement;
import org.jivesoftware.smackx.signed.xep0285.element.SignatureElement;
import org.jivesoftware.smackx.signed.xep0285.element.SignedElement;
import org.jivesoftware.smackx.signed.xep0285.signing.Signer;
import java.io.IOException;
public class EncapsulatingSignatureManager {
public static SignedElement createSignedElement(PlainElement plainElement, Signer signer) throws IOException {
DataElement dataElement = DataElement.fromPlainElement(plainElement);
PGPSignature signature = signer.createSignature(dataElement.getUtf8Bytes());
SignatureElement signatureElement = SignatureElement.fromBytes(signer.getAlgorithmName(signature), signature.getEncoded());
return new SignedElement(signatureElement, dataElement);
}
}

View File

@ -0,0 +1,58 @@
package org.jivesoftware.smackx.signed.xep0285.element;
import org.jivesoftware.smack.packet.NamedElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smack.util.stringencoder.Base64;
import java.nio.charset.StandardCharsets;
public final class DataElement implements NamedElement {
public static final String ELEMENT = "data";
private final String base64Content;
private final String utf8Content;
private DataElement(String utf8Content, String base64Content) {
this.utf8Content = utf8Content;
this.base64Content = base64Content;
}
public static DataElement fromUtf8Content(String utf8Content) {
return new DataElement(utf8Content, Base64.encodeToString(utf8Content.getBytes(StandardCharsets.UTF_8)));
}
public static DataElement fromBase64Content(String base64Content) {
return new DataElement(Base64.decodeToString(base64Content), base64Content);
}
public static DataElement fromPlainElement(PlainElement element) {
return fromUtf8Content(element.toXML().toString());
}
public String getUtf8Content() {
return utf8Content;
}
public byte[] getUtf8Bytes() {
return getUtf8Content().getBytes(StandardCharsets.UTF_8);
}
public String getBase64Content() {
return base64Content;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
return new XmlStringBuilder(this)
.rightAngleBracket()
.append(getBase64Content())
.closeElement(this);
}
}

View File

@ -0,0 +1,92 @@
package org.jivesoftware.smackx.signed.xep0285.element;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.EqualsUtil;
import org.jivesoftware.smack.util.HashCode;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smack.util.stringencoder.Base64;
import java.nio.charset.StandardCharsets;
import java.util.Date;
public final class PlainElement implements ExtensionElement {
public static final String ELEMENT = "plain";
public static final String NAMESPACE = "urn:xmpp:signed:0";
public static final String ATTR_TIMESTAMP = "timestamp";
private final String utf8Content;
private final String base64Content;
private final Date timestamp;
private PlainElement(String utf8Content, String base64Content, Date timestamp) {
this.utf8Content = utf8Content;
this.base64Content = base64Content;
this.timestamp = timestamp;
}
public static PlainElement fromExtensionElement(ExtensionElement element) {
return fromUtf8String(element.toXML().toString(), new Date());
}
public static PlainElement fromUtf8String(String utf8, Date timestamp) {
return new PlainElement(utf8, Base64.encodeToString(utf8.getBytes(StandardCharsets.UTF_8)), timestamp);
}
public static PlainElement fromBase64String(String base64, Date timestamp) {
return new PlainElement(Base64.decodeToString(base64), base64, timestamp);
}
public String getBase64Content() {
return base64Content;
}
public String getUtf8Content() {
return utf8Content;
}
public byte[] asUtf8Bytes() {
return toXML().toString().getBytes(StandardCharsets.UTF_8);
}
public Date getTimestamp() {
return timestamp;
}
@Override
public String getNamespace() {
return NAMESPACE;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
return new XmlStringBuilder(this)
.attribute(ATTR_TIMESTAMP, getTimestamp())
.rightAngleBracket()
.append(getBase64Content())
.closeElement(this);
}
@Override
public boolean equals(Object other) {
return EqualsUtil.equals(this, other, (e, o) -> e
.append(getElementName(), o.getElementName())
.append(getBase64Content(), o.getBase64Content())
.append(getTimestamp(), o.getTimestamp()));
}
@Override
public int hashCode() {
return HashCode.builder()
.append(getElementName())
.append(getBase64Content())
.append(getTimestamp())
.build();
}
}

View File

@ -0,0 +1,56 @@
package org.jivesoftware.smackx.signed.xep0285.element;
import org.jivesoftware.smack.packet.NamedElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smack.util.stringencoder.Base64;
public final class SignatureElement implements NamedElement {
public static final String ELEMENT = "signature";
public static final String ATTR_ALGORITHM = "algorithm";
private final String algorithm;
private final byte[] bytes;
private final String base64Content;
private SignatureElement(String algorithm, byte[] bytes, String base64Content) {
this.algorithm = algorithm;
this.bytes = bytes;
this.base64Content = base64Content;
}
public static SignatureElement fromBytes(String algorithm, byte[] bytes) {
return new SignatureElement(algorithm, bytes, Base64.encodeToString(bytes));
}
public static SignatureElement fromBase64Content(String algorithm, String base64Content) {
return new SignatureElement(algorithm, Base64.decode(base64Content), base64Content);
}
public String getAlgorithm() {
return algorithm;
}
public String getBase64Content() {
return base64Content;
}
public byte[] getBytes() {
return bytes.clone();
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
return new XmlStringBuilder(this)
.attribute(ATTR_ALGORITHM, getAlgorithm())
.rightAngleBracket()
.append(getBase64Content())
.closeElement(this);
}
}

View File

@ -0,0 +1,45 @@
package org.jivesoftware.smackx.signed.xep0285.element;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.XmlStringBuilder;
public class SignedElement implements ExtensionElement {
public static final String ELEMENT = "signed";
private final SignatureElement signature;
private final DataElement data;
public SignedElement(SignatureElement signature, DataElement data) {
this.signature = signature;
this.data = data;
}
public SignatureElement getSignature() {
return signature;
}
public DataElement getData() {
return data;
}
@Override
public String getNamespace() {
return PlainElement.NAMESPACE;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
return new XmlStringBuilder(this)
.rightAngleBracket()
.append(getSignature())
.append(getData())
.closeElement(this);
}
}

View File

@ -0,0 +1,20 @@
package org.jivesoftware.smackx.signed.xep0285.provider;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.signed.xep0285.element.PlainElement;
import java.io.IOException;
public class PlainElementProvider extends ExtensionElementProvider<PlainElement> {
@Override
public PlainElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
throws XmlPullParserException, IOException, SmackParsingException {
String timestamp = ParserUtils.getRequiredAttribute(parser, PlainElement.ATTR_TIMESTAMP);
return PlainElement.fromBase64String(parser.nextText(), ParserUtils.getDateFromXep82String(timestamp));
}
}

View File

@ -0,0 +1,45 @@
package org.jivesoftware.smackx.signed.xep0285.provider;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.signed.xep0285.element.DataElement;
import org.jivesoftware.smackx.signed.xep0285.element.SignatureElement;
import org.jivesoftware.smackx.signed.xep0285.element.SignedElement;
import java.io.IOException;
public class SignedElementProvider extends ExtensionElementProvider<SignedElement> {
@Override
public SignedElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
throws XmlPullParserException, IOException {
SignatureElement signature = null;
DataElement data = null;
XmlPullParser.TagEvent tag;
String name;
do {
tag = parser.nextTag();
name = parser.getName();
if (tag == XmlPullParser.TagEvent.START_ELEMENT) {
switch (name) {
case SignatureElement.ELEMENT:
String algorithm = ParserUtils.getRequiredAttribute(parser, SignatureElement.ATTR_ALGORITHM);
signature = SignatureElement.fromBase64Content(algorithm, parser.nextText());
break;
case DataElement.ELEMENT:
data = DataElement.fromBase64Content(parser.nextText());
break;
}
}
} while (parser.getDepth() != initialDepth);
return new SignedElement(Objects.requireNonNull(signature), Objects.requireNonNull(data));
}
}

View File

@ -0,0 +1,74 @@
package org.jivesoftware.smackx.signed.xep0285.signing;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.util.io.Streams;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.algorithm.PublicKeyAlgorithm;
import org.pgpainless.decryption_verification.OpenPgpMetadata;
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 OpenPGPSigner implements Signer {
private final SecretKeyRingProtector protector;
private final PGPSecretKeyRing secretKey;
public OpenPGPSigner(PGPSecretKeyRing secretKey, SecretKeyRingProtector protector) {
this.protector = protector;
this.secretKey = secretKey;
}
@Override
public PGPSignature createSignature(byte[] data) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
try {
EncryptionStream signer = PGPainless.createEncryptor().onOutputStream(outputStream)
.doNotEncrypt()
.createDetachedSignature()
.signWith(protector, secretKey)
.noArmor();
Streams.pipeAll(inputStream, signer);
signer.close();
inputStream.close();
outputStream.close();
OpenPgpMetadata metadata = signer.getResult();
return metadata.getSignatures().iterator().next();
} catch (PGPException e) {
throw new IOException(e);
}
}
@Override
public String getAlgorithmName(PGPSignature signature) {
PublicKeyAlgorithm signAlgo = PublicKeyAlgorithm.fromId(signature.getKeyAlgorithm());
String signAlgoName;
switch (signAlgo) {
case RSA_GENERAL:
case RSA_ENCRYPT:
case RSA_SIGN:
signAlgoName = "RSA";
break;
case DSA:
signAlgoName = "DSA";
break;
case ECDSA:
signAlgoName = "ECDSA";
break;
default:
throw new IllegalArgumentException("Signature was signed with unknown signature algorithm: " + signature.getKeyAlgorithm());
}
signAlgoName += HashAlgorithm.fromId(signature.getHashAlgorithm());
return signAlgoName;
}
}

View File

@ -0,0 +1,12 @@
package org.jivesoftware.smackx.signed.xep0285.signing;
import org.bouncycastle.openpgp.PGPSignature;
import java.io.IOException;
public interface Signer {
PGPSignature createSignature(byte[] data) throws IOException;
String getAlgorithmName(PGPSignature signature);
}

View File

@ -3,7 +3,13 @@ package org.jivesoftware.smackx.util;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.util.stringencoder.Base64;
import org.jivesoftware.smack.xml.SmackXmlParser;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.security.Security;
public class MercurySmackTestSuite {
@ -35,4 +41,39 @@ public class MercurySmackTestSuite {
Security.addProvider(new BouncyCastleProvider());
}
public static XmlPullParser getParser(String string) {
return getParser(string, null);
}
public static XmlPullParser getParser(String string, String startTag) {
return getParser(new StringReader(string), startTag);
}
private static XmlPullParser getParser(Reader reader, String startTag) {
XmlPullParser parser;
try {
parser = SmackXmlParser.newXmlParser(reader);
if (startTag == null) {
while (parser.getEventType() != XmlPullParser.Event.START_ELEMENT) {
parser.next();
}
return parser;
}
boolean found = false;
while (!found) {
if ((parser.next() == XmlPullParser.Event.START_ELEMENT) && parser.getName().equals(startTag))
found = true;
}
if (!found)
throw new IllegalArgumentException("Can not find start tag '" + startTag + "'");
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
return parser;
}
}

View File

@ -1,35 +0,0 @@
package org.mercury_im.messenger.core.di.module;
import org.apache.xml.security.Init;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.c14n.InvalidCanonicalizerException;
import org.jivesoftware.smackx.ikey.util.canonicalization.ElementCanonicalizer;
import org.jivesoftware.smackx.ikey.util.canonicalization.XmlSecElementCanonicalizer;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class IkeyModule {
@Singleton
@Provides
static Canonicalizer provideCanonicalizer() {
if (!Init.isInitialized()) {
Init.init();
}
try {
return Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS);
} catch (InvalidCanonicalizerException e) {
throw new AssertionError(e);
}
}
@Singleton
@Provides
static ElementCanonicalizer provideElementCanonicalizer(Canonicalizer canonicalizer) {
return new XmlSecElementCanonicalizer(canonicalizer);
}
}

View File

@ -1,8 +1,7 @@
package org.jivesoftware.smackx.ikey.element;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.xml.SmackXmlParser;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.ikey.mechanism.IkeyType;
import org.jivesoftware.smackx.ikey.provider.IkeyElementProvider;
@ -12,8 +11,6 @@ import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.impl.JidCreate;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
@ -44,53 +41,18 @@ public class IkeyElementTest extends MercurySmackTestSuite {
);
ProofElement proof = new ProofElement("d2hpbGUgdGhpcyBpcyBub3QgYSB2YWxpZCBwcm9vZiwgaXQgaXMgc3VmZmljaWVudCBmb3IgdGVzdGluZy4=");
IkeyElement ikeyElement = new IkeyElement(type, superordinate, subordinates, proof);
IkeyElement ikeyElement = new IkeyElement(type, superordinate, new SignedElement(subordinates), proof);
String xml = ikeyElement.toXML().toString();
System.out.println(xml);
IkeyElement parsed = IkeyElementProvider.INSTANCE.parse(getParser(xml));
IkeyElement parsed = IkeyElementProvider.INSTANCE.parse(PacketParserUtils.getParserFor(xml));
assertEquals(ikeyElement, parsed);
assertEquals(subordinates, parsed.getSignedElement().getChildElement());
}
private static SubordinateListElement buildSubListElement(EntityBareJid jid, Date date, SubordinateElement... subordinateElements) {
return new SubordinateListElement(jid, date, Arrays.asList(subordinateElements));
}
public static XmlPullParser getParser(String string) {
return getParser(string, null);
}
public static XmlPullParser getParser(String string, String startTag) {
return getParser(new StringReader(string), startTag);
}
private static XmlPullParser getParser(Reader reader, String startTag) {
XmlPullParser parser;
try {
parser = SmackXmlParser.newXmlParser(reader);
if (startTag == null) {
while (parser.getEventType() != XmlPullParser.Event.START_ELEMENT) {
parser.next();
}
return parser;
}
boolean found = false;
while (!found) {
if ((parser.next() == XmlPullParser.Event.START_ELEMENT) && parser.getName().equals(startTag))
found = true;
}
if (!found)
throw new IllegalArgumentException("Can not find start tag '" + startTag + "'");
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
return parser;
}
}

View File

@ -1,23 +1,15 @@
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.smack.util.stringencoder.Base64;
import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureCreationMechanism;
import org.jivesoftware.smackx.ikey.IkeySignatureCreator;
import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureVerificationMechanism;
import org.jivesoftware.smackx.ikey.IkeySignatureVerifier;
import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureCreationMechanism;
import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureVerificationMechanism;
import org.jivesoftware.smackx.ikey.mechanism.IkeyType;
import org.jivesoftware.smackx.ikey.util.canonicalization.XmlSecElementCanonicalizer;
import org.jivesoftware.smackx.ikey_ox.OxIkeySignatureCreationMechanism;
import org.jivesoftware.smackx.ikey_ox.OxIkeySignatureVerificationMechanism;
import org.jivesoftware.smackx.util.MercurySmackTestSuite;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.impl.JidCreate;
@ -43,15 +35,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
public class IkeySignatureCreatorAndVerifierTest extends MercurySmackTestSuite {
@BeforeAll
public static void initialize() {
if (!Init.isInitialized()) {
Init.init();
}
}
@Test
public void createIkeyElementAndVerifySignature() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, URISyntaxException, XMLParserException, IOException, CanonicalizationException, InvalidCanonicalizerException {
public void createIkeyElementAndVerifySignature()
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, URISyntaxException, IOException {
EntityBareJid jid = JidCreate.entityBareFromOrThrowUnchecked("alice@wonderland.lit");
PGPKeyRing keyRing = PGPainless.generateKeyRing()
.withMasterKey(KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256))
@ -60,13 +46,12 @@ public class IkeySignatureCreatorAndVerifierTest extends MercurySmackTestSuite {
.withPrimaryUserId("xmpp:" + jid)
.withoutPassphrase()
.build();
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);
IkeySignatureCreator creator = new IkeySignatureCreator(signingMechanism);
IkeySignatureVerificationMechanism verificationMechanism = new OxIkeySignatureVerificationMechanism(keyRing.getPublicKeys());
IkeySignatureVerifier verifier = new IkeySignatureVerifier(verificationMechanism, elementCanonicalizer);
IkeySignatureVerifier verifier = new IkeySignatureVerifier(verificationMechanism);
OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(keyRing.getMasterKey());
SuperordinateElement superordinate = new SuperordinateElement(Base64.encodeToString(keyRing.getMasterKey().getEncoded()));
@ -77,7 +62,7 @@ public class IkeySignatureCreatorAndVerifierTest extends MercurySmackTestSuite {
SubordinateListElement subs = new SubordinateListElement(jid, new Date(), subList);
ProofElement proofElement = creator.createProofFor(subs);
IkeyElement ikeyElement = new IkeyElement(IkeyType.OX, superordinate, subs, proofElement);
IkeyElement ikeyElement = new IkeyElement(IkeyType.OX, superordinate, new SignedElement(subs), proofElement);
System.out.println(ikeyElement.toXML().toString());

View File

@ -1,47 +0,0 @@
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.util.canonicalization.XmlSecElementCanonicalizer;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class XmlSecElementCanonicalizerTest {
@BeforeAll
public static void initialize() {
if (!Init.isInitialized()) {
Init.init();
}
}
@Test
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'\n" +
" type='OX'>\n" +
" <subordinates jid='alice@wonderland.lit'>\n" +
" <sub uri=\"xmpp:alice@wonderland.lit?;node=urn:xmpp:openpgp:0:public-keys:34669E842617A0D38A96016B359160D0B0868569;item=2020-01-21T10:46:21Z\"\n" +
" fpr='34669E842617A0D38A96016B359160D0B0868569'/>\n" +
" </subordinates>\n" +
" <proof>iF4EABMIAAYFAl9I3esACgkQNZFg0LCGhWntNAD+LDO/Q+WQ5TrQOt4vBcqnUarCOZ6Ev4Wp4QgsIjs2BHcA/2BOIC6FBqkx80zB8NZsZu4H1fvn+gWgrscXhgf9+f+h</proof>\n" +
"</ikey>";
String can1 = canonicalizer.removeInterElementWhitespace(new String(canonicalizer.canonicalize(element), StandardCharsets.UTF_8));
String can2 = canonicalizer.removeInterElementWhitespace(new String(canonicalizer.canonicalize(elementWithInsignificantWhitespace), StandardCharsets.UTF_8));
assertEquals(can1, can2);
}
}

View File

@ -0,0 +1,42 @@
package org.jivesoftware.smackx.signed.xep0285;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.signed.xep0285.element.PlainElement;
import org.jivesoftware.smackx.signed.xep0285.provider.PlainElementProvider;
import org.jivesoftware.smackx.util.MercurySmackTestSuite;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class PlainElementTest extends MercurySmackTestSuite {
@Test
public void test() throws XmlPullParserException, IOException, SmackParsingException {
String plain = "<message xmlns=\"jabber:client\" " +
"from=\"juliet@capulet.net/balcony\" " +
"to=\"romeo@montegue.net\" " +
"type=\"chat\">" +
"<thread>c6373824-a307-40dd-8fe0-bad6e7299ad0</thread>" +
"<body>Wherefore art thou, Romeo?</body>" +
"</message>";
String example2 = "<plain xmlns=\"urn:xmpp:signed:0\" " +
"timestamp=\"2010-06-29T02:15:21.012+00:00\">" +
"PG1lc3NhZ2UgeG1sbnM9ImphYmJlcjpjbGllbnQiIGZyb209Imp1bGlldEBjYXB" +
"1bGV0Lm5ldC9iYWxjb255IiB0bz0icm9tZW9AbW9udGVndWUubmV0IiB0eXBlPS" +
"JjaGF0Ij48dGhyZWFkPmM2MzczODI0LWEzMDctNDBkZC04ZmUwLWJhZDZlNzI5O" +
"WFkMDwvdGhyZWFkPjxib2R5PldoZXJlZm9yZSBhcnQgdGhvdSwgUm9tZW8/PC9i" +
"b2R5PjwvbWVzc2FnZT4=" +
"</plain>";
PlainElement parsed = new PlainElementProvider()
.parse(getParser(example2));
PlainElement fromPlain = PlainElement.fromUtf8String(plain, ParserUtils.getDateFromXep82String("2010-06-29T02:15:21.012+00:00"));
assertEquals(parsed, fromPlain);
assertEquals(plain, parsed.getUtf8Content());
}
}

View File

@ -0,0 +1,40 @@
package org.jivesoftware.smackx.signed.xep0285;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.signed.xep0285.element.SignedElement;
import org.jivesoftware.smackx.signed.xep0285.provider.SignedElementProvider;
import org.jivesoftware.smackx.util.MercurySmackTestSuite;
import org.junit.jupiter.api.Test;
import java.io.IOException;
public class SignedElementTest extends MercurySmackTestSuite {
@Test
public void test() throws XmlPullParserException, IOException, SmackParsingException {
String example3 =
"<signed xmlns=\"urn:xmpp:signed:0\">" +
"<signature algorithm=\"RSA-SHA1\">" +
"DxbxIziY1C1Ytcxkj0IFLsfmDLMv96JMlMAQZ7jh49IbsOIPsxI2LyLmqhKH/994UXDJKQLHvLJz" +
"gAmw8V2b+zmyZeItJzSmB+HHiLFVXkD2Dd4JfetsafsfIcB7uNWg0gAeiKrTHfFgiyEC/2WxwOj3" +
"JUMRyQ9ykEPIzS0GZ/k=" +
"</signature>" +
"<data>" +
"PHBsYWluIHhtbG5zPSJ1cm46eG1wcDpzaWduZWQ6MCIgdGltZXN0YW1wPSIyMDEwLTA2LTI5VDAy" +
"OjE1OjIxLjAxMloiPgogIFBHMWxjM05oWjJVZ2VHMXNibk05SW1waFltSmxjanBqYkdsbGJuUWlJ" +
"R1p5YjIwOUltcDFiR2xsZEVCallYQgogIDFiR1YwTG01bGRDOWlZV3hqYjI1NUlpQjBiejBpY205" +
"dFpXOUFiVzl1ZEdWbmRXVXVibVYwSWlCMGVYQmxQUwogIEpqYUdGMElqNDhkR2h5WldGa1BtTTJN" +
"emN6T0RJMExXRXpNRGN0TkRCa1pDMDRabVV3TFdKaFpEWmxOekk1TwogIFdGa01Ed3ZkR2h5WldG" +
"a1BqeGliMlI1UGxkb1pYSmxabTl5WlNCaGNuUWdkR2h2ZFN3Z1VtOXRaVzgvUEM5aQogIGIyUjVQ" +
"and2YldWemMyRm5aVDQ9CjwvcGxhaW4+Cg==" +
"</data>" +
"</signed>";
SignedElement signedElement = new SignedElementProvider()
.parse(getParser(example3));
System.out.println(example3);
System.out.println(signedElement.toXML());
}
}

@ -1 +1 @@
Subproject commit 38bfe2492149cb095a1376080320e37c71832504
Subproject commit 1c822dcaa4d4cb92d8b2d048f49bb69885143d56