diff --git a/README-DEV.md b/README-DEV.md
index 42249a911..9c9acd7ca 100644
--- a/README-DEV.md
+++ b/README-DEV.md
@@ -11,8 +11,8 @@ Test with the PHPCS configuration in tools/phpcs/ruleset.xml
 Testing
 -------
 Use PHPUnit for Unit Testing.
-Test with the 2 known users: (SimpleSAMLphp)[http://www.simplesaml.org] and
-(OpenConext-engineblock)[http://www.openconext.org] .
+Test with the 2 known users: (SimpleSAMLphp)[https://www.simplesamlphp.org] and
+(OpenConext-engineblock)[https://www.openconext.org] .
 
 ### Using Tests in Development
 
diff --git a/src/SAML2/SOAPClient.php b/src/SAML2/SOAPClient.php
index a08753684..31622d099 100644
--- a/src/SAML2/SOAPClient.php
+++ b/src/SAML2/SOAPClient.php
@@ -34,6 +34,7 @@ public function send(Message $msg, SimpleSAML_Configuration $srcMetadata, Simple
         $ctxOpts = array(
             'ssl' => array(
                 'capture_peer_cert' => true,
+                'allow_self_signed' => true
             ),
         );
 
diff --git a/src/SAML2/Utilities/ArrayCollection.php b/src/SAML2/Utilities/ArrayCollection.php
index d1ce1fabd..ea7223989 100644
--- a/src/SAML2/Utilities/ArrayCollection.php
+++ b/src/SAML2/Utilities/ArrayCollection.php
@@ -38,7 +38,7 @@ public function filter(\Closure $f)
 
     public function set($key, $value)
     {
-        $this->elements[$value] = $key;
+        $this->elements[$key] = $value;
     }
 
     public function remove($element)
diff --git a/tests/SAML2/Certificate/FingerprintCollectionTest.php b/tests/SAML2/Certificate/FingerprintCollectionTest.php
new file mode 100644
index 000000000..57a21e5a9
--- /dev/null
+++ b/tests/SAML2/Certificate/FingerprintCollectionTest.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace SAML2\Certificate;
+
+class FingerprintCollectionTest extends \PHPUnit_Framework_TestCase
+{
+
+    /**
+     * @group certificate
+     * @test
+     */
+    public function add_fingerprint()
+    {
+        $fpr0 = new Fingerprint('00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00');
+        $fpr1 = new Fingerprint('00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:01');
+
+        $fpc = new FingerprintCollection();
+        // collection has none of the fingerprints
+        $this->assertFalse($fpc->contains($fpr0));
+        $this->assertFalse($fpc->contains($fpr1));
+
+        $fpc->add($fpr0);
+        // fingerprint 0 is present, 1 remains absent
+        $this->assertTrue($fpc->contains($fpr0));
+        $this->assertFalse($fpc->contains($fpr1));
+
+        $fpc->add($fpr0);
+        // adding an existing fingerprint is idempotent
+        $this->assertTrue($fpc->contains($fpr0));
+        $this->assertFalse($fpc->contains($fpr1));
+
+        $fpc->add($fpr1);
+        // both fingerprints are now present
+        $this->assertTrue($fpc->contains($fpr0));
+        $this->assertTrue($fpc->contains($fpr1));
+    }
+
+    /**
+     * @group certificate
+     * @test
+     *
+     * @expectedException \SAML2\Exception\InvalidArgumentException
+     */
+    public function fails_on_invalid_fingerprint_data()
+    {
+        $fpc = new FingerprintCollection();
+        $fpc->add('string');
+    }
+}
diff --git a/tests/SAML2/Certificate/KeyCollectionTest.php b/tests/SAML2/Certificate/KeyCollectionTest.php
new file mode 100644
index 000000000..ec3df278c
--- /dev/null
+++ b/tests/SAML2/Certificate/KeyCollectionTest.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace SAML2\Certificate;
+
+class KeyCollectionTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @group certificate
+     *
+     * @test
+     * @expectedException \SAML2\Exception\InvalidArgumentException
+     */
+    public function testKeyCollectionAddWrongType()
+    {
+        $kc = new KeyCollection();
+        $kc->add("not a key, just a string");
+    }
+}
diff --git a/tests/SAML2/Certificate/KeyTest.php b/tests/SAML2/Certificate/KeyTest.php
index 9f8afc76b..27023968c 100644
--- a/tests/SAML2/Certificate/KeyTest.php
+++ b/tests/SAML2/Certificate/KeyTest.php
@@ -17,6 +17,19 @@ public function invalid_key_usage_should_throw_an_exception()
         $key->canBeUsedFor('foo');
     }
 
+    /**
+     * @group certificate
+     *
+     * @test
+     * @expectedException \SAML2\Exception\InvalidArgumentException
+     */
+    public function invalid_offset_type_should_throw_an_exception()
+    {
+        $key = new Key(array(Key::USAGE_SIGNING => true));
+        $key->offsetGet(0);
+    }
+
+
     /**
      * @group certificate
      *
@@ -32,4 +45,33 @@ public function assert_that_key_usage_check_works_correctly()
         $key[Key::USAGE_ENCRYPTION] = false;
         $this->assertFalse($key->canBeUsedFor(Key::USAGE_ENCRYPTION));
     }
+
+    /**
+     * @group certificate
+     *
+     * @test
+     */
+    public function assert_that_offsetget_works_correctly()
+    {
+        $key = new Key(array(Key::USAGE_SIGNING => true));
+        $this->assertTrue($key->offsetGet(Key::USAGE_SIGNING));
+    }
+
+    /**
+     * @group certificate
+     *
+     * @test
+     */
+    public function assert_that_offsetunset_unsets_offset()
+    {
+        $key = new Key(array(Key::USAGE_SIGNING => true, Key::USAGE_ENCRYPTION => true));
+        $this->assertTrue($key->offsetExists(Key::USAGE_SIGNING));
+        $this->assertTrue($key->offsetExists(Key::USAGE_ENCRYPTION));
+        $key->offsetUnset(Key::USAGE_SIGNING);
+        $this->assertFalse($key->offsetExists(Key::USAGE_SIGNING));
+        $this->assertTrue($key->offsetExists(Key::USAGE_ENCRYPTION));
+        $key->offsetUnset(Key::USAGE_ENCRYPTION);
+        $this->assertFalse($key->offsetExists(Key::USAGE_SIGNING));
+        $this->assertFalse($key->offsetExists(Key::USAGE_ENCRYPTION));
+    }
 }
diff --git a/tests/SAML2/Certificate/PrivateKeyTest.php b/tests/SAML2/Certificate/PrivateKeyTest.php
new file mode 100644
index 000000000..0c859ee25
--- /dev/null
+++ b/tests/SAML2/Certificate/PrivateKeyTest.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace SAML2\Certificate;
+
+class PrivateKeyTest extends \PHPUnit_Framework_TestCase
+{
+
+    /**
+     * @group certificate
+     * @test
+     */
+    public function test_create_from_key()
+    {
+        $key = \SAML2\CertificatesMock::getPlainPrivateKey();
+
+        $pk_nopass = PrivateKey::create($key);
+        $this->assertEquals($key, $pk_nopass->getKeyAsString());
+
+        $pk_withpass = PrivateKey::create($key, "s3cr1t");
+        $this->assertEquals($key, $pk_withpass->getKeyAsString());
+        $this->assertEquals("s3cr1t", $pk_withpass->getPassphrase());
+    }
+
+    /**
+     * @group certificate
+     *
+     * @test
+     * @expectedException \SAML2\Exception\InvalidArgumentException
+     */
+    public function test_create_from_nonstring_throws_exception()
+    {
+        PrivateKey::create(0);
+    }
+
+    /**
+     * @group certificate
+     *
+     * @test
+     * @expectedException \SAML2\Exception\InvalidArgumentException
+     */
+    public function test_create_with_nonstring_password_throws_exception()
+    {
+        $key = \SAML2\CertificatesMock::getPlainPrivateKey();
+        PrivateKey::create($key, 1);
+    }
+}
diff --git a/tests/SAML2/CertificatesMock.php b/tests/SAML2/CertificatesMock.php
index 0e5730004..276568037 100644
--- a/tests/SAML2/CertificatesMock.php
+++ b/tests/SAML2/CertificatesMock.php
@@ -26,6 +26,8 @@ class CertificatesMock
 Q4/67OZfHd7R+POBXhophSMv1ZOo
 -----END CERTIFICATE-----';
 
+    const PUBLIC_KEY_PEM_CONTENTS = 'MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo';
+
     const PRIVATE_KEY_PEM = '-----BEGIN RSA PRIVATE KEY-----
 MIICXgIBAAKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9
 IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+
@@ -42,6 +44,33 @@ class CertificatesMock
 PQnYaNUN/Fy2SYtETXTb0CQ9X1rt8ffkFP7ya+5TC83aMg==
 -----END RSA PRIVATE KEY-----';
 
+    const PUBLIC_KEY_2_PEM = '-----BEGIN CERTIFICATE-----
+MIIEdjCCA16gAwIBAgIRALcDQnHscLnQAL4GULGaHsIwDQYJKoZIhvcNAQEFBQAw
+NjELMAkGA1UEBhMCTkwxDzANBgNVBAoTBlRFUkVOQTEWMBQGA1UEAxMNVEVSRU5B
+IFNTTCBDQTAeFw0wOTEwMjYwMDAwMDBaFw0xMjEwMjUyMzU5NTlaMFExCzAJBgNV
+BAYTAk5MMRUwEwYDVQQKEwxTVVJGbmV0IEIuVi4xETAPBgNVBAsTCFNlcnZpY2Vz
+MRgwFgYDVQQDEw9iZXRhLnN1cmZuZXQubmwwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQC7D6V4dl41AF70veMMQqL7kpk+p06qU9EW2YUuJAnwhRO1Sfbi
+qPweitLE7ReXfK/vSPvdFG194gZjJ4JS7M7pP00eolfEK6ljLhMoUrzbBuEc5LoQ
+B6HsOogod7YrJiVRXLzM4bV2LzbyjAwBgForoM4l576CjbN/NDAPO3YvbktodR5D
+H/nPiw/5L7w2KS3x55xUd8clY7Nji9W7XbviMUAugSkkC2ethl0AMpdmk6BI5pjy
+r6KCUrGz9bKl101yUGhrkHy1NpHagOoxakkgVDgi2Erf8aDnkCe8A419m2mBD1WA
+9uQWF5eMpiY8fDMwjwSFDFoSUlJPhiKN8vpnAgMBAAGjggFiMIIBXjAfBgNVHSME
+GDAWgBQMvZNoDPPeq6NJays3V0fqkOO57TAdBgNVHQ4EFgQUrCpeMIzTW5M13G4i
+b1B+yq7Fm60wDgYDVR0PAQH/BAQDAgWgMAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYw
+FAYIKwYBBQUHAwEGCCsGAQUFBwMCMBgGA1UdIAQRMA8wDQYLKwYBBAGyMQECAh0w
+OgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC50Y3MudGVyZW5hLm9yZy9URVJF
+TkFTU0xDQS5jcmwwbQYIKwYBBQUHAQEEYTBfMDUGCCsGAQUFBzAChilodHRwOi8v
+Y3J0LnRjcy50ZXJlbmEub3JnL1RFUkVOQVNTTENBLmNydDAmBggrBgEFBQcwAYYa
+aHR0cDovL29jc3AudGNzLnRlcmVuYS5vcmcwGgYDVR0RBBMwEYIPYmV0YS5zdXJm
+bmV0Lm5sMA0GCSqGSIb3DQEBBQUAA4IBAQC8iCICDbP4/8JTDeLPfo/n6roOvpMs
+teQt7X5oN2Ka1xgKflpBGqJO5o3PcnfP437kcLRTnp6XDyTfS4eyZdxCqCECR2Pb
+8nbULVFv9hyF6asIWUfbJ67CFcRIpcuaD5habSrg8+rT86DjKdtYQKwbKL+rNbOs
+g6/ROR7vJgbSqrBLraXvl8HDUq5+lSF/II4LHVzNM8TpQlMY4ynRP6GEjcNUTH3I
+FKPQk+NwBYQqJ83Uil/36kbXsHQ81o/Vp6it7tlvLBOP1EN9jNGUXZuAqvFphNkw
+EJpABx1x4ukY8bZVl6QzQ79P48oGxOaIy27/g1FVkGqRtA4UPABcn0sJ
+-----END CERTIFICATE-----';
+
     /**
      * @return XMLSecurityKey
      */
@@ -61,4 +90,50 @@ public static function getPrivateKey()
         $privateKey->loadKey(self::PRIVATE_KEY_PEM);
         return $privateKey;
     }
+
+    /**
+     * @return XMLSecurityKey
+     */
+    public static function getPublicKeySha1()
+    {
+        $publicKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'public'));
+        $publicKey->loadKey(self::PUBLIC_KEY_PEM);
+        return $publicKey;
+    }
+
+    /**
+     * @return XMLSecurityKey
+     */
+    public static function getPublicKey2Sha1()
+    {
+        $publicKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'public'));
+        $publicKey->loadKey(self::PUBLIC_KEY_2_PEM);
+        return $publicKey;
+    }
+
+    public static function getPlainPublicKey()
+    {
+        return self::PUBLIC_KEY_PEM;
+    }
+
+    public static function getPlainPrivateKey()
+    {
+        return self::PRIVATE_KEY_PEM;
+    }
+
+    /**
+     * Returns just the certificate contents without the begin and end markings
+     */
+    public static function getPlainPublicKeyContents()
+    {
+        return self::PUBLIC_KEY_PEM_CONTENTS;
+    }
+
+    /**
+     * Returns malformed public key by truncating it.
+     */
+    public static function getPlainInvalidPublicKey()
+    {
+        return substr(self::PUBLIC_KEY_PEM,200);
+    }
 }
