diff --git a/src/XML/ds/AbstractDSAKeyValueType.php b/src/XML/ds/AbstractDSAKeyValueType.php
new file mode 100644
index 00000000..bd5e1c51
--- /dev/null
+++ b/src/XML/ds/AbstractDSAKeyValueType.php
@@ -0,0 +1,207 @@
+y;
+ }
+
+
+ /**
+ * Get the G.
+ *
+ * @return \SimpleSAML\XMLSecurity\XML\ds\G
+ */
+ public function getG(): G
+ {
+ return $this->g;
+ }
+
+
+ /**
+ * Get the J.
+ *
+ * @return \SimpleSAML\XMLSecurity\XML\ds\J
+ */
+ public function getJ(): J
+ {
+ return $this->j;
+ }
+
+
+ /**
+ * Get the P.
+ *
+ * @return \SimpleSAML\XMLSecurity\XML\ds\P|null
+ */
+ public function getP(): ?P
+ {
+ return $this->p;
+ }
+
+
+ /**
+ * Get the Q.
+ *
+ * @return \SimpleSAML\XMLSecurity\XML\ds\Q|null
+ */
+ public function getQ(): ?Q
+ {
+ return $this->q;
+ }
+
+
+ /**
+ * Get the Seed.
+ *
+ * @return \SimpleSAML\XMLSecurity\XML\ds\Seed|null
+ */
+ public function getSeed(): ?Seed
+ {
+ return $this->seed;
+ }
+
+
+ /**
+ * Get the PgenCounter.
+ *
+ * @return \SimpleSAML\XMLSecurity\XML\ds\PgenCounter|null
+ */
+ public function getPgenCounter(): ?PgenCounter
+ {
+ return $this->pgenCounter;
+ }
+
+
+ /**
+ * Initialize an DSAKeyValue object from an existing XML.
+ *
+ * @param \DOMElement $xml
+ * @return static
+ *
+ * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
+ * if the qualified name of the supplied element is wrong
+ * @throws \SimpleSAML\XML\Exception\MissingAttributeException
+ * if the supplied element is missing one of the mandatory attributes
+ * @throws \SimpleSAML\XML\Exception\TooManyElementsException
+ * if too many child-elements of a type are specified
+ */
+ public static function fromXML(DOMElement $xml): static
+ {
+ Assert::same($xml->localName, 'DSAKeyValue', InvalidDOMElementException::class);
+ Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
+
+ $y = Y::getChildrenOfClass($xml);
+ Assert::minCount($y, 1, TooManyElementsException::class);
+ Assert::maxCount($y, 1, TooManyElementsException::class);
+
+ $g = G::getChildrenOfClass($xml);
+ Assert::maxCount($g, 1, TooManyElementsException::class);
+
+ $j = J::getChildrenOfClass($xml);
+ Assert::maxCount($j, 1, TooManyElementsException::class);
+
+ $p = P::getChildrenOfClass($xml);
+ Assert::maxCount($p, 1, TooManyElementsException::class);
+
+ $q = Q::getChildrenOfClass($xml);
+ Assert::maxCount($q, 1, TooManyElementsException::class);
+
+ $seed = Seed::getChildrenOfClass($xml);
+ Assert::maxCount($seed, 1, TooManyElementsException::class);
+
+ $pgenCounter = PgenCounter::getChildrenOfClass($xml);
+ Assert::maxCount($pgenCounter, 1, TooManyElementsException::class);
+
+ return new static(
+ array_pop($y),
+ array_pop($g),
+ array_pop($j),
+ array_pop($p),
+ array_pop($q),
+ array_pop($seed),
+ array_pop($pgenCounter),
+ );
+ }
+
+
+ /**
+ * Convert this DSAKeyValue object to XML.
+ *
+ * @param \DOMElement|null $parent The element we should append this DSAKeyValue to.
+ * @return \DOMElement
+ */
+ public function toXML(?DOMElement $parent = null): DOMElement
+ {
+ $e = $this->instantiateParentElement($parent);
+
+ $this->getP()?->toXML($e);
+ $this->getQ()?->toXML($e);
+ $this->getG()?->toXML($e);
+ $this->getY()->toXML($e);
+ $this->getJ()?->toXML($e);
+ $this->getSeed()?->toXML($e);
+ $this->getPgenCounter()?->toXML($e);
+
+ return $e;
+ }
+}
diff --git a/src/XML/ds/DSAKeyValue.php b/src/XML/ds/DSAKeyValue.php
new file mode 100644
index 00000000..38d347c4
--- /dev/null
+++ b/src/XML/ds/DSAKeyValue.php
@@ -0,0 +1,14 @@
+setContent($content);
+ }
+}
diff --git a/src/XML/ds/J.php b/src/XML/ds/J.php
new file mode 100644
index 00000000..22e724c3
--- /dev/null
+++ b/src/XML/ds/J.php
@@ -0,0 +1,26 @@
+setContent($content);
+ }
+}
diff --git a/src/XML/ds/P.php b/src/XML/ds/P.php
new file mode 100644
index 00000000..a5173b4c
--- /dev/null
+++ b/src/XML/ds/P.php
@@ -0,0 +1,26 @@
+setContent($content);
+ }
+}
diff --git a/src/XML/ds/PgenCounter.php b/src/XML/ds/PgenCounter.php
new file mode 100644
index 00000000..9210d1b8
--- /dev/null
+++ b/src/XML/ds/PgenCounter.php
@@ -0,0 +1,26 @@
+setContent($content);
+ }
+}
diff --git a/src/XML/ds/Q.php b/src/XML/ds/Q.php
new file mode 100644
index 00000000..3089f707
--- /dev/null
+++ b/src/XML/ds/Q.php
@@ -0,0 +1,26 @@
+setContent($content);
+ }
+}
diff --git a/src/XML/ds/Seed.php b/src/XML/ds/Seed.php
new file mode 100644
index 00000000..cf0713c9
--- /dev/null
+++ b/src/XML/ds/Seed.php
@@ -0,0 +1,26 @@
+setContent($content);
+ }
+}
diff --git a/src/XML/ds/Y.php b/src/XML/ds/Y.php
new file mode 100644
index 00000000..2d8b7daf
--- /dev/null
+++ b/src/XML/ds/Y.php
@@ -0,0 +1,26 @@
+setContent($content);
+ }
+}
diff --git a/tests/XML/ds/DSAKeyValueTest.php b/tests/XML/ds/DSAKeyValueTest.php
new file mode 100644
index 00000000..27d06611
--- /dev/null
+++ b/tests/XML/ds/DSAKeyValueTest.php
@@ -0,0 +1,100 @@
+assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($dsaKeyValue),
+ );
+ }
+
+
+ /**
+ */
+ public function testMarshallingElementOrder(): void
+ {
+ $p = new P('GpM1');
+ $q = new Q('GpM2');
+ $g = new G('GpM3');
+ $y = new Y('GpM4');
+ $j = new J('GpM5');
+ $seed = new Seed('GpM6');
+ $pgenCounter = new PgenCounter('GpM7');
+
+ $dsaKeyValue = new DSAKeyValue($y, $g, $j, $p, $q, $seed, $pgenCounter);
+
+ $dsaKeyValueElement = $dsaKeyValue->toXML();
+ /** @var \DOMElement[] $children */
+ $children = $dsaKeyValueElement->childNodes;
+
+ $this->assertEquals('ds:P', $children[0]->tagName);
+ $this->assertEquals('ds:Q', $children[1]->tagName);
+ $this->assertEquals('ds:G', $children[2]->tagName);
+ $this->assertEquals('ds:Y', $children[3]->tagName);
+ $this->assertEquals('ds:J', $children[4]->tagName);
+ $this->assertEquals('ds:Seed', $children[5]->tagName);
+ $this->assertEquals('ds:PgenCounter', $children[6]->tagName);
+ }
+}
diff --git a/tests/XML/ds/GTest.php b/tests/XML/ds/GTest.php
new file mode 100644
index 00000000..3c82fce4
--- /dev/null
+++ b/tests/XML/ds/GTest.php
@@ -0,0 +1,51 @@
+assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($g),
+ );
+ }
+}
diff --git a/tests/XML/ds/JTest.php b/tests/XML/ds/JTest.php
new file mode 100644
index 00000000..0dfe7076
--- /dev/null
+++ b/tests/XML/ds/JTest.php
@@ -0,0 +1,51 @@
+assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($j),
+ );
+ }
+}
diff --git a/tests/XML/ds/PTest.php b/tests/XML/ds/PTest.php
new file mode 100644
index 00000000..ecb1091c
--- /dev/null
+++ b/tests/XML/ds/PTest.php
@@ -0,0 +1,51 @@
+assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($p),
+ );
+ }
+}
diff --git a/tests/XML/ds/PgenCounterTest.php b/tests/XML/ds/PgenCounterTest.php
new file mode 100644
index 00000000..09178b72
--- /dev/null
+++ b/tests/XML/ds/PgenCounterTest.php
@@ -0,0 +1,51 @@
+assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($pgenCounter),
+ );
+ }
+}
diff --git a/tests/XML/ds/QTest.php b/tests/XML/ds/QTest.php
new file mode 100644
index 00000000..aace3a99
--- /dev/null
+++ b/tests/XML/ds/QTest.php
@@ -0,0 +1,51 @@
+assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($q),
+ );
+ }
+}
diff --git a/tests/XML/ds/SeedTest.php b/tests/XML/ds/SeedTest.php
new file mode 100644
index 00000000..f6ded14b
--- /dev/null
+++ b/tests/XML/ds/SeedTest.php
@@ -0,0 +1,51 @@
+assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($seed),
+ );
+ }
+}
diff --git a/tests/XML/ds/YTest.php b/tests/XML/ds/YTest.php
new file mode 100644
index 00000000..a1417763
--- /dev/null
+++ b/tests/XML/ds/YTest.php
@@ -0,0 +1,51 @@
+assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($y),
+ );
+ }
+}
diff --git a/tests/resources/xml/ds_DSAKeyValue.xml b/tests/resources/xml/ds_DSAKeyValue.xml
new file mode 100644
index 00000000..3b16168c
--- /dev/null
+++ b/tests/resources/xml/ds_DSAKeyValue.xml
@@ -0,0 +1,9 @@
+
+ GpM1
+ GpM2
+ GpM3
+ GpM4
+ GpM5
+ GpM6
+ GpM7
+
diff --git a/tests/resources/xml/ds_G.xml b/tests/resources/xml/ds_G.xml
new file mode 100644
index 00000000..da043e98
--- /dev/null
+++ b/tests/resources/xml/ds_G.xml
@@ -0,0 +1 @@
+GpM6
diff --git a/tests/resources/xml/ds_J.xml b/tests/resources/xml/ds_J.xml
new file mode 100644
index 00000000..f37e0756
--- /dev/null
+++ b/tests/resources/xml/ds_J.xml
@@ -0,0 +1 @@
+GpM6
diff --git a/tests/resources/xml/ds_P.xml b/tests/resources/xml/ds_P.xml
new file mode 100644
index 00000000..e61189b9
--- /dev/null
+++ b/tests/resources/xml/ds_P.xml
@@ -0,0 +1 @@
+GpM6
diff --git a/tests/resources/xml/ds_PgenCounter.xml b/tests/resources/xml/ds_PgenCounter.xml
new file mode 100644
index 00000000..6b0dda66
--- /dev/null
+++ b/tests/resources/xml/ds_PgenCounter.xml
@@ -0,0 +1 @@
+GpM6
diff --git a/tests/resources/xml/ds_Q.xml b/tests/resources/xml/ds_Q.xml
new file mode 100644
index 00000000..5f388348
--- /dev/null
+++ b/tests/resources/xml/ds_Q.xml
@@ -0,0 +1 @@
+GpM6
diff --git a/tests/resources/xml/ds_Seed.xml b/tests/resources/xml/ds_Seed.xml
new file mode 100644
index 00000000..6aa6c0fa
--- /dev/null
+++ b/tests/resources/xml/ds_Seed.xml
@@ -0,0 +1 @@
+GpM6
diff --git a/tests/resources/xml/ds_Y.xml b/tests/resources/xml/ds_Y.xml
new file mode 100644
index 00000000..3fae3718
--- /dev/null
+++ b/tests/resources/xml/ds_Y.xml
@@ -0,0 +1 @@
+GpM6