Source Code Cross Referenced for ENCRYPT1_4.java in  » Net » JGroups-2.4.1-sp3 » org » jgroups » protocols » obsolete » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Net » JGroups 2.4.1 sp3 » org.jgroups.protocols.obsolete 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        // changes made by mandar
002:
003:        // Added .* imports
004:        // replacing SecretKey with SecretKey
005:
006:        // $Id: ENCRYPT1_4.java,v 1.1 2006/03/10 15:09:46 belaban Exp $
007:
008:        package org.jgroups.protocols.obsolete;
009:
010:        import org.jgroups.Address;
011:        import org.jgroups.Event;
012:        import org.jgroups.Message;
013:        import org.jgroups.View;
014:        import org.jgroups.protocols.PingRsp;
015:        import org.jgroups.stack.Protocol;
016:
017:        import javax.crypto.Cipher;
018:        import javax.crypto.KeyGenerator;
019:        import javax.crypto.SecretKey;
020:        import javax.crypto.SecretKeyFactory;
021:        import javax.crypto.spec.SecretKeySpec;
022:        import java.io.IOException;
023:        import java.security.*;
024:        import java.security.spec.X509EncodedKeySpec;
025:        import java.util.Properties;
026:        import java.util.Vector;
027:
028:        /**
029:         * ENCRYPT1_4 layer. Encrypt and decrypt the group communication in JGroups
030:         */
031:        public class ENCRYPT1_4 extends Protocol {
032:
033:            public static class EncryptHeader extends org.jgroups.Header {
034:                int type;
035:                static final int ENCRYPT = 0;
036:                static final int KEY_REQUEST = 1;
037:                static final int SERVER_PUBKEY = 2;
038:                static final int SECRETKEY = 3;
039:                static final int SECRETKEY_READY = 4;
040:
041:                // adding key for Message object purpose
042:                static final String KEY = "encrypt";
043:
044:                public EncryptHeader() {
045:                }
046:
047:                public EncryptHeader(int type) {
048:                    this .type = type;
049:                }
050:
051:                public void writeExternal(java.io.ObjectOutput out)
052:                        throws IOException {
053:                    out.writeInt(type);
054:                }
055:
056:                public void readExternal(java.io.ObjectInput in)
057:                        throws IOException, ClassNotFoundException {
058:                    type = in.readInt();
059:                }
060:
061:                public String toString() {
062:                    return "[ENCTYPT: <variables> ]";
063:                }
064:            }
065:
066:            Address local_addr = null;
067:            Address keyServerAddr = null;
068:            boolean keyServer = false;
069:            String asymAlgorithm = "RSA";
070:            String symAlgorithm = "DES/ECB/PKCS5Padding";
071:            int asymInit = 512; // initial public/private key length
072:            int symInit = 56; // initial shared key length
073:            // for public/private Key
074:            KeyPair Kpair; // to store own's public/private Key
075:            SecretKey desKey = null;
076:            final PublicKey pubKey = null; // for server to store the temporary client public key
077:            PublicKey serverPubKey = null; // for client to store server's public Key
078:            Cipher cipher;
079:            Cipher rsa;
080:            final Vector members = new Vector();
081:            final Vector notReady = new Vector();
082:
083:            public ENCRYPT1_4() {
084:                //Provider prov = Security.getProvider("SUN");
085:                //Security.addProvider(prov);
086:            }
087:
088:            public String getName() {
089:                return "ENCRYPT1_4";
090:            }
091:
092:            /*
093:             * GetAlgorithm: Get the algorithm name from "algorithm/mode/padding"
094:             */
095:            private static String getAlgorithm(String s) {
096:                int index = s.indexOf("/");
097:                if (index == -1)
098:                    return s;
099:
100:                return s.substring(0, index);
101:            }
102:
103:            public boolean setProperties(Properties props) {
104:                String str;
105:
106:                super .setProperties(props);
107:                // asymmetric key length
108:                str = props.getProperty("asymInit");
109:                if (str != null) {
110:                    asymInit = Integer.parseInt(str);
111:                    props.remove("asymInit");
112:
113:                    if (log.isInfoEnabled())
114:                        log.info("Asym algo bits used is " + asymInit);
115:                }
116:
117:                // symmetric key length
118:                str = props.getProperty("symInit");
119:                if (str != null) {
120:                    symInit = Integer.parseInt(str);
121:                    props.remove("symInit");
122:
123:                    if (log.isInfoEnabled())
124:                        log.info("Sym algo bits used is " + symInit);
125:                }
126:
127:                // asymmetric algorithm name
128:                str = props.getProperty("asymAlgorithm");
129:                if (str != null) {
130:                    asymAlgorithm = str;
131:                    props.remove("asymAlgorithm");
132:
133:                    if (log.isInfoEnabled())
134:                        log.info("Asym algo used is " + asymAlgorithm);
135:                }
136:
137:                // symmetric algorithm name
138:                str = props.getProperty("symAlgorithm");
139:                if (str != null) {
140:                    symAlgorithm = str;
141:                    props.remove("symAlgorithm");
142:
143:                    if (log.isInfoEnabled())
144:                        log.info("Sym algo used is " + symAlgorithm);
145:                }
146:                if (props.size() > 0) {
147:
148:                    if (log.isErrorEnabled())
149:                        log.error("these properties are not recognized: "
150:                                + props);
151:                    return false;
152:                }
153:
154:                return true;
155:            }
156:
157:            public void init() throws Exception {
158:                // generate keys according to the specified algorithms
159:                // generate publicKey and Private Key using RSA
160:                KeyPairGenerator KpairGen = KeyPairGenerator
161:                        .getInstance(getAlgorithm(asymAlgorithm));
162:                KpairGen.initialize(asymInit, new SecureRandom());
163:                Kpair = KpairGen.generateKeyPair();
164:
165:                // generate secret key
166:                KeyGenerator keyGen = KeyGenerator
167:                        .getInstance(getAlgorithm(symAlgorithm));
168:                keyGen.init(symInit);
169:                desKey = keyGen.generateKey();
170:
171:                // initialize for rsa, cipher encryption/decryption
172:                rsa = Cipher.getInstance(asymAlgorithm);
173:                cipher = Cipher.getInstance(symAlgorithm);
174:
175:                if (log.isInfoEnabled())
176:                    log
177:                            .info(" Both asym and sym algo initialized with the single shared key");
178:            }
179:
180:            /** Just remove if you don't need to reset any state */
181:            public static void reset() {
182:            }
183:
184:            public void up(Event evt) {
185:                Message msg;
186:                Message newMsg;
187:                EncryptHeader hdr;
188:
189:                if (log.isInfoEnabled())
190:                    log.info("Event going up is " + evt);
191:
192:                switch (evt.getType()) {
193:                case Event.SET_LOCAL_ADDRESS:
194:
195:                    if (log.isInfoEnabled())
196:                        log.info("Set address call");
197:                    local_addr = (Address) evt.getArg();
198:                    break;
199:                case Event.FIND_INITIAL_MBRS_OK:
200:                    Vector member = (Vector) evt.getArg();
201:
202:                    if (log.isInfoEnabled())
203:                        log.info("FIND_INIT members call, left members are "
204:                                + member.size());
205:
206:                    // this check is required, to prevent keyServer= false when adding itself
207:                    if (!keyServer)
208:                        keyServer = member.size() <= 0;
209:
210:                    if (member != null && member.size() > 0)
211:                        keyServerAddr = ((PingRsp) member.firstElement()).coord_addr;
212:                    else
213:                        keyServerAddr = local_addr;
214:
215:                    if (!keyServer) {
216:
217:                        desKey = null;
218:
219:                        if (log.isDebugEnabled())
220:                            log
221:                                    .debug("This is not keyserver, deskey set to null");
222:                        // client send clien's public key to server and request server's public key
223:                        newMsg = new Message(keyServerAddr, local_addr, Kpair
224:                                .getPublic().getEncoded());
225:                        // making changes (MANDAR)
226:                        newMsg.putHeader(EncryptHeader.KEY, new EncryptHeader(
227:                                EncryptHeader.KEY_REQUEST));
228:                        passDown(new Event(Event.MSG, newMsg));
229:                    }
230:
231:                    if (log.isInfoEnabled())
232:                        log
233:                                .info("Done parsing for encrypt headers, sending upwards"
234:                                        + evt);
235:                    passUp(evt);
236:                    return;
237:
238:                case Event.MSG:
239:                    msg = (Message) evt.getArg();
240:
241:                    if (log.isInfoEnabled())
242:                        log
243:                                .info("This is a message from peer, not control header"
244:                                        + msg);
245:
246:                    // making changes (MANDAR)
247:                    if (msg == null) {
248:
249:                        if (log.isDebugEnabled())
250:                            log.debug("Null message");
251:                        passUp(evt);
252:                        return;
253:                    }
254:
255:                    // making changes (MANDAR)
256:                    //Object obj=msg.peekHeader();
257:                    Object obj = msg.removeHeader(EncryptHeader.KEY);
258:
259:                    if (log.isInfoEnabled())
260:                        log.info("Stripping the required protocol header");
261:
262:                    // if not encrypted message, pass up
263:                    if (obj == null || !(obj instanceof  EncryptHeader)) {
264:
265:                        if (log.isInfoEnabled())
266:                            log
267:                                    .info("Dropping package as ENCRYPT1_4 protocol is not been recognized, msg will not be passed up");
268:
269:                        // BELA comment this out in case U think otherwise
270:                        //passUp(evt);
271:                        return;
272:                    }
273:
274:                    // making changes (MANDAR)
275:                    //hdr = (EncryptHeader)msg.removeHeader();
276:                    hdr = (EncryptHeader) obj;
277:
278:                    if (log.isInfoEnabled())
279:                        log.info("Header received " + hdr + ':' + hdr.type);
280:                    switch (hdr.type) {
281:                    // key request from client and send server's public key to client
282:                    case EncryptHeader.KEY_REQUEST:
283:                        try {
284:
285:                            if (log.isDebugEnabled())
286:                                log.debug("Request for key");
287:                            // store the this client to notReady list using client's address
288:                            notReady.addElement(msg.getSrc());
289:                            // store the client's public key for temporary
290:                            PublicKey tmpPubKey = generatePubKey(msg
291:                                    .getBuffer());
292:
293:                            if (log.isDebugEnabled())
294:                                log.debug("Generated requestors public key");
295:
296:                            // send server's publicKey
297:                            newMsg = new Message(msg.getSrc(), local_addr,
298:                                    Kpair.getPublic().getEncoded());
299:                            // making changes (MANDAR)
300:                            newMsg.putHeader(EncryptHeader.KEY,
301:                                    new EncryptHeader(
302:                                            EncryptHeader.SERVER_PUBKEY));
303:
304:                            if (log.isDebugEnabled())
305:                                log
306:                                        .debug("Encoded servers public key using clients public key, only client can debug it using its private key and sending it back");
307:                            passDown(new Event(Event.MSG, newMsg));
308:
309:                            // my changes (MANDAR)
310:                            rsa.init(Cipher.ENCRYPT_MODE, tmpPubKey);
311:                            byte[] encryptedKey = rsa.doFinal(desKey
312:                                    .getEncoded());
313:
314:                            if (log.isDebugEnabled())
315:                                log
316:                                        .debug(" Generated encoded key which only client can decode");
317:
318:                            // send shared DesKey to client
319:                            //   1. Decrypt desKey with server's own private Key
320:                            //   2. Encrypt decrypted desKey with client's own public Key
321:                            // encrypt encoded desKey using server's private key
322:                            /*
323:                                        rsa.init(Cipher.ENCRYPT_MODE, Kpair.getPrivate());
324:                                        byte[] decryptedKey=rsa.doFinal(desKey.getEncoded());
325:
326:                                        // encrypt decrypted key using client's public key
327:                                        rsa.init(Cipher.ENCRYPT_MODE, pubKey);
328:                                        byte[] encryptedKey=rsa.doFinal(decryptedKey);
329:                             */
330:                            //send encrypted deskey to client			    
331:                            newMsg = new Message(msg.getSrc(), local_addr,
332:                                    encryptedKey);
333:                            // making changes (MANDAR)
334:                            newMsg.putHeader(EncryptHeader.KEY,
335:                                    new EncryptHeader(EncryptHeader.SECRETKEY));
336:
337:                            if (log.isDebugEnabled())
338:                                log.debug(" Sending encoded key to client");
339:                            passDown(new Event(Event.MSG, newMsg));
340:                        } catch (Exception e) {
341:                            e.printStackTrace();
342:                            System.out.println(e + "0");
343:                        }
344:                        return;
345:                    case EncryptHeader.SECRETKEY_READY:
346:                        //server get client's public key and generate the secret key
347:                        notReady.removeElement(msg.getSrc());
348:
349:                        if (log.isDebugEnabled())
350:                            log.debug("Removed client " + msg.getSrc()
351:                                    + "from notready list");
352:                        return;
353:                    case EncryptHeader.SERVER_PUBKEY:
354:                        serverPubKey = generatePubKey(msg.getBuffer());
355:
356:                        if (log.isDebugEnabled())
357:                            log.debug(" Obtained the servers public key");
358:                        return;
359:
360:                    case EncryptHeader.SECRETKEY:
361:                        try {
362:                            // decrypt using client's private Key
363:                            rsa.init(Cipher.DECRYPT_MODE, Kpair.getPrivate());
364:                            // my changes (MANDAR)
365:                            byte[] encodedKey = rsa.doFinal(msg.getBuffer());
366:
367:                            if (log.isDebugEnabled())
368:                                log
369:                                        .debug("generating encoded key obtained from server-admin");
370:
371:                            /* Piece commented out by MANDAR 
372:                                        byte[] decryptedKey=rsa.doFinal(msg.getBuffer());			    
373:                                        // decrypt using server's public Key
374:                                        rsa.init(Cipher.DECRYPT_MODE, serverPubKey);
375:                                        byte[] encodedKey=rsa.doFinal(decryptedKey);
376:                             */
377:
378:                            // decode secretKey
379:                            desKey = decodedKey(encodedKey);
380:                            if (desKey == null)
381:                                log.error("ohh oh !! DES key is null");
382:
383:                            // send ready message (MANDAR) null -> ""
384:                            newMsg = new Message(msg.getSrc(), local_addr, null);
385:                            // making changes (MANDAR)
386:                            newMsg.putHeader(EncryptHeader.KEY,
387:                                    new EncryptHeader(
388:                                            EncryptHeader.SECRETKEY_READY));
389:                            passDown(new Event(Event.MSG, newMsg));
390:
391:                            if (log.isDebugEnabled())
392:                                log
393:                                        .debug("Got the deskey, sending down sec_Ready header");
394:                        } catch (Exception e) {
395:                            e.printStackTrace();
396:                            System.out.println(e + "5");
397:                        }
398:                        return;
399:
400:                    default:
401:                        break;
402:                    }
403:
404:                    if (hdr.type != 0)
405:                        log.error("Error , header is not 0");
406:
407:                    // not have shared key yet
408:                    // this encrypted message is of no use, drop it
409:                    if (desKey == null)
410:                        return;
411:
412:                    if (log.isInfoEnabled())
413:                        log.info(" Starting to decypher messages");
414:
415:                    // if both the shared key and incoming message are not null
416:                    // decrypt the message
417:                    if (msg.getBuffer() != null) {
418:                        try {
419:                            cipher.init(Cipher.DECRYPT_MODE, desKey);
420:                            msg.setBuffer(cipher.doFinal(msg.getBuffer()));
421:                        } catch (Exception e) {
422:                            e.printStackTrace();
423:                        }
424:                    }
425:                    break;
426:                }
427:
428:                if (log.isInfoEnabled())
429:                    log.info("Passing up event");
430:                passUp(evt); // Pass up to the layer above us
431:            }
432:
433:            public void down(Event evt) {
434:                Message msg;
435:                Message newMsg;
436:                boolean leave = false;
437:
438:                if (log.isInfoEnabled())
439:                    log.info("down:evt is " + evt + ':' + evt.getType());
440:
441:                switch (evt.getType()) {
442:
443:                case Event.VIEW_CHANGE:
444:
445:                    if (log.isInfoEnabled())
446:                        log.info("View change call, new member coming in");
447:                    Vector new_members = ((View) evt.getArg()).getMembers();
448:
449:                    // member size decreases: member leaves, need a new key
450:                    if (members.size() > new_members.size())
451:                        leave = true;
452:
453:                    // copy member list
454:                    synchronized (members) {
455:                        members.removeAllElements();
456:                        if (new_members != null && new_members.size() > 0)
457:                            for (int i = 0; i < new_members.size(); i++)
458:                                members.addElement(new_members.elementAt(i));
459:                    }// end of sync
460:
461:                    // redistribute/regain the new key because old member leaves
462:                    if (leave) {
463:                        // get coordinator address
464:                        Object obj = members.firstElement();
465:
466:                        // if I'm the coordinator/key-server
467:                        if (obj.equals(local_addr)) {
468:                            //create the new shared key and distribute
469:                            keyServer = true;
470:                            keyServerAddr = local_addr;
471:
472:                            // reset shared key
473:                            desKey = null;
474:
475:                            if (log.isInfoEnabled())
476:                                log.info(" leave caused deskey to be null ");
477:
478:                            try {
479:                                //generate new shared key
480:                                KeyGenerator keyGen = KeyGenerator
481:                                        .getInstance(getAlgorithm(symAlgorithm));
482:                                keyGen.init(symInit);
483:                                desKey = keyGen.generateKey();
484:                            } catch (Exception e) {
485:                                e.printStackTrace();
486:                            }
487:                        }//end of local_addr == obj
488:                        // if I'm not the coordinator/key-server
489:                        else {
490:                            keyServer = false;
491:                            keyServerAddr = (Address) obj;
492:
493:                            // reset shared key
494:                            desKey = null;
495:
496:                            // client send clien's public key to server and request server's public key
497:                            newMsg = new Message(keyServerAddr, local_addr,
498:                                    Kpair.getPublic().getEncoded());
499:                            // making changes (MANDAR)
500:                            newMsg
501:                                    .putHeader(EncryptHeader.KEY,
502:                                            new EncryptHeader(
503:                                                    EncryptHeader.KEY_REQUEST));
504:                            passDown(new Event(Event.MSG, newMsg));
505:
506:                            if (log.isDebugEnabled())
507:                                log
508:                                        .debug("Requesting new key to be part of group");
509:                        } // end of else
510:                    }
511:                    break;
512:
513:                case Event.MSG:
514:                    msg = (Message) evt.getArg();
515:
516:                    if (log.isDebugEnabled())
517:                        log.debug("Its a message call " + msg);
518:                    int i;
519:
520:                    // For Server:
521:                    // if some members don't have the shared key yet
522:                    if (!notReady.isEmpty()) {
523:                        System.out.println("not Ready list  :"
524:                                + notReady.toString());
525:                        if (msg.getDest() == null) {
526:                            for (i = 0; i < notReady.size(); i++) {
527:                                // making changes (MANDAR)
528:                                newMsg = new Message((Address) notReady
529:                                        .elementAt(i), local_addr, msg
530:                                        .getBuffer());
531:                                passDown(new Event(Event.MSG, newMsg));
532:                            }
533:                            break;
534:                        } else {
535:                            for (i = 0; i < notReady.size(); i++) {
536:                                if (msg.getDest().equals(notReady.elementAt(i))) {
537:                                    passDown(evt);
538:                                    return;
539:                                }// end of if..
540:                            }// end of for..
541:                        }// end of else
542:                    }
543:
544:                    // I already know the shared key
545:                    if (desKey != null) {
546:
547:                        if (log.isInfoEnabled())
548:                            log.info("DESkey is not null, I know it ");
549:                        try {
550:                            // if the message is not empty, encrypt it
551:                            if (msg.getBuffer() != null) {
552:                                cipher.init(Cipher.ENCRYPT_MODE, desKey);
553:                                msg.setBuffer(cipher.doFinal(msg.getBuffer()));
554:                                msg.putHeader(EncryptHeader.KEY,
555:                                        new EncryptHeader(0));
556:
557:                                if (log.isInfoEnabled())
558:                                    log.info(" have DES key , package sent");
559:                            } else {
560:                                msg.setBuffer(null);
561:                                msg.putHeader(EncryptHeader.KEY,
562:                                        new EncryptHeader(0));
563:
564:                                if (log.isInfoEnabled())
565:                                    log.info(" buffer null, added header");
566:                            }
567:                        } catch (Exception e) {
568:                            e.printStackTrace();
569:                        }
570:                    }
571:                    break;
572:                }// check des key..
573:
574:                if (log.isInfoEnabled())
575:                    log.info("Pass Down: " + evt.toString());
576:                passDown(evt); // Pass on to the layer below us
577:            }
578:
579:            private SecretKey decodedKey(byte[] encodedKey) {
580:                SecretKey key = null;
581:                try {
582:                    //change needed mandar
583:                    SecretKeyFactory KeyFac = SecretKeyFactory
584:                            .getInstance(getAlgorithm(symAlgorithm));
585:                    SecretKeySpec desKeySpec = new SecretKeySpec(encodedKey,
586:                            getAlgorithm(symAlgorithm));
587:                    key = KeyFac.generateSecret(desKeySpec);
588:                } catch (Exception e) {
589:                    e.printStackTrace();
590:                }
591:                return key;
592:            }
593:
594:            private PublicKey generatePubKey(byte[] encodedKey) {
595:                PublicKey tmpPubKey = null;
596:                try {
597:                    KeyFactory KeyFac = KeyFactory
598:                            .getInstance(getAlgorithm(asymAlgorithm));
599:                    X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(
600:                            encodedKey);
601:                    tmpPubKey = KeyFac.generatePublic(x509KeySpec);
602:                } catch (Exception e) {
603:                    e.printStackTrace();
604:                }
605:                return tmpPubKey;
606:            }
607:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.