diff --git a/tests/SAML2/DOMDocumentFactoryTest.php b/tests/SAML2/DOMDocumentFactoryTest.php
index b97a6dba6..794375249 100644
--- a/tests/SAML2/DOMDocumentFactoryTest.php
+++ b/tests/SAML2/DOMDocumentFactoryTest.php
@@ -46,7 +46,7 @@ public function testXmlStringIsCorrectlyLoaded()
      */
     public function testOnlyAStringIsAcceptedByFromFile($argument)
     {
-        DOMDocumentFactory::fromString($argument);
+        DOMDocumentFactory::fromFile($argument);
     }
 
     /**
diff --git a/tests/SAML2/HTTPPostTest.php b/tests/SAML2/HTTPPostTest.php
new file mode 100644
index 000000000..1c91ec57f
--- /dev/null
+++ b/tests/SAML2/HTTPPostTest.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace SAML2;
+
+use PHPUnit_Framework_Error_Warning;
+use PHPUnit_Framework_TestCase;
+
+class HTTPPostTest extends PHPUnit_Framework_TestCase
+{
+    /**
+     * test parsing of basic query string with authnrequest and
+     * verify that the correct issuer is found.
+     */
+    public function testRequestParsing()
+    {
+        $_POST = array();
+        $_POST['SAMLRequest'] = 'PHNhbWxwOkF1dGhuUmVxdWVzdCB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iQ09SVE9lZjI3YWE5NWM2ZjAzZTMyMjhhZDQyNDU0MWEzMWY1ZWUxMDY5ZmM0IiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNS0xMi0zMVQxMTo1OToyMVoiIERlc3RpbmF0aW9uPSJodHRwczovL3Roa2ktc2lkLnB0LTQ4LnV0ci5zdXJmY2xvdWQubmwvc3NwL3NhbWwyL2lkcC9TU09TZXJ2aWNlLnBocCIgQXNzZXJ0aW9uQ29uc3VtZXJTZXJ2aWNlVVJMPSJodHRwczovL2VuZ2luZS50ZXN0LnN1cmZjb25leHQubmwvYXV0aGVudGljYXRpb24vc3AvY29uc3VtZS1hc3NlcnRpb24iIFByb3RvY29sQmluZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmJpbmRpbmdzOkhUVFAtUE9TVCI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vZW5naW5lLnRlc3Quc3VyZmNvbmV4dC5ubC9hdXRoZW50aWNhdGlvbi9zcC9tZXRhZGF0YTwvc2FtbDpJc3N1ZXI+PHNhbWxwOk5hbWVJRFBvbGljeSBBbGxvd0NyZWF0ZT0idHJ1ZSIvPjxzYW1scDpTY29waW5nIFByb3h5Q291bnQ9IjEwIj48c2FtbHA6UmVxdWVzdGVySUQ+aHR0cHM6Ly90aGtpLXNpZC5wdC00OC51dHIuc3VyZmNsb3VkLm5sL21lbGxvbi9tZXRhZGF0YTwvc2FtbHA6UmVxdWVzdGVySUQ+PC9zYW1scDpTY29waW5nPjwvc2FtbHA6QXV0aG5SZXF1ZXN0Pg==';
+
+        $hp = new HTTPPost();
+        $request = $hp->receive();
+        $this->assertInstanceOf('SAML2\AuthnRequest', $request);
+        $issuer = $request->getIssuer();
+        $this->assertEquals('https://engine.test.surfconext.nl/authentication/sp/metadata', $issuer);
+    }
+
+    /**
+     * test parsing of SAMLResponse in post.
+     * verify that the correct issuer is found.
+     * verify that relaystate is found.
+     */
+    public function testResponseParsing()
+    {
+        $_POST = array();
+        $_POST['SAMLResponse'] = 'PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJDT1JUT2JjODA4OGRkNjA2YmQ4ZGQ1NDMwMzQ1ZGIxZjc2YWY1Njk1YzkzMzIiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDE1LTEyLTMxVDExOjQ4OjEyWiIgRGVzdGluYXRpb249Imh0dHBzOi8vdGhraS1zaWQucHQtNDgudXRyLnN1cmZjbG91ZC5ubC9tZWxsb24vcG9zdFJlc3BvbnNlIiBJblJlc3BvbnNlVG89Il83Mjc1NUI3MkRBQTJDQjY3M0ExQ0YxNURCMkQ1OTA5QiI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vZW5naW5lLnRlc3Quc3VyZmNvbmV4dC5ubC9hdXRoZW50aWNhdGlvbi9pZHAvbWV0YWRhdGE8L3NhbWw6SXNzdWVyPjxzYW1scDpTdGF0dXM+PHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPjxzYW1sOkFzc2VydGlvbiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIElEPSJDT1JUTzNmZjNjNzEzZmM0NGVkMWI5ZWM4MzkzMWEwZWVhNjlmNGRmOWY1MGQiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDE1LTEyLTMxVDExOjQ4OjEyWiI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vZW5naW5lLnRlc3Quc3VyZmNvbmV4dC5ubC9hdXRoZW50aWNhdGlvbi9pZHAvbWV0YWRhdGE8L3NhbWw6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPgogIDxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+CiAgICA8ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3JzYS1zaGExIi8+CiAgPGRzOlJlZmVyZW5jZSBVUkk9IiNDT1JUTzNmZjNjNzEzZmM0NGVkMWI5ZWM4MzkzMWEwZWVhNjlmNGRmOWY1MGQiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+PGRzOkRpZ2VzdFZhbHVlPkNHekdBNXBKQlN6VkRZOHpTbGplcEhRdEp5ND08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+R2VKVVQ2WjllRENpSTVLVVYzU2FvMHhHL2hnZUI0bk5JcHJvMzYzSElPNm1aYkFMVVBuaVQrS212OFMwOUFDc0ppS0k5MVhidjkvbkVJMGxzVy82aG5teEl6WFFKamU5eTlxcnBhSGtZS203S01oanNRYWhjTWFxMDBVNlNtYnY1RDhNd3ZDak1xWVU3eEVzUmkzejdnTmthT1E1V0Q5dCtUUEMxZGVoc2JnPTwvZHM6U2lnbmF0dXJlVmFsdWU+CjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUMrekNDQW1TZ0F3SUJBZ0lKQVBKdkxqUXNSUjRpTUEwR0NTcUdTSWIzRFFFQkJRVUFNRjB4Q3pBSkJnTlZCQVlUQWs1TU1SQXdEZ1lEVlFRSUV3ZFZkSEpsWTJoME1SQXdEZ1lEVlFRSEV3ZFZkSEpsWTJoME1SQXdEZ1lEVlFRS0V3ZFRWVkpHYm1WME1SZ3dGZ1lEVlFRREV3OTBaWE4wTWlCellXMXNJR05sY25Rd0hoY05NVFV3TXpJME1UUXdNekUzV2hjTk1qVXdNekl4TVRRd016RTNXakJkTVFzd0NRWURWUVFHRXdKT1RERVFNQTRHQTFVRUNCTUhWWFJ5WldOb2RERVFNQTRHQTFVRUJ4TUhWWFJ5WldOb2RERVFNQTRHQTFVRUNoTUhVMVZTUm01bGRERVlNQllHQTFVRUF4TVBkR1Z6ZERJZ2MyRnRiQ0JqWlhKME1JR2ZNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0R05BRENCaVFLQmdRQ3hLWXJuVzhOd3FkUndSd2FIdWx1ZUw2OWdRRlRKYmRmb0FTTGhZaWUyYk9pb0p5SncxZitjQXZwanNsNFNWWXB2RXNlSWV0WEg4TGdwazdLNzNQa0RKTDhkRmc0c0VqRkY2amdtanJPRVMzb3gvZ3RUZDlkL1Z3UEhJL3ZQSWNHeTFzYlZKNHBFTUZsNWQ4R09Bem9Ka05XZFBqOXdWNHJ2NHV2MzVNYXk4d0lEQVFBQm80SENNSUcvTUIwR0ExVWREZ1FXQkJUVElhUHdwS3BKbFk4ZUlNU3pOUlpValN0YmlqQ0Jqd1lEVlIwakJJR0hNSUdFZ0JUVElhUHdwS3BKbFk4ZUlNU3pOUlpValN0YmlxRmhwRjh3WFRFTE1Ba0dBMVVFQmhNQ1Rrd3hFREFPQmdOVkJBZ1RCMVYwY21WamFIUXhFREFPQmdOVkJBY1RCMVYwY21WamFIUXhFREFPQmdOVkJBb1RCMU5WVWtadVpYUXhHREFXQmdOVkJBTVREM1JsYzNReUlITmhiV3dnWTJWeWRJSUpBUEp2TGpRc1JSNGlNQXdHQTFVZEV3UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUZCUUFEZ1lFQUs4RXZUVTBMZ0hKc1N1Z29yT2VtZ1JscHBNZkpBZU9tdXVaTmhTTVkyUWh1bUZPWnBhQWI4TkZJd1VLVVZ5eUpuU283azZrdEhDS0k5NHNRczk3NjI0MmhURERZRXdXSkQ5SGhBc0FxT28yMVVhOGdaVDM4L3dtNjJlM0tncktYdm5sakFiS1BYRFhKTTRha3o3eTZINnd2dklHVDZmMGYwaUpXSHEzNGp3dz08L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OnRyYW5zaWVudCI+ZjhjMWRmMDc0Y2IxMTVhOTllYWNlOGI1OWNlZjVjMzk5YjRhNWFhNDwvc2FtbDpOYW1lSUQ+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAxNS0xMi0zMVQxMTo1MzoxMloiIFJlY2lwaWVudD0iaHR0cHM6Ly90aGtpLXNpZC5wdC00OC51dHIuc3VyZmNsb3VkLm5sL21lbGxvbi9wb3N0UmVzcG9uc2UiIEluUmVzcG9uc2VUbz0iXzcyNzU1QjcyREFBMkNCNjczQTFDRjE1REIyRDU5MDlCIi8+PC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sOlN1YmplY3Q+PHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTUtMTItMzFUMTE6NDg6MTFaIiBOb3RPbk9yQWZ0ZXI9IjIwMTUtMTItMzFUMTE6NTM6MTJaIj48c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sOkF1ZGllbmNlPmh0dHBzOi8vdGhraS1zaWQucHQtNDgudXRyLnN1cmZjbG91ZC5ubC9tZWxsb24vbWV0YWRhdGE8L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE1LTEyLTMxVDExOjQ3OjUwWiI+PHNhbWw6QXV0aG5Db250ZXh0PjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjxzYW1sOkF1dGhlbnRpY2F0aW5nQXV0aG9yaXR5Pmh0dHA6Ly9tb2NrLWlkcDwvc2FtbDpBdXRoZW50aWNhdGluZ0F1dGhvcml0eT48c2FtbDpBdXRoZW50aWNhdGluZ0F1dGhvcml0eT5odHRwOi8vbW9jay1pZHA8L3NhbWw6QXV0aGVudGljYXRpbmdBdXRob3JpdHk+PC9zYW1sOkF1dGhuQ29udGV4dD48L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+PC9zYW1sOkFzc2VydGlvbj48L3NhbWxwOlJlc3BvbnNlPg==';
+        $_POST['RelayState'] = 'relaystate001';
+
+        $hp = new HTTPPost();
+        $response = $hp->receive();
+        $this->assertInstanceOf('SAML2\Response', $response);
+        $issuer = $response->getIssuer();
+        $this->assertEquals('https://engine.test.surfconext.nl/authentication/idp/metadata', $issuer);
+        $relay = $response->getRelayState();
+        $this->assertEquals('relaystate001', $relay);
+    }
+
+
+    /**
+     * test parsing a request that contains no SAMLRequest or Response
+     * must generate an exception.
+     */
+    public function testNoRequestParsing()
+    {
+        $_POST = array();
+        $_POST = array('non' => 'sense');
+        $hp = new HTTPPost();
+        $this->setExpectedException('Exception', 'Missing SAMLRequest or SAMLResponse parameter');
+        $msg = $hp->receive();
+    }
+
+    /**
+     * Construct an authnrequest and send it.
+     */
+    public function testSendAuthnrequest()
+    {
+        $request = new AuthnRequest();
+        $hp = new HTTPPost();
+        $hp->send($request);
+    }
+
+    /**
+     * Construct an authnresponse and send it.
+     * Also test setting a relaystate and destination for the response.
+     */
+    public function testSendAuthnResponse()
+    {
+        $response = new Response();
+        $response->setIssuer('testIssuer');
+        $response->setRelayState('http://example.org');
+        $response->setDestination('http://example.org/login?success=yes');
+        $response->setSignatureKey(CertificatesMock::getPrivateKey());
+        $hr = new HTTPPost();
+        $hr->send($response);
+    }
+}
diff --git a/tests/SAML2/HTTPRedirectTest.php b/tests/SAML2/HTTPRedirectTest.php
index 3145be9c7..c1ecc0b1c 100644
--- a/tests/SAML2/HTTPRedirectTest.php
+++ b/tests/SAML2/HTTPRedirectTest.php
@@ -70,6 +70,44 @@ public function testSignedRequestParsing()
         $this->assertEquals('https://beta.surfnet.nl/simplesaml/module.php/core/authenticate.php?as=Braindrops', $relaystate);
     }
 
