source: psi/trunk/cutestuff/xmlsec/xmlenc.cpp@ 189

Last change on this file since 189 was 2, checked in by dmik, 19 years ago

Imported original Psi 0.10 sources from Affinix

File size: 16.3 KB
Line 
1/*
2 * xmlenc.cpp - XML Encryption
3 * Copyright (C) 2003 Justin Karneges
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21#include"xmlenc.h"
22
23#include"../util/bytestream.h"
24#include"../util/base64.h"
25#include"keyops.h"
26
27static QByteArray nodeToArray(const QDomNode &e)
28{
29 QString out;
30 QTextStream ts(&out, IO_WriteOnly);
31 e.save(ts, 1);
32 QCString xmlToEnc = out.utf8();
33 int len = xmlToEnc.length();
34 QByteArray b(len);
35 memcpy(b.data(), xmlToEnc.data(), len);
36 return b;
37}
38
39static QDomElement findSubTag(const QDomElement &e, const QString &name, bool *found)
40{
41 if(found)
42 *found = false;
43
44 for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
45 QDomElement i = n.toElement();
46 if(i.isNull())
47 continue;
48 if(i.tagName() == name) {
49 if(found)
50 *found = true;
51 return i;
52 }
53 }
54
55 QDomElement tmp;
56 return tmp;
57}
58
59
60using namespace XmlEnc;
61
62
63//----------------------------------------------------------------------------
64// KeyInfo
65//----------------------------------------------------------------------------
66KeyInfo::KeyInfo()
67{
68}
69
70KeyInfo::~KeyInfo()
71{
72}
73
74bool KeyInfo::isEmpty() const
75{
76 if(v_name.isEmpty() && v_value.isEmpty() && v_rmethods.isEmpty() && v_key.isNull())
77 return true;
78 return false;
79}
80
81QString KeyInfo::name() const
82{
83 return v_name;
84}
85
86QByteArray KeyInfo::value () const
87{
88 return v_value;
89}
90
91QStringList KeyInfo::retrievalMethods() const
92{
93 return v_rmethods;
94}
95
96QDomElement KeyInfo::encryptedKey() const
97{
98 return v_key;
99}
100
101void KeyInfo::setName(const QString &s)
102{
103 v_name = s;
104}
105
106void KeyInfo::setValue(const QByteArray &d)
107{
108 v_value = d;
109}
110
111void KeyInfo::setRetrievalMethods(const QStringList &s)
112{
113 v_rmethods = s;
114}
115
116void KeyInfo::attachEncryptedKey(const QDomElement &e)
117{
118 v_key = e;
119}
120
121QDomElement KeyInfo::toXml(QDomDocument *doc) const
122{
123 QDomElement e = doc->createElement("ds:KeyInfo");
124 e.setAttribute("xmlns:ds", "http://www.w3.org/2000/09/xmldsig#");
125
126 if(!v_value.isEmpty()) {
127 QDomElement v = doc->createElement("ds:KeyValue");
128 v.appendChild(doc->createTextNode(Base64::arrayToString(v_value)));
129 e.appendChild(v);
130 }
131
132 if(!v_name.isEmpty()) {
133 QDomElement n = doc->createElement("ds:KeyName");
134 n.appendChild(doc->createTextNode(v_name));
135 e.appendChild(n);
136 }
137
138 for(QStringList::ConstIterator it = v_rmethods.begin(); it != v_rmethods.end(); ++it) {
139 QDomElement r = doc->createElement("ds:RetrievalMethod");
140 r.setAttribute("Type", "http://www.w3.org/2001/04/xmlenc#EncryptedKey");
141 r.setAttribute("URI", *it);
142 e.appendChild(r);
143 }
144
145 if(!v_key.isNull())
146 e.appendChild(v_key);
147
148 return e;
149}
150
151bool KeyInfo::fromXml(const QDomElement &e)
152{
153 if(e.tagName() != "ds:KeyInfo" || e.attribute("xmlns:ds") != "http://www.w3.org/2000/09/xmldsig#")
154 return false;
155
156 QByteArray val;
157 QString n;
158 QStringList rml;
159 QDomElement ek;
160
161 bool found;
162 QDomElement i;
163
164 i = findSubTag(e, "ds:KeyValue", &found);
165 if(found)
166 val = Base64::stringToArray(i.text());
167 i = findSubTag(e, "ds:KeyName", &found);
168 if(found)
169 n = i.text();
170 QDomNodeList l = e.elementsByTagName("ds:RetrievalMethod");
171 for(int n = 0; n < (int)l.count(); ++n) {
172 QDomElement r = l.item(n).toElement();
173 if(r.attribute("Type") == "http://www.w3.org/2001/04/xmlenc#EncryptedKey")
174 rml += r.attribute("URI");
175 }
176 i = findSubTag(e, "EncryptedKey", &found);
177 if(found)
178 ek = i;
179
180 // all good
181 v_value = val;
182 v_name = n;
183 v_rmethods = rml;
184 v_key = ek;
185
186 return true;
187}
188
189
190//----------------------------------------------------------------------------
191// EncryptionProperty
192//----------------------------------------------------------------------------
193EncryptionProperty::EncryptionProperty(const QString &target, const QString &id)
194{
195 v_target = target;
196 v_id = id;
197}
198
199QString EncryptionProperty::target() const
200{
201 return v_target;
202}
203
204QString EncryptionProperty::id() const
205{
206 return v_id;
207}
208
209QString EncryptionProperty::property(const QString &var) const
210{
211 QStringList::ConstIterator it = vars.begin();
212 QStringList::ConstIterator it2 = vals.begin();
213 while(it != vars.end()) {
214 if((*it) == var)
215 return *it2;
216 ++it;
217 ++it2;
218 }
219 return "";
220}
221
222void EncryptionProperty::setTarget(const QString &target)
223{
224 v_target = target;
225}
226
227void EncryptionProperty::setId(const QString &id)
228{
229 v_id = id;
230}
231
232void EncryptionProperty::setProperty(const QString &var, const QString &val)
233{
234 if(var == "Target" || var == "Id")
235 return;
236
237 // see if we have it already
238 QStringList::Iterator it = vars.begin();
239 QStringList::Iterator it2 = vals.begin();
240 while(it != vars.end()) {
241 if((*it) == var) {
242 *it2 = val;
243 return;
244 }
245 ++it;
246 ++it2;
247 }
248
249 vars += var;
250 vals += val;
251}
252
253QDomElement EncryptionProperty::toXml(QDomDocument *doc) const
254{
255 QDomElement e = doc->createElement("EncryptionProperty");
256 if(!v_target.isEmpty())
257 e.setAttribute("Target", v_target);
258 if(!v_id.isEmpty())
259 e.setAttribute("Id", v_id);
260
261 QStringList::ConstIterator it = vars.begin();
262 QStringList::ConstIterator it2 = vals.begin();
263 while(it != vars.end()) {
264 e.setAttribute(*it, *it2);
265 ++it;
266 ++it2;
267 }
268
269 return e;
270}
271
272bool EncryptionProperty::fromXml(QDomElement &e)
273{
274 if(e.tagName() != "EncryptionProperty")
275 return false;
276
277 v_target = e.attribute("Target");
278 v_id = e.attribute("Id");
279 vars.clear();
280 vals.clear();
281 QDomNamedNodeMap map = e.attributes();
282 for(int n = 0; n < (int)map.count(); ++n) {
283 QDomAttr a = map.item(n).toAttr();
284 QString n = a.name();
285 if(n == "Target" || n == "Id")
286 continue;
287 vars += n;
288 vals += a.value();
289 }
290
291 return true;
292}
293
294
295//----------------------------------------------------------------------------
296// EncryptionProperties
297//----------------------------------------------------------------------------
298EncryptionProperties::EncryptionProperties(const QString &s)
299:QValueList<EncryptionProperty>()
300{
301 v_id = s;
302}
303
304QString EncryptionProperties::id() const
305{
306 return v_id;
307}
308
309void EncryptionProperties::setId(const QString &s)
310{
311 v_id = s;
312}
313
314QDomElement EncryptionProperties::toXml(QDomDocument *doc) const
315{
316 QDomElement e = doc->createElement("EncryptionProperties");
317 if(!v_id.isEmpty())
318 e.setAttribute("Id", v_id);
319 for(QValueList<EncryptionProperty>::ConstIterator it = begin(); it != end(); ++it) {
320 const EncryptionProperty &p = *it;
321 e.appendChild(p.toXml(doc));
322 }
323 return e;
324}
325
326bool EncryptionProperties::fromXml(QDomElement &e)
327{
328 if(e.tagName() != "EncryptionProperties")
329 return false;
330
331 clear();
332 v_id = e.attribute("Id");
333 QDomNodeList l = e.elementsByTagName("EncryptionProperty");
334 for(int n = 0; n < (int)l.count(); ++n) {
335 QDomElement r = l.item(n).toElement();
336 EncryptionProperty p;
337 if(!p.fromXml(r))
338 return false;
339 append(p);
340 }
341
342 return true;
343}
344
345
346//----------------------------------------------------------------------------
347// Encrypted
348//----------------------------------------------------------------------------
349Encrypted::Encrypted()
350{
351 baseNS = "http://www.w3.org/2001/04/xmlenc#";
352 clear();
353}
354
355Encrypted::~Encrypted()
356{
357 clear();
358}
359
360void Encrypted::clear()
361{
362 v_method = None;
363 v_id = "";
364 v_dataType = Arbitrary;
365 v_type = Data;
366 v_mimeType = "";
367 v_keyInfo = KeyInfo();
368
369 v_cval = "";
370 v_cref = Reference();
371 v_carrykeyname = "";
372}
373
374Method Encrypted::cipherTypeToMethod(Cipher::Type t) const
375{
376 if(t == Cipher::TripleDES)
377 return TripleDES;
378 else if(t == Cipher::AES_128)
379 return AES_128;
380 else if(t == Cipher::AES_256)
381 return AES_256;
382 else
383 return None;
384}
385
386void Encrypted::setDataReference(const Reference &cref, Method m)
387{
388 v_method = m;
389 v_cref = cref;
390}
391
392bool Encrypted::encryptData(const QByteArray &data, const Cipher::Key &key)
393{
394 QByteArray iv = Cipher::generateIV(key.type());
395 if(!sym_encrypt(data, key, iv, &v_cval))
396 return false;
397 v_dataType = Arbitrary;
398 v_method = cipherTypeToMethod(key.type());
399 return true;
400}
401
402bool Encrypted::encryptElement(const QDomElement &data, const Cipher::Key &key)
403{
404 QByteArray iv = Cipher::generateIV(key.type());
405 if(!sym_encrypt(nodeToArray(data), key, iv, &v_cval))
406 return false;
407 v_type = Data;
408 v_dataType = Element;
409 v_method = cipherTypeToMethod(key.type());
410 return true;
411}
412
413bool Encrypted::encryptContent(const QDomElement &data, const Cipher::Key &key)
414{
415 // convert children to raw data
416 QByteArray a;
417 for(QDomNode n = data.firstChild(); !n.isNull(); n = n.nextSibling())
418 ByteStream::appendArray(&a, nodeToArray(n));
419
420 QByteArray iv = Cipher::generateIV(key.type());
421 if(!sym_encrypt(a, key, iv, &v_cval))
422 return false;
423 v_type = Data;
424 v_dataType = Content;
425 v_method = cipherTypeToMethod(key.type());
426 return true;
427}
428
429bool Encrypted::encryptKey(const Cipher::Key &data, const Cipher::Key &key)
430{
431 if(!sym_keywrap(data.data(), key, &v_cval))
432 return false;
433 v_type = Key;
434 v_dataType = Arbitrary;
435 v_method = cipherTypeToMethod(key.type());
436 return true;
437}
438
439bool Encrypted::encryptKey(const Cipher::Key &data, const RSAKey &key)
440{
441 bool useOAEP;
442 if(data.type() == Cipher::TripleDES)
443 useOAEP = false;
444 else
445 useOAEP = true;
446
447 bool ok;
448 QByteArray result;
449 if(useOAEP)
450 result = encryptRSA2(data.data(), key, &ok);
451 else
452 result = encryptRSA(data.data(), key, &ok);
453 if(!ok)
454 return false;
455
456 v_type = Key;
457 v_dataType = Arbitrary;
458 v_method = useOAEP ? RSA_OAEP : RSA_1_5;
459 v_cval = Base64::arrayToString(result);
460 return true;
461}
462
463QByteArray Encrypted::decryptData(const Cipher::Key &key) const
464{
465 QByteArray result;
466 if(!sym_decrypt(v_cval, key, &result))
467 return QByteArray();
468 return result;
469}
470
471QDomElement Encrypted::decryptElement(QDomDocument *doc, const Cipher::Key &key) const
472{
473 QByteArray result;
474 if(!sym_decrypt(v_cval, key, &result))
475 return QDomElement();
476
477 QDomDocument d;
478 if(!d.setContent(result))
479 return QDomElement();
480 return doc->importNode(d.documentElement(), true).toElement();
481}
482
483QDomNodeList Encrypted::decryptContent(QDomDocument *doc, const Cipher::Key &key) const
484{
485 QByteArray result;
486 if(!sym_decrypt(v_cval, key, &result))
487 return QDomNodeList();
488
489 // nul-terminate
490 result.resize(result.size()+1);
491 result[result.size()-1] = 0;
492
493 QCString cs = "<dummy>";
494 cs += (char *)result.data();
495 cs += "</dummy>";
496
497 QDomDocument d;
498 if(!d.setContent(cs))
499 return QDomNodeList();
500 QDomElement e = d.documentElement().firstChild().toElement();
501 if(e.isNull() || e.tagName() != "dummy")
502 return QDomNodeList();
503
504 return doc->importNode(e, true).childNodes();
505}
506
507QByteArray Encrypted::decryptKey(const Cipher::Key &key) const
508{
509 QByteArray result;
510 if(!sym_keyunwrap(v_cval, key, &result))
511 return QByteArray();
512
513 return result;
514}
515
516QByteArray Encrypted::decryptKey(const RSAKey &key) const
517{
518 QByteArray data = Base64::stringToArray(v_cval);
519 QByteArray result;
520 bool ok;
521 if(v_method == RSA_OAEP)
522 result = decryptRSA2(data, key, &ok);
523 else
524 result = decryptRSA(data, key, &ok);
525 if(!ok)
526 return QByteArray();
527
528 return result;
529}
530
531QDomElement Encrypted::toXml(QDomDocument *doc) const
532{
533 QString baseNS = "http://www.w3.org/2001/04/xmlenc#";
534 QDomElement enc;
535
536 // base xml
537 if(v_type == Data)
538 enc = doc->createElement("EncryptedData");
539 else
540 enc = doc->createElement("EncryptedKey");
541 enc.setAttribute("xmlns", baseNS);
542 if(!v_id.isEmpty())
543 enc.setAttribute("Id", v_id);
544 if(v_type == Data)
545 enc.setAttribute("Type", baseNS + "Element");
546
547 // method
548 QDomElement meth = doc->createElement("EncryptionMethod");
549 meth.setAttribute("Algorithm", methodToAlgorithm(v_method, v_type));
550 if(v_method == RSA_OAEP) {
551 // add digest method
552 QDomElement dm = doc->createElement("ds:DigestMethod");
553 dm.setAttribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1");
554 meth.appendChild(dm);
555 }
556 enc.appendChild(meth);
557
558 // keyinfo
559 if(!v_keyInfo.isEmpty())
560 enc.appendChild(v_keyInfo.toXml(doc));
561
562 // cipherdata
563 QDomElement cd = doc->createElement("CipherData");
564 if(!v_cref.uri().isEmpty()) {
565 QDomElement cr = doc->createElement("CipherReference");
566 cr.setAttribute("URI", v_cref.uri());
567 if(!v_cref.transforms().isNull())
568 cr.appendChild(v_cref.transforms());
569 cd.appendChild(cr);
570 }
571 else {
572 QDomElement cv = doc->createElement("CipherValue");
573 cv.appendChild(doc->createTextNode(v_cval));
574 cd.appendChild(cv);
575 }
576 enc.appendChild(cd);
577
578 // encryption properties
579 if(!v_props.isEmpty())
580 enc.appendChild(v_props.toXml(doc));
581
582 // reference list
583 if(!v_reflist.isEmpty()) {
584 QDomElement e = doc->createElement("ReferenceList");
585 for(QValueList<Reference>::ConstIterator it = v_reflist.begin(); it != v_reflist.end(); ++it) {
586 const Reference &r = *it;
587 QDomElement df = doc->createElement("DataReference");
588 df.setAttribute("URI", r.uri());
589 if(!r.transforms().isNull())
590 df.appendChild(r.transforms());
591 e.appendChild(df);
592 }
593 enc.appendChild(e);
594 }
595
596 // carry key name
597 if(!v_carrykeyname.isEmpty()) {
598 QDomElement e = doc->createElement("CarriedKeyName");
599 e.appendChild(doc->createTextNode(v_carrykeyname));
600 enc.appendChild(e);
601 }
602
603 return enc;
604}
605
606bool Encrypted::fromXml(const QDomElement &e)
607{
608 QString baseNS = "http://www.w3.org/2001/04/xmlenc#";
609 if(e.attribute("xmlns") != baseNS)
610 return false;
611 DataType dt = Arbitrary;
612 if(e.hasAttribute("Type")) {
613 QString str = e.attribute("Type");
614 int n = str.find('#');
615 if(n == -1)
616 return false;
617 ++n;
618 if(str.mid(0, n) != baseNS)
619 return false;
620 str = str.mid(n);
621 if(str == "Element")
622 dt = Element;
623 else if(str == "Content")
624 dt = Content;
625 }
626
627 Type t;
628 if(e.tagName() == "EncryptedData")
629 t = Data;
630 else if(e.tagName() == "EncryptedKey")
631 t = Key;
632 else
633 return false;
634
635 // method
636 Method m = None;
637 bool found;
638 QDomElement i = findSubTag(e, "EncryptionMethod", &found);
639 if(!found)
640 return false;
641 QString str = i.attribute("Algorithm");
642 int n = str.find('#');
643 if(n == -1)
644 return false;
645 ++n;
646 if(str.mid(0, n) != baseNS)
647 return false;
648 str = str.mid(n);
649
650 m = algorithmToMethod(str);
651 if(m == None)
652 return false;
653
654 // keyinfo
655 KeyInfo ki;
656 i = findSubTag(e, "ds:KeyInfo", &found);
657 if(found) {
658 if(!ki.fromXml(i))
659 return false;
660 }
661
662 // cipherdata
663 QString cval;
664 Reference cref;
665 i = findSubTag(e, "CipherData", &found);
666 if(!found)
667 return false;
668 i = findSubTag(i, "CipherValue", &found);
669 if(found)
670 cval = i.text();
671 else {
672 i = findSubTag(i, "CipherReference", &found);
673 if(!found)
674 return false;
675 cref.setURI(i.attribute("URI"));
676 QDomElement tf = i.firstChild().toElement();
677 if(!tf.isNull())
678 cref.setTransforms(tf);
679 }
680
681 // encryption properties
682 EncryptionProperties props;
683 i = findSubTag(e, "EncryptionProperties", &found);
684 if(found) {
685 if(!props.fromXml(i))
686 return false;
687 }
688
689 // reference list
690 ReferenceList rl;
691 i = findSubTag(e, "ReferenceList", &found);
692 if(found) {
693 QDomNodeList l = e.elementsByTagName("DataReference");
694 for(int n = 0; n < (int)l.count(); ++n) {
695 QDomElement dr = l.item(n).toElement();
696 Reference r;
697 r.setURI(dr.attribute("URI"));
698 QDomElement tf = i.firstChild().toElement();
699 if(!tf.isNull())
700 r.setTransforms(tf);
701 rl += r;
702 }
703 }
704
705 // carry key name
706 QString carrykey;
707 i = findSubTag(e, "CarriedKeyName", &found);
708 if(found)
709 carrykey = i.text();
710
711 // looks good, let's take it
712 clear();
713 v_type = t;
714 v_dataType = dt;
715 v_method = m;
716 v_cval = cval;
717 v_cref = cref;
718 v_keyInfo = ki;
719 v_props = props;
720 v_reflist = rl;
721 v_carrykeyname = carrykey;
722
723 return true;
724}
725
726QString Encrypted::methodToAlgorithm(Method m, Type t) const
727{
728 QString s;
729 if(m == TripleDES)
730 s = (t == Key ? "kw-tripledes": "tripledes-cbc");
731 else if(m == AES_128)
732 s = (t == Key ? "kw-aes128": "aes128-cbc");
733 else if(m == AES_256)
734 s = (t == Key ? "kw-aes256": "aes256-cbc");
735 else if(m == RSA_1_5)
736 s = "rsa-1_5";
737 else if(m == RSA_OAEP)
738 s = "rsa-oaep-mgf1p";
739 else
740 return "";
741
742 return (baseNS + s);
743}
744
745Method Encrypted::algorithmToMethod(const QString &s) const
746{
747 Method m;
748 if(s == "tripledes-cbc" || s == "kw-tripledes")
749 m = TripleDES;
750 else if(s == "aes128-cbc" || s == "kw-aes128")
751 m = AES_128;
752 else if(s == "aes256-cbc" || s == "kw-aes256")
753 m = AES_256;
754 else if(s == "rsa-1_5")
755 m = RSA_1_5;
756 else if(s == "rsa-oaep-mgf1p")
757 m = RSA_OAEP;
758 else
759 return None;
760
761 return m;
762}
Note: See TracBrowser for help on using the repository browser.