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