+    /**
+     * Test validating a signed authentication request.
+     */
+    public function testSignedRequestValidation()
+    {
+        $qs = 'SAMLRequest=nVLBauMwEP0Vo7sjW7FpKpJA2rBsoNuGOruHXhZFHm8EsuRqxtv27yvbWWgvYelFgjfvzbx5zBJVazu56enkHuG5B6TktbUO5VhYsT446RUalE61gJK0rDY%2F7qSYZbILnrz2ln2QXFYoRAhkvGPJbrtiv7VoygJEoTJ9LOusXDSFuJ4vdH6cxwoIEGUjsrqoFUt%2BQcCoXLHYKMoRe9g5JOUoQlleprlI8%2FyQz6W4ksXiiSXbuI1xikbViahDyfkRSM2wD40DmjnL0bSdhcE6Hx7BTd3xqnqoIPw1GmbdqWPJNx80jCGtGIUeWLL5t8mtd9i3EM78n493%2FzWr9XVvx%2B58mj39IlUaR%2FQmKOPq4Dtkyf4c9E1EjPtzOePjREL5%2FXDYp%2FuH6sDWy6G3HDML66%2B5ayO7VlHx2dySf2y9nM7pPprabffeGv02ZNcquux5QEydNiNVUlAODTiKMVvrX24DKIJz8nw9jfx8tOt3&RelayState=https%3A%2F%2Fbeta.surfnet.nl%2Fsimplesaml%2Fmodule.php%2Fcore%2Fauthenticate.php%3Fas%3DBraindrops&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1&Signature=b%2Bqe%2FXGgICOrEL1v9dwuoy0RJtJ%2FGNAr7gJGYSJzLG0riPKwo7v5CH8GPC2P9IRikaeaNeQrnhBAaf8FCWrO0cLFw4qR6msK9bxRBGk%2BhIaTUYCh54ETrVCyGlmBneMgC5%2FiCRvtEW3ESPXCCqt8Ncu98yZmv9LIVyHSl67Se%2BfbB9sDw3%2FfzwYIHRMqK2aS8jnsnqlgnBGGOXqIqN3%2Bd%2F2dwtCfz14s%2F9odoYzSUv32qfNPiPez6PSNqwhwH7dWE3TlO%2FjZmz0DnOeQ2ft6qdZEi5ZN5KCV6VmNKpkrLMq6DDPnuwPm%2F8oCAoT88R2jG7uf9QZB%2BArWJKMEhDLsCA%3D%3D';
+        $_SERVER['QUERY_STRING'] = $qs;
+
+        $hr = new HTTPRedirect();
+        $request = $hr->receive();
+
+        // validate with the correct certificate, should verify
+        $result = $request->validate(CertificatesMock::getPublicKey2Sha1());
+        $this->assertTrue($result);
+
+        // validate with another cert, should fail
+        $this->setExpectedException('Exception', 'Unable to validate signature');
+        $result = $request->validate(CertificatesMock::getPublicKeySha1());
+    }
+
+    /**
+     * Test validating a signed authentication request.
+     */
+    public function testSignedRequestValidationWrongKeytype()
+    {
+        $qs = 'SAMLRequest=nVLBauMwEP0Vo7sjW7FpKpJA2rBsoNuGOruHXhZFHm8EsuRqxtv27yvbWWgvYelFgjfvzbx5zBJVazu56enkHuG5B6TktbUO5VhYsT446RUalE61gJK0rDY%2F7qSYZbILnrz2ln2QXFYoRAhkvGPJbrtiv7VoygJEoTJ9LOusXDSFuJ4vdH6cxwoIEGUjsrqoFUt%2BQcCoXLHYKMoRe9g5JOUoQlleprlI8%2FyQz6W4ksXiiSXbuI1xikbViahDyfkRSM2wD40DmjnL0bSdhcE6Hx7BTd3xqnqoIPw1GmbdqWPJNx80jCGtGIUeWLL5t8mtd9i3EM78n493%2FzWr9XVvx%2B58mj39IlUaR%2FQmKOPq4Dtkyf4c9E1EjPtzOePjREL5%2FXDYp%2FuH6sDWy6G3HDML66%2B5ayO7VlHx2dySf2y9nM7pPprabffeGv02ZNcquux5QEydNiNVUlAODTiKMVvrX24DKIJz8nw9jfx8tOt3&RelayState=https%3A%2F%2Fbeta.surfnet.nl%2Fsimplesaml%2Fmodule.php%2Fcore%2Fauthenticate.php%3Fas%3DBraindrops&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1&Signature=b%2Bqe%2FXGgICOrEL1v9dwuoy0RJtJ%2FGNAr7gJGYSJzLG0riPKwo7v5CH8GPC2P9IRikaeaNeQrnhBAaf8FCWrO0cLFw4qR6msK9bxRBGk%2BhIaTUYCh54ETrVCyGlmBneMgC5%2FiCRvtEW3ESPXCCqt8Ncu98yZmv9LIVyHSl67Se%2BfbB9sDw3%2FfzwYIHRMqK2aS8jnsnqlgnBGGOXqIqN3%2Bd%2F2dwtCfz14s%2F9odoYzSUv32qfNPiPez6PSNqwhwH7dWE3TlO%2FjZmz0DnOeQ2ft6qdZEi5ZN5KCV6VmNKpkrLMq6DDPnuwPm%2F8oCAoT88R2jG7uf9QZB%2BArWJKMEhDLsCA%3D%3D';
+        $_SERVER['QUERY_STRING'] = $qs;
+
+        $hr = new HTTPRedirect();
+        $request = $hr->receive();
+
+        // validate with wrong type of cert
+        $this->setExpectedException('Exception', 'Invalid key type for validating signature');
+        $result = $request->validate(CertificatesMock::getPublicKey());
+    }
+
+
+
     /**
      * test that a request with unsupported encoding specified fails
      */
