001    package railo.runtime.security;
002    
003    import java.util.Random;
004    
005    import railo.runtime.crypt.BinConverter;
006    import railo.runtime.crypt.BlowfishCBC;
007    import railo.runtime.crypt.BlowfishECB;
008    import railo.runtime.crypt.SHA1;
009    
010    /**
011      * support class for easy string encryption with the Blowfish algorithm,
012      * now in CBC mode with a SHA-1 key setup and correct padding
013      */
014    public final class SerialDecoder {
015    
016      BlowfishCBC m_bfish;
017    
018    
019    
020      // one random generator for all simple callers...
021    
022      static Random m_rndGen;
023    
024    
025    
026      // ...and created early
027    
028      static {
029    
030        m_rndGen = new Random();
031    
032      };
033    
034    
035    
036    
037    
038    
039    
040      /**
041    
042        * constructor to set up a string as the key (oversized password will be cut)
043    
044        * @param sPassword the password (treated as a real unicode array)
045    
046        */
047    
048      public SerialDecoder(String sPassword) {
049    
050    
051    
052        // hash down the password to a 160bit key
053    
054        SHA1 hasher = new SHA1();
055    
056        hasher.update(sPassword);
057    
058        hasher.finalize();
059    
060    
061    
062        // setup the encryptor (use a dummy IV)
063    
064        m_bfish = new BlowfishCBC(hasher.getDigest(), 0);
065    
066        hasher.clear();
067    
068      };
069    
070      /**
071    
072        * decrypts a hexbin string (handling is case sensitive)
073    
074        * @param sCipherText hexbin string to decrypt
075    
076        * @return decrypted string (null equals an error)
077    
078        */
079    
080      public String decrypt(String sCipherText) {
081    
082    
083    
084        // get the number of estimated bytes in the string (cut off broken blocks)
085    
086        int nLen = (sCipherText.length() >> 1) & ~7;
087    
088    
089    
090        // does the given stuff make sense (at least the CBC IV)?
091    
092        if (nLen < BlowfishECB.BLOCKSIZE)
093    
094          return null;
095    
096    
097    
098        // get the CBC IV
099    
100        byte[] cbciv = new byte[BlowfishECB.BLOCKSIZE];
101    
102        int nNumOfBytes = BinConverter.binHexToBytes(sCipherText,
103    
104                                                     cbciv,
105    
106                                                     0,
107    
108                                                     0,
109    
110                                                     BlowfishECB.BLOCKSIZE);
111    
112        if (nNumOfBytes < BlowfishECB.BLOCKSIZE)
113    
114          return null;
115    
116        // (got it)
117    
118        m_bfish.setCBCIV(cbciv);
119    
120    
121    
122        // something left to decrypt?
123    
124        nLen -= BlowfishECB.BLOCKSIZE;
125    
126        if (nLen == 0)
127    
128          return "";
129    
130    
131    
132        // get all data bytes now
133    
134        byte[] buf = new byte[nLen];
135    
136        nNumOfBytes = BinConverter.binHexToBytes(sCipherText,
137    
138                                                 buf,
139    
140                                                 BlowfishECB.BLOCKSIZE * 2,
141    
142                                                 0,
143    
144                                                 nLen);
145    
146    
147    
148        // we cannot accept broken binhex sequences due to padding
149    
150        // and decryption
151    
152        if (nNumOfBytes < nLen)
153    
154          return null;
155    
156    
157    
158        // decrypt the buffer
159    
160        m_bfish.decrypt(buf);
161    
162    
163    
164        // get the last padding byte
165    
166        int nPadByte = buf[buf.length - 1] & 0x0ff;
167    
168        // ( try to get all information if the padding doesn't seem to be correct)
169    
170        if ((nPadByte > 8) || (nPadByte < 0))
171    
172          nPadByte = 0;
173    
174    
175    
176        // calculate the real size of this message
177    
178        nNumOfBytes -= nPadByte;
179    
180        if (nNumOfBytes < 0)
181    
182          return "";
183    
184    
185    
186        // success
187    
188        return BinConverter.byteArrayToUNCString(buf, 0, nNumOfBytes);
189    
190      };
191    
192    
193    
194    
195    
196      /**
197    
198        * destroys (clears) the encryption engine,
199    
200        * after that the instance is not valid anymore
201    
202        */
203    
204      public void destroy() {
205    
206        m_bfish.cleanUp();
207    
208      };
209    
210    };
211    
212