Migrate ikey away from xmlsec
This commit is contained in:
parent
58740c4a69
commit
be59c09b15
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
|
@ -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*<", "><");
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue