Ah. I think I found it. The following lines are incorrect:
// Setup byte[] bytes = Base64.decode(appkey.getBytes()); ... // Decrypt result byte[] decoded = Base64.decode(result1.getBytes()) appkey and result1 are UTF-8-encoded Strings of base64-encoded byte arrays: i.e. there are two levels of encoding going on here - one for base64 and another encoding for the String itself. When you call String#getBytes(), the JDK will return the bytes of that string using the platform's default encoding character set. Shiro instead explicitly uses UTF-8 for guaranteed behavior. The Base64 class will use the UTF-8 charset as required. So you just need to instead call Base64.decode(appkey) and Base64.decode(result1). HTH! Cheers, -- Les Hazlewood Founder, Katasoft, Inc. Application Security Products & Professional Apache Shiro Support and Training: http://www.katasoft.com On Tue, Apr 5, 2011 at 6:04 PM, Tauren Mills <[email protected]> wrote: > Sorry to bring this up again, but unfortunately my solution only works for > strings that are exactly 16 characters long. I'm not sure what I'm doing > wrong. This seems like it should be so simple... > public class CryptoTest { > private static final String appkey = "sA1FGmFsXUHwPXrJOyWYfA=="; > private static final String value16 = "ABCDEFGHIJKLMNOP"; > private static final String value8 = "ABCDEFGH"; > private static final String value13a = "4007000000027"; > private static final String value13b = "4007000000027 "; > private static final String value13c = "4007000000027..."; > private static final String value13d = "4007000000027XXX"; > public static void main(String[] args) { > doIt(value8); > doIt(value16); > doIt(value13a); > doIt(value13b); > doIt(value13c); > doIt(value13d); > } > private static void doIt(String value) { > System.out.println("-----------"); > System.out.println("Original Value: "+value); > System.out.println("Original Value length: "+value.length()); > // Setup > AesCipherService cipherService = new AesCipherService(); > byte[] bytes = Base64.decode(appkey.getBytes()); > try { > // Encrypt value > ByteSource encrypted = cipherService.encrypt(value.getBytes(), bytes); > String result1 = encrypted.toBase64(); > System.out.println("Encrypted: "+result1); > System.out.println("Encrypted length: "+result1.length()); > // Decrypt result > byte[] decoded = Base64.decode(result1.getBytes()); > System.out.println("Decoded length: "+decoded.length); > System.out.println("Decoded: "+new SimpleByteSource(decoded).toString()); > ByteSource decrypted = cipherService.decrypt(decoded, bytes); > System.out.println("Decrypted: "+new > SimpleByteSource(Base64.decode(decrypted.getBytes())).toString()); > } catch (CryptoException e) { > e.printStackTrace(); > } > } > } > This produces the following output: > ----------- > Original Value: ABCDEFGH > Original Value length: 8 > Encrypted: DARMjklfVRyLoD1irf0apGStAymsKOEHR1/muvhwbag= > Encrypted length: 44 > Decoded length: 32 > Decoded: DARMjklfVRyLoD1irf0apGStAymsKOEHR1/muvhwbag= > Decrypted: ABCDEFGH > ----------- > Original Value: ABCDEFGHIJKLMNOP > Original Value length: 16 > Encrypted: 9ODpJ68Krm7PcjlvD7MVjmBefjijw6BTV1DWtt2mfqJxn6wccIRrs6hvV1/CWycr > Encrypted length: 64 > Decoded length: 48 > Decoded: 9ODpJ68Krm7PcjlvD7MVjmBefjijw6BTV1DWtt2mfqJxn6wccIRrs6hvV1/CWycr > Decrypted: ABCDEFGHIJKLMNOP > ----------- > Original Value: 4007000000027 > Original Value length: 13 > Encrypted: g5KGBx/79z6e/CapGmVhAbApmxpZ+v2+xcChw+bQrrE= > Encrypted length: 44 > Decoded length: 32 > Decoded: g5KGBx/79z6e/CapGmVhAbApmxpZ+v2+xcChw+bQrrE= > Decrypted: 400700000002AA== > ----------- > Original Value: 4007000000027 > Original Value length: 16 > Encrypted: qtX3ct7uVg11kZqwGiPK0JB5wi5E40+I3kHpk3bOcHu3n/cxPd/b5q8dE0CRM7aX > Encrypted length: 64 > Decoded length: 48 > Decoded: qtX3ct7uVg11kZqwGiPK0JB5wi5E40+I3kHpk3bOcHu3n/cxPd/b5q8dE0CRM7aX > Decrypted: 400700000002AA== > ----------- > Original Value: 4007000000027... > Original Value length: 16 > Encrypted: 6RdBKnJDxOMVjvQoMu6UHfZ/ZOLoEVB+lBfoBySKSax+Ivv49fA3zUhIos4LWsXG > Encrypted length: 64 > Decoded length: 48 > Decoded: 6RdBKnJDxOMVjvQoMu6UHfZ/ZOLoEVB+lBfoBySKSax+Ivv49fA3zUhIos4LWsXG > Decrypted: 400700000002AA== > ----------- > Original Value: 4007000000027XXX > Original Value length: 16 > Encrypted: heR4cKiGBuXOUWqzsTS+oy3X0ygVcc8z2iqUv+rSf79sozT00csoepRDklSXx36+ > Encrypted length: 64 > Decoded length: 48 > Decoded: heR4cKiGBuXOUWqzsTS+oy3X0ygVcc8z2iqUv+rSf79sozT00csoepRDklSXx36+ > Decrypted: 4007000000027XXX > The weird thing is that strings of numbers or letters that are 8 or 16 > characters long work. But if the string is 13 characters long and padded > with spaces or periods to make it 16 long, it still doesn't work right. > What's going on? I'm feeling quite stupid right now, hopefully someone can > clear it all up for me. > > Thanks! > Tauren > > On Tue, Apr 5, 2011 at 5:41 AM, Tauren Mills <[email protected]> wrote: >> >> Never mind, and sorry for the noise. I should stop working all night and >> get some sleep. >> I had forgotten to do a Base64.decode() before decrypting. Once I added >> that, it works a whole lot better! >> Tauren >> >> On Tue, Apr 5, 2011 at 4:53 AM, Tauren Mills <[email protected]> wrote: >>> >>> I finally got around to testing out some crypto code and am having >>> CryptoExceptions thrown when decrypting. The problem seems to be with >>> padding. I was under the assumption that Shiro took care of all that for me. >>> If not, how do I solve this? >>> import org.apache.shiro.codec.Base64; >>> import org.apache.shiro.crypto.AesCipherService; >>> import org.apache.shiro.crypto.CryptoException; >>> import org.apache.shiro.util.ByteSource; >>> public class CryptoTest { >>> private static final String appkey = "sA1FGmFsXUHwPXrJOyWYfA=="; >>> private static final String value16 = "ABCDEFGHIJKLMNOP"; >>> private static final String value8 = "ABCDEFGH"; >>> public static void main(String[] args) { >>> try { >>> doIt(value8); >>> } catch (CryptoException e) { >>> e.printStackTrace(); >>> } >>> System.out.println("-----------"); >>> try { >>> doIt(value16); >>> } catch (CryptoException e) { >>> e.printStackTrace(); >>> } >>> } >>> private static void doIt(String value) throws CryptoException { >>> System.out.println("Value: "+value); >>> System.out.println("Value length: "+value.length()); >>> // Setup >>> AesCipherService cipherService = new AesCipherService(); >>> byte[] bytes = Base64.decode(appkey.getBytes()); >>> // Encrypt value >>> ByteSource encrypted = cipherService.encrypt(value.getBytes(), bytes); >>> String result1 = encrypted.toBase64(); >>> System.out.println("Encrypted: "+result1); >>> System.out.println("Encrypted length: "+result1.length()); >>> // Decrypt result >>> ByteSource decrypted = cipherService.decrypt(result1.getBytes(), bytes); >>> String result2 = decrypted.toString(); >>> System.out.println("Decrypted: "+result2); >>> System.out.println("Decrypted length: "+result2.length()); >>> } >>> } >>> This produces the following output: >>> Value: ABCDEFGH >>> Value length: 8 >>> Encrypted: +LZxy50cDjd8UFanLoNLa8yvw6ctNLRGJsY4BzDIM8s= >>> Encrypted length: 44 >>> ----------- >>> Value: ABCDEFGHIJKLMNOP >>> Value length: 16 >>> Encrypted: >>> fgc/8DUeyt7wQkO/TS3SNwaTOVU3swTMaMHMm3mQ05jge77vrXlrxQYNoqPey1wg >>> Encrypted length: 64 >>> org.apache.shiro.crypto.CryptoException: Unable to execute 'doFinal' with >>> cipher instance [javax.crypto.Cipher@17386918]. >>> at >>> org.apache.shiro.crypto.JcaCipherService.crypt(JcaCipherService.java:464) >>> at >>> org.apache.shiro.crypto.JcaCipherService.crypt(JcaCipherService.java:447) >>> at >>> org.apache.shiro.crypto.JcaCipherService.decrypt(JcaCipherService.java:392) >>> at >>> org.apache.shiro.crypto.JcaCipherService.decrypt(JcaCipherService.java:384) >>> at com.sprtz.test.CryptoTest.doIt(CryptoTest.java:61) >>> at com.sprtz.test.CryptoTest.main(CryptoTest.java:17) >>> Caused by: javax.crypto.IllegalBlockSizeException: Input length must be >>> multiple of 16 when decrypting with padded cipher >>> at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..) >>> at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..) >>> at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..) >>> at javax.crypto.Cipher.doFinal(DashoA13*..) >>> at >>> org.apache.shiro.crypto.JcaCipherService.crypt(JcaCipherService.java:461) >>> ... 5 more >>> org.apache.shiro.crypto.CryptoException: Unable to execute 'doFinal' with >>> cipher instance [javax.crypto.Cipher@787bb290]. >>> at >>> org.apache.shiro.crypto.JcaCipherService.crypt(JcaCipherService.java:464) >>> at >>> org.apache.shiro.crypto.JcaCipherService.crypt(JcaCipherService.java:447) >>> at >>> org.apache.shiro.crypto.JcaCipherService.decrypt(JcaCipherService.java:392) >>> at >>> org.apache.shiro.crypto.JcaCipherService.decrypt(JcaCipherService.java:384) >>> at com.sprtz.test.CryptoTest.doIt(CryptoTest.java:61) >>> at com.sprtz.test.CryptoTest.main(CryptoTest.java:25) >>> Caused by: javax.crypto.BadPaddingException: Given final block not >>> properly padded >>> at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..) >>> at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..) >>> at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..) >>> at javax.crypto.Cipher.doFinal(DashoA13*..) >>> at >>> org.apache.shiro.crypto.JcaCipherService.crypt(JcaCipherService.java:461) >>> ... 5 more >>> The code seems fairly straight forward. Notice the exceptions are >>> different based on the length of the value to encrypt/decrypt. >>> I'm using 128bit AES since I don't have the extras installed. Any >>> thoughts on what I am doing wrong? >>> Thanks, >>> Tauren
