001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.core.util.crypt;
018
019import org.apache.wicket.Session;
020import org.apache.wicket.core.util.crypt.KeyInSessionSunJceCryptFactory.CryptData;
021import org.apache.wicket.util.crypt.ICrypt;
022import org.apache.wicket.util.crypt.SunJceCrypt;
023import org.apache.wicket.util.io.IClusterable;
024import org.apache.wicket.util.lang.Args;
025
026import java.security.Provider;
027import java.security.Security;
028import java.util.UUID;
029
030/**
031 * Crypt factory that produces {@link SunJceCrypt} instances based on session-specific
032 * encryption key. This allows each user to have his own encryption key, hardening against CSRF
033 * attacks.
034 * <br>
035 * Note that the use of this crypt factory will result in an immediate creation of a session.
036 *
037 * @author igor.vaynberg
038 */
039public class KeyInSessionSunJceCryptFactory extends AbstractKeyInSessionCryptFactory<CryptData>
040{
041
042        private final String cryptMethod;
043
044        /**
045         * Constructor using {@link javax.crypto.Cipher} {@value org.apache.wicket.util.crypt.SunJceCrypt#DEFAULT_CRYPT_METHOD}
046         */
047        public KeyInSessionSunJceCryptFactory()
048        {
049                this(SunJceCrypt.DEFAULT_CRYPT_METHOD);
050        }
051
052        /**
053         * Constructor that uses a custom {@link javax.crypto.Cipher}
054         *
055         * @param cryptMethod
056         *              the name of the crypt method (cipher)
057         */
058        public KeyInSessionSunJceCryptFactory(String cryptMethod)
059        {
060                this.cryptMethod = Args.notNull(cryptMethod, "Crypt method");
061
062                final Provider[] providers = Security.getProviders("Cipher." + cryptMethod);
063                if (providers == null || providers.length == 0)
064                {
065                        try
066                        {
067                                // Initialize and add a security provider required for encryption
068                                final Class<?> clazz = Class.forName("com.sun.crypto.provider.SunJCE");
069
070                                final Provider provider = (Provider) clazz.getDeclaredConstructor().newInstance();
071                                Security.addProvider(provider);
072                        }
073                        catch (Exception ex)
074                        {
075                                throw new RuntimeException("Unable to load SunJCE service provider", ex);
076                        }
077                }
078        }
079
080        /**
081         * @return the {@link org.apache.wicket.util.crypt.ICrypt} to use
082         * 
083         * @deprecated this method is no longer called TODO remove in Wicket 10
084         */
085        @Deprecated(forRemoval = true)
086        protected ICrypt createCrypt()
087        {
088                return null;
089        }
090        
091        @Override
092        protected CryptData generateKey(Session session)
093        {
094            // generate new salt
095        byte[] salt = SunJceCrypt.randomSalt();
096        
097            // generate new key
098        String key = session.getId() + "." + UUID.randomUUID().toString();
099        
100        return new CryptData(key, salt);
101        }
102        
103        @Override
104        protected ICrypt createCrypt(CryptData keyParams)
105        {
106            SunJceCrypt crypt = new SunJceCrypt(cryptMethod, keyParams.salt, 1000);
107        crypt.setKey(keyParams.key);
108        
109        return crypt;
110        }
111
112    static final class CryptData implements IClusterable
113        {
114        private static final long serialVersionUID = 1L;
115
116        final String key;
117                
118                final byte[] salt;
119                
120                CryptData(String key, byte[] salt)
121                {
122                        this.key = key;
123                        this.salt = salt;
124                }
125        }
126}