@@ -142,6 +180,7 @@ public function testSendAuthnResponse()
         $response->setIssuer('testIssuer');
         $response->setRelayState('http://example.org');
         $response->setDestination('http://example.org/login?success=yes');
+        $response->setSignatureKey(CertificatesMock::getPrivateKey());
         $hr = new HTTPRedirect();
         $hr->send($response);
     }
diff --git a/tests/SAML2/StatusResponseTest.php b/tests/SAML2/StatusResponseTest.php
index 3bffa0a42..43761828f 100644
--- a/tests/SAML2/StatusResponseTest.php
+++ b/tests/SAML2/StatusResponseTest.php
@@ -33,4 +33,159 @@ public function testMarshalling()
         $this->assertCount(1, $statusMessageElements);
         $this->assertEquals('OurMessageText', $statusMessageElements[0]->textContent);
     }
+
+    public function testUnmarshalling()
+    {
+        $xml = <<<XML
+<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
+                xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
+                ID="s2a0da3504aff978b0f8c80f6a62c713c4a2f64c5b"
+                InResponseTo="_bec424fa5103428909a30ff1e31168327f79474984"
+                Version="2.0"
+                IssueInstant="2007-12-10T11:39:48Z"
+                Destination="http://somewhere.example.org/simplesaml/saml2/sp/AssertionConsumerService.php">
+    <saml:Issuer>max.example.org</saml:Issuer>
+    <samlp:Status>
+        <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Responder" />
+        <samlp:StatusMessage>Something is wrong...</samlp:StatusMessage>
+    </samlp:Status>
+</samlp:Response>
+XML;
+
+        $fixtureResponseDom = DOMDocumentFactory::fromString($xml);
+        $response           = new Response($fixtureResponseDom->firstChild);
+
+        $this->assertFalse($response->isSuccess());
+
+        $status = $response->getStatus();
+        $this->assertEquals("urn:oasis:names:tc:SAML:2.0:status:Responder", $status['Code']);
+        $this->assertNull($status['SubCode']);
+        $this->assertEquals("Something is wrong...", $status['Message']);
+
+        $this->assertEquals("_bec424fa5103428909a30ff1e31168327f79474984", $response->getInResponseTo());
+    }
+
+    /**
+     * A status reponse that is not an error
+     */
+    public function testStatusSuccess()
+    {
+        $xml = <<<XML
+<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
+                xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
+                ID="s2a0da3504aff978b0f8c80f6a62c713c4a2f64c5b"
+                InResponseTo="_bec424fa5103428909a30ff1e31168327f79474984"
+                Version="2.0"
+                IssueInstant="2007-12-10T11:39:48Z"
+                Destination="http://somewhere.example.org/simplesaml/saml2/sp/AssertionConsumerService.php">
+    <saml:Issuer>max.example.org</saml:Issuer>
+    <samlp:Status>
+        <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
+    </samlp:Status>
+</samlp:Response>
+XML;
+
+        $fixtureResponseDom = DOMDocumentFactory::fromString($xml);
+        $response           = new Response($fixtureResponseDom->firstChild);
+
+        $this->assertTrue($response->isSuccess());
+
+        $status = $response->getStatus();
+        $this->assertEquals("urn:oasis:names:tc:SAML:2.0:status:Success", $status['Code']);
+        $this->assertNull($status['SubCode']);
+        $this->assertNull($status['Message']);
+    }
+
+    /**
+     * See if we can parse a StatusResponse with a subcode
+     */
+    public function testStatusSubcode()
+    {
+        $xml = <<<XML
+<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
+                xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
+                ID="id-HW1q4BUkjB3GuVvTlqYtwr1cuuI-"
+                Version="2.0"
+                IssueInstant="2016-01-20T20:28:02Z"
+                Destination="https://engine.example.edu/authentication/sp/consume-assertion"
+                InResponseTo="CORTO8a275030e97351e68e7cc0f89d5b46393d9ee3d9">
+  <saml:Issuer>https://example.org/</saml:Issuer>
+  <samlp:Status>
+    <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Requester">
+      <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:RequestDenied"/>
+    </samlp:StatusCode>
+    <samlp:StatusMessage>The AuthnRequest could not be validated</samlp:StatusMessage>
+  </samlp:Status>
+</samlp:Response>
+XML;
+
+        $fixtureResponseDom = DOMDocumentFactory::fromString($xml);
+        $response           = new Response($fixtureResponseDom->firstChild);
+
+        $this->assertFalse($response->isSuccess());
+
+        $status = $response->getStatus();
+        $this->assertEquals("urn:oasis:names:tc:SAML:2.0:status:Requester", $status['Code']);
+        $this->assertEquals("urn:oasis:names:tc:SAML:2.0:status:RequestDenied", $status['SubCode']);
+        $this->assertEquals("The AuthnRequest could not be validated", $status['Message']);
+    }
+
+    /**
+     * Test adding in-response-to to a status message.
+     */
+    public function testResponseTo()
+    {
+        $response = new Response();
+        $response->setIssueInstant(1453323439);
+        $response->setStatus(array(
+            'Code' => 'OurStatusCode'
+        ));
+        $response->setInResponseTo('aabb12234');
+
+        $responseElement = $response->toUnsignedXML();
+
+        $expectedStructureDocument = new \DOMDocument();
+        $expectedStructureDocument->loadXML(<<<STATUSXML
+<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
+                xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
+                ID="123"
+                Version="2.0"
+                IssueInstant="2016-01-20T20:57:19Z"
+                InResponseTo="aabb12234">
+  <samlp:Status>
+    <samlp:StatusCode Value="OurStatusCode"/>
+  </samlp:Status>
+</samlp:Response>
+STATUSXML
+       );
+       $expectedStructure = $expectedStructureDocument->documentElement;
+       $this->assertEqualXMLStructure($expectedStructure, $responseElement);
+    }
+
+    /**
+     * StatusCode is required in a StatusResponse.
+     */
+    public function testNoStatusCodeThrowsException()
+    {
+        $this->setExpectedException('Exception', 'Missing status code');
+
+        $xml = <<<XML
+<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
+                xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
+                ID="s2a0da3504aff978b0f8c80f6a62c713c4a2f64c5b"
+                InResponseTo="_bec424fa5103428909a30ff1e31168327f79474984"
+                Version="2.0"
+                IssueInstant="2007-12-10T11:39:48Z"
+                Destination="http://somewhere.example.org/simplesaml/saml2/sp/AssertionConsumerService.php">
+    <saml:Issuer>max.example.org</saml:Issuer>
+    <samlp:Status>
+        <samlp:StatusMessage>Something is wrong...</samlp:StatusMessage>
+    </samlp:Status>
+</samlp:Response>
+XML;
+
+        $fixtureResponseDom = DOMDocumentFactory::fromString($xml);
+        $response           = new Response($fixtureResponseDom->firstChild);
+    }
+
 }
