Refactored Signature signing/validation, various Validator bugs fixed.
[shibboleth/cpp-xmltooling.git] / xmltoolingtest / SignatureTest.h
1 /*\r
2  *  Copyright 2001-2005 Internet2\r
3  * \r
4  * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *\r
8  *     http://www.apache.org/licenses/LICENSE-2.0\r
9  *\r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  */\r
16 \r
17 #include "XMLObjectBaseTestCase.h"\r
18 \r
19 #include <fstream>\r
20 #include <openssl/pem.h>\r
21 #include <xercesc/util/XMLUniDefs.hpp>\r
22 #include <xsec/dsig/DSIGReference.hpp>\r
23 #include <xsec/enc/XSECKeyInfoResolverDefault.hpp>\r
24 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>\r
25 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyRSA.hpp>\r
26 #include <xsec/enc/XSECCryptoException.hpp>\r
27 #include <xsec/framework/XSECException.hpp>\r
28 \r
29 class TestContext : public ContentReference\r
30 {\r
31     XMLCh* m_uri;\r
32     \r
33 public:\r
34     TestContext(const XMLCh* uri) {\r
35         m_uri=XMLString::replicate(uri);\r
36     }\r
37     \r
38     virtual ~TestContext() {\r
39         XMLString::release(&m_uri);\r
40     }\r
41 \r
42     ContentReference* clone() const {\r
43         return new TestContext(m_uri);\r
44     }\r
45 \r
46     void createReferences(DSIGSignature* sig) {\r
47         DSIGReference* ref=sig->createReference(m_uri);\r
48         ref->appendEnvelopedSignatureTransform();\r
49         ref->appendCanonicalizationTransform(CANON_C14NE_NOC);\r
50     }\r
51 };\r
52 \r
53 class TestValidator : public Validator\r
54 {\r
55     XMLCh* m_uri;\r
56     \r
57 public:\r
58     TestValidator(const XMLCh* uri) {\r
59         m_uri=XMLString::replicate(uri);\r
60     }\r
61     \r
62     virtual ~TestValidator() {\r
63         XMLString::release(&m_uri);\r
64     }\r
65 \r
66     Validator* clone() const {\r
67         return new TestValidator(m_uri);\r
68     }\r
69 \r
70     void validate(const XMLObject* xmlObject) const {\r
71         DSIGSignature* sig=dynamic_cast<const Signature*>(xmlObject)->getXMLSignature();\r
72         if (!sig)\r
73             throw SignatureException("Only a marshalled Signature object can be verified.");\r
74         const XMLCh* uri=sig->getReferenceList()->item(0)->getURI();\r
75         TSM_ASSERT_SAME_DATA("Reference URI does not match.",uri,m_uri,XMLString::stringLen(uri));\r
76         XSECKeyInfoResolverDefault resolver;\r
77         sig->setKeyInfoResolver(&resolver); // It will clone the resolver for us.\r
78         try {\r
79             if (!sig->verify())\r
80                 throw SignatureException("Signature did not verify.");\r
81         }\r
82         catch(XSECException& e) {\r
83             auto_ptr_char temp(e.getMsg());\r
84             throw SignatureException(string("Caught an XMLSecurity exception verifying signature: ") + temp.get());\r
85         }\r
86         catch(XSECCryptoException& e) {\r
87             throw SignatureException(string("Caught an XMLSecurity exception verifying signature: ") + e.getMsg());\r
88         }\r
89     }\r
90 };\r
91 \r
92 class _addcert : public std::binary_function<X509Data*,XSECCryptoX509*,void> {\r
93 public:\r
94     void operator()(X509Data* bag, XSECCryptoX509* cert) const {\r
95         safeBuffer& buf=cert->getDEREncodingSB();\r
96         X509Certificate* x=X509CertificateBuilder::buildX509Certificate();\r
97         x->setValue(buf.sbStrToXMLCh());\r
98         bag->getX509Certificates().push_back(x);\r
99     }\r
100 };\r
101 \r
102 class SignatureTest : public CxxTest::TestSuite {\r
103     XSECCryptoKey* m_key;\r
104     vector<XSECCryptoX509*> m_certs;\r
105 public:\r
106     void setUp() {\r
107         QName qname(SimpleXMLObject::NAMESPACE,SimpleXMLObject::LOCAL_NAME);\r
108         QName qtype(SimpleXMLObject::NAMESPACE,SimpleXMLObject::TYPE_NAME);\r
109         XMLObjectBuilder::registerBuilder(qname, new SimpleXMLObjectBuilder());\r
110         XMLObjectBuilder::registerBuilder(qtype, new SimpleXMLObjectBuilder());\r
111         string keypath=data_path + "key.pem";\r
112         BIO* in=BIO_new(BIO_s_file_internal());\r
113         if (in && BIO_read_filename(in,keypath.c_str())>0) {\r
114             EVP_PKEY* pkey=PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);\r
115             if (pkey) {\r
116                 m_key=new OpenSSLCryptoKeyRSA(pkey);\r
117                 EVP_PKEY_free(pkey);\r
118             }\r
119         }\r
120         if (in) BIO_free(in);\r
121         TS_ASSERT(m_key!=NULL);\r
122 \r
123         string certpath=data_path + "cert.pem";\r
124         in=BIO_new(BIO_s_file_internal());\r
125         if (in && BIO_read_filename(in,certpath.c_str())>0) {\r
126             X509* x=NULL;\r
127             while (x=PEM_read_bio_X509(in,NULL,NULL,NULL)) {\r
128                 m_certs.push_back(new OpenSSLCryptoX509(x));\r
129                 X509_free(x);\r
130             }\r
131         }\r
132         if (in) BIO_free(in);\r
133         TS_ASSERT(m_certs.size()>0);\r
134         \r
135     }\r
136 \r
137     void tearDown() {\r
138         QName qname(SimpleXMLObject::NAMESPACE,SimpleXMLObject::LOCAL_NAME);\r
139         QName qtype(SimpleXMLObject::NAMESPACE,SimpleXMLObject::TYPE_NAME);\r
140         XMLObjectBuilder::deregisterBuilder(qname);\r
141         XMLObjectBuilder::deregisterBuilder(qtype);\r
142         delete m_key;\r
143         for_each(m_certs.begin(),m_certs.end(),xmltooling::cleanup<XSECCryptoX509>());\r
144     }\r
145 \r
146     void testSignature() {\r
147         TS_TRACE("testSignature");\r
148 \r
149         QName qname(SimpleXMLObject::NAMESPACE,SimpleXMLObject::LOCAL_NAME);\r
150         const SimpleXMLObjectBuilder* b=dynamic_cast<const SimpleXMLObjectBuilder*>(XMLObjectBuilder::getBuilder(qname));\r
151         TS_ASSERT(b!=NULL);\r
152         \r
153         auto_ptr<SimpleXMLObject> sxObject(b->buildObject());\r
154         TS_ASSERT(sxObject.get()!=NULL);\r
155         VectorOf(SimpleXMLObject) kids=sxObject->getSimpleXMLObjects();\r
156         kids.push_back(b->buildObject());\r
157         kids.push_back(b->buildObject());\r
158         \r
159         // Test some collection stuff\r
160         auto_ptr_XMLCh foo("Foo");\r
161         auto_ptr_XMLCh bar("Bar");\r
162         kids.begin()->setId(foo.get());\r
163         kids[1]->setValue(bar.get());\r
164         \r
165         // Append a Signature.\r
166         Signature* sig=SignatureBuilder::buildSignature();\r
167         sxObject->setSignature(sig);\r
168         sig->setContentReference(new TestContext(&chNull));\r
169         sig->setSigningKey(m_key->clone());\r
170         \r
171         // Build KeyInfo.\r
172         KeyInfo* keyInfo=KeyInfoBuilder::buildKeyInfo();\r
173         X509Data* x509Data=X509DataBuilder::buildX509Data();\r
174         keyInfo->getX509Datas().push_back(x509Data);\r
175         for_each(m_certs.begin(),m_certs.end(),bind1st(_addcert(),x509Data));\r
176         sig->setKeyInfo(keyInfo);\r
177         \r
178         // Signing context for the whole document.\r
179         vector<Signature*> sigs(1,sig);\r
180         DOMElement* rootElement = NULL;\r
181         try {\r
182             rootElement=sxObject->marshall((DOMDocument*)NULL,&sigs);\r
183         }\r
184         catch (XMLToolingException& e) {\r
185             TS_TRACE(e.what());\r
186             throw;\r
187         }\r
188         \r
189         string buf;\r
190         XMLHelper::serialize(rootElement, buf);\r
191         //TS_TRACE(buf.c_str());\r
192 \r
193         istringstream in(buf);\r
194         DOMDocument* doc=XMLToolingConfig::getConfig().getParser().parse(in);\r
195         auto_ptr<SimpleXMLObject> sxObject2(dynamic_cast<SimpleXMLObject*>(b->buildFromDocument(doc)));\r
196         TS_ASSERT(sxObject2.get()!=NULL);\r
197         TS_ASSERT(sxObject2->getSignature()!=NULL);\r
198         sxObject2->getSignature()->registerValidator(new TestValidator(&chNull));\r
199         \r
200         try {\r
201             sxObject2->getSignature()->validate(false);\r
202         }\r
203         catch (XMLToolingException& e) {\r
204             TS_TRACE(e.what());\r
205             throw;\r
206         }\r
207     }\r
208 \r
209 };\r