diff --git a/tests/SAML2/Utilities/ArrayCollectionTest.php b/tests/SAML2/Utilities/ArrayCollectionTest.php
new file mode 100644
index 000000000..3263787c0
--- /dev/null
+++ b/tests/SAML2/Utilities/ArrayCollectionTest.php
@@ -0,0 +1,134 @@
+<?php
+
+namespace SAML2\Utilities;
+
+class ArrayCollectionTest extends \PHPUnit_Framework_TestCase
+{
+
+    public function test_construct_get_add_set()
+    {
+        $arc = new ArrayCollection(array('aap', 'aap', 'noot'));
+
+        $this->assertEquals($arc->get(0), 'aap');
+        $this->assertEquals($arc->get(1), 'aap');
+        $this->assertEquals($arc->get(2), 'noot');
+        $this->assertNull($arc->get(3));
+
+        $arc->add('mies');
+        $this->assertEquals($arc->get(0), 'aap');
+        $this->assertEquals($arc->get(1), 'aap');
+        $this->assertEquals($arc->get(2), 'noot');
+        $this->assertEquals($arc->get(3), 'mies');
+
+        $arc->set(1, 'mies');
+        $this->assertEquals($arc->get(0), 'aap');
+        $this->assertEquals($arc->get(1), 'mies');
+        $this->assertEquals($arc->get(2), 'noot');
+        $this->assertEquals($arc->get(3), 'mies');
+    }
+
+    public function test_remove()
+    {
+        $arc = new ArrayCollection(array('aap', 'aap', 'noot', 'mies'));
+
+        $removed = $arc->remove('noot');
+        $this->assertEquals('noot', $removed);
+        $this->assertEquals($arc->get(0), 'aap');
+        $this->assertEquals($arc->get(1), 'aap');
+        $this->assertEquals($arc->get(2), null);
+        $this->assertEquals($arc->get(3), 'mies');
+
+        $removed = $arc->remove('wim');
+        $this->assertFalse($removed);
+        $this->assertEquals($arc->get(0), 'aap');
+        $this->assertEquals($arc->get(1), 'aap');
+        $this->assertEquals($arc->get(2), null);
+        $this->assertEquals($arc->get(3), 'mies');
+
+        $removed = $arc->remove('aap');
+        $this->assertEquals('aap', $removed);
+        $this->assertEquals($arc->get(0), null);
+        $this->assertEquals($arc->get(1), 'aap');
+        $this->assertEquals($arc->get(2), null);
+        $this->assertEquals($arc->get(3), 'mies');
+
+        $removed = $arc->remove('aap');
+        $this->assertEquals('aap', $removed);
+        $this->assertEquals($arc->get(0), null);
+        $this->assertEquals($arc->get(1), null);
+        $this->assertEquals($arc->get(2), null);
+        $this->assertEquals($arc->get(3), 'mies');
+    }
+
+    public function test_first_last_count()
+    {
+        $arc = new ArrayCollection(array('aap', 'aap', 'noot', 'mies'));
+
+        $this->assertEquals($arc->first(), 'aap');
+        $this->assertEquals($arc->last(), 'mies');
+        $this->assertEquals($arc->count(), 4);
+    }
+
+    public function test_offset()
+    {
+        $arc = new ArrayCollection(array('aap', 'aap', 'noot', 'mies'));
+
+        $this->assertTrue($arc->offsetExists(0));
+        $this->assertTrue($arc->offsetExists(1));
+        $this->assertFalse($arc->offsetExists(4));
+
+        $this->assertEquals($arc->offsetGet(0), 'aap');
+        $this->assertEquals($arc->offsetGet(2), 'noot');
+
+        $arc->offsetSet(2, 'zus');
+        $this->assertEquals($arc->offsetGet(2), 'zus');
+
+        $arc->offsetUnset(0);
+        $this->assertFalse($arc->offsetExists(0));
+        $this->assertTrue($arc->offsetExists(1));
+    }
+
+    public function test_onlyelement()
+    {
+        $arc = new ArrayCollection(array('aap'));
+        $this->assertEquals($arc->getOnlyElement(), 'aap');
+    }
+
+    public function test_onlyelement_fail()
+    {
+        $arc = new ArrayCollection(array('aap', 'noot'));
+        $this->setExpectedException('SAML2\Exception\RuntimeException', 'SAML2\Utilities\ArrayCollection::SAML2\Utilities\ArrayCollection::getOnlyElement requires that the collection has exactly one element, "2" elements found');
+        $arc->getOnlyElement();
+    }
+
+    public function test_getiterator()
+    {
+        $arc = new ArrayCollection(array('aap', 'noot'));
+        $this->assertInstanceOf('\ArrayIterator', $arc->getIterator());
+    }
+
+    public function test_filter_map()
+    {
+        $arc = new ArrayCollection(array('aap', 'aap', 'noot', 'mies'));
+
+        $filtered = $arc->filter(function ($i) { return $i != 'aap'; });
+        $this->assertInstanceOf('\SAML2\Utilities\ArrayCollection', $filtered);
+        $this->assertEquals($filtered->get(0), null);
+        $this->assertEquals($filtered->get(1), null);
+        $this->assertEquals($filtered->get(2), 'noot');
+        $this->assertEquals($filtered->get(3), 'mies');
+
+        $mapped = $arc->map(function ($i) { return ucfirst($i); });
+        $this->assertInstanceOf('\SAML2\Utilities\ArrayCollection', $mapped);
+        $this->assertEquals($mapped->get(0), 'Aap');
+        $this->assertEquals($mapped->get(1), 'Aap');
+        $this->assertEquals($mapped->get(2), 'Noot');
+        $this->assertEquals($mapped->get(3), 'Mies');
+
+        // ensure original is not changed 
+        $this->assertEquals($arc->get(0), 'aap');
+        $this->assertEquals($arc->get(1), 'aap');
+        $this->assertEquals($arc->get(2), 'noot');
+        $this->assertEquals($arc->get(3), 'mies');
+    }
+}
diff --git a/tests/SAML2/Utilities/CertificateTest.php b/tests/SAML2/Utilities/CertificateTest.php
new file mode 100644
index 000000000..16c6af067
--- /dev/null
+++ b/tests/SAML2/Utilities/CertificateTest.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace SAML2\Utilities;
+
+class CertificateTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @group utilities
+     * @test
+     */
+    public function testValidStructure()
+    {
+        $result = Certificate::hasValidStructure(\SAML2\CertificatesMock::getPlainPublicKey());
+        $this->assertTrue($result);
+        $result = Certificate::hasValidStructure(\SAML2\CertificatesMock::getPlainInvalidPublicKey());
+        $this->assertFalse($result);
+    }
+
+    /**
+     * @group utilities
+     * @test
+     */
+    public function testConvertToCertificate()
+    {
+        $result = Certificate::convertToCertificate(\SAML2\CertificatesMock::getPlainPublicKeyContents());
+        // the formatted public key in CertificatesMock is stored with unix newlines
+        $this->assertEquals(\SAML2\CertificatesMock::getPlainPublicKey() . "\n", str_replace("\r", "", $result));
+    }
+}
diff --git a/tests/SAML2/XML/saml/AttributeValueTest.php b/tests/SAML2/XML/saml/AttributeValueTest.php
new file mode 100644
index 000000000..de01bc2e0
--- /dev/null
+++ b/tests/SAML2/XML/saml/AttributeValueTest.php
@@ -0,0 +1,112 @@
+<?php
+
+namespace SAML2\XML\md;
+
+use SAML2\Constants;
+use SAML2\DOMDocumentFactory;
+use SAML2\Utils;
+use SAML2\XML\saml\Attribute;
+use SAML2\XML\saml\AttributeValue;
+
+/**
+ * Class \SAML2\XML\md\AttributeTest
+ */
+class AttributeValueTest extends \PHPUnit_Framework_TestCase
+{
+
+    /**
+     * Verifies that supplying an empty string as attribute value will
+     * generate a tag with no content (instead of e.g. an empty tag).
+     */
+    public function testEmptyStringAttribute()
+    {
+        $attribute = new Attribute();
+        $attribute->Name = 'TheName';
+        $attribute->NameFormat = 'TheNameFormat';
+        $attribute->FriendlyName = 'TheFriendlyName';
+        $attribute->AttributeValue = array(
+            new AttributeValue(""),
+        );
+
+        $document = DOMDocumentFactory::fromString('<root />');
+        $returnedStructure = $attribute->toXML($document->firstChild);
+
+        $expectedStructureDocument = new \DOMDocument();
+        $expectedStructureDocument->loadXML(<<<ATTRIBUTEVALUE
+<saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Name="TheName"
+ NameFormat="TheNameFormat" FriendlyName="TheFriendlyName">
+  <saml:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string"></saml:AttributeValue>
+</saml:Attribute>
+ATTRIBUTEVALUE
+        );
+        $expectedStructure = $expectedStructureDocument->documentElement;
+
+        $this->assertEqualXMLStructure($expectedStructure, $returnedStructure);
+        $this->assertEquals("", $attribute->AttributeValue[0]->getString());
+    }
+
+    /**
+     * Verifies that we can create an AttributeValue from a DOMElement.
+     */
+    public function testCreateAttributeFromDOMElement()
+    {
+        $attribute = new Attribute();
+        $attribute->Name = 'TheName';
+        $attribute->NameFormat = 'TheNameFormat';
+        $attribute->FriendlyName = 'TheFriendlyName';
+
+        $element = new \DOMDocument();
+        $element->loadXML(<<<ATTRIBUTEVALUE
+<NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">urn:collab:person:surftest.nl:example</NameID>
+ATTRIBUTEVALUE
+        );
+
+        $attribute->AttributeValue = array(
+            new AttributeValue($element->documentElement),
+        );
+
+        $document = DOMDocumentFactory::fromString('<root />');
+        $returnedStructure = $attribute->toXML($document->firstChild);
+
+        $expectedStructureDocument = new \DOMDocument();
+        $expectedStructureDocument->loadXML(<<<ATTRIBUTEXML
+<saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
+  Name="TheName" NameFormat="TheNameFormat" FriendlyName="TheFriendlyName">
+  <saml:AttributeValue>
+    <NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">urn:collab:person:surftest.nl:example</NameID>
+  </saml:AttributeValue>
+</saml:Attribute>
+ATTRIBUTEXML
+        );
+        $expectedStructure = $expectedStructureDocument->documentElement;
+
+        $this->assertEqualXMLStructure($expectedStructure, $returnedStructure);
+        $this->assertEquals("urn:collab:person:surftest.nl:example", $attribute->AttributeValue[0]->getString());
+    }
+
+    /**
+     * Serialize an AttributeValue and Unserialize that again.
+     */
+    public function testSerialize()
+    {
+        $av1 = new AttributeValue("Aap:noot:mies");
+        $ser = $av1->serialize();
+        $av2 = new AttributeValue("Wim");
+        $av2->unserialize($ser);
+
+        $this->assertEquals("Aap:noot:mies", $av2->getString());
+
+        $element = new \DOMDocument();
+        $element->loadXML(<<<ATTRIBUTEVALUE
+<NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">urn:collab:person:surftest.nl:example</NameID>
+ATTRIBUTEVALUE
+        );
+
+        $av3 = new AttributeValue($element->documentElement);
+        $ser = $av3->serialize();
+        $av4 = new AttributeValue("Wim");
+        $av4->unserialize($ser);
+        $this->assertEquals("urn:collab:person:surftest.nl:example", $av4->getString());
+    }
+}