Commit 3cd73ed8dc98f7d2f29508e935e137a97d02609e

Authored by Derek Hulley
1 parent 264b8f4eed
Exists in master

Transfer code from svn to gitlab.alfresco.com

Showing 220 changed files with 32707 additions and 0 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 220 files displayed.

File was created 1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
2 <modelVersion>4.0.0</modelVersion>
3
4 <parent>
5 <groupId>org.alfresco</groupId>
6 <artifactId>alfresco-super-pom</artifactId>
7 <version>6</version>
8 </parent>
9 <artifactId>alfresco-core</artifactId>
10 <version>6.5-SNAPSHOT</version>
11 <name>Alfresco Core</name>
12 <description>Alfresco core libraries and utils</description>
13
14 <scm>
15 <connection>scm:svn:https://svn.alfresco.com/repos/alfresco-open-mirror/services/alfresco-core/trunk/</connection>
16 <developerConnection>scm:svn:https://svn.alfresco.com/repos/alfresco-enterprise/services/alfresco-core/trunk/</developerConnection>
17 </scm>
18
19 <distributionManagement>
20 <repository>
21 <id>alfresco-internal</id>
22 <url>https://artifacts.alfresco.com/nexus/content/repositories/releases</url>
23 </repository>
24 <snapshotRepository>
25 <id>alfresco-internal-snapshots</id>
26 <url>https://artifacts.alfresco.com/nexus/content/repositories/snapshots</url>
27 </snapshotRepository>
28 </distributionManagement>
29
30 <properties>
31 <dependency.spring.version>3.2.16.RELEASE</dependency.spring.version>
32 <dependency.surf.version>6.8</dependency.surf.version>
33 </properties>
34
35 <dependencies>
36 <dependency>
37 <groupId>commons-codec</groupId>
38 <artifactId>commons-codec</artifactId>
39 <version>1.10</version>
40 </dependency>
41 <dependency>
42 <groupId>commons-collections</groupId>
43 <artifactId>commons-collections</artifactId>
44 <version>3.2.2</version>
45 </dependency>
46 <dependency>
47 <groupId>commons-httpclient</groupId>
48 <artifactId>commons-httpclient</artifactId>
49 <version>3.1-HTTPCLIENT-1265</version>
50 </dependency>
51 <dependency>
52 <groupId>commons-logging</groupId>
53 <artifactId>commons-logging</artifactId>
54 <version>1.2</version>
55 </dependency>
56 <dependency>
57 <groupId>commons-io</groupId>
58 <artifactId>commons-io</artifactId>
59 <version>2.4</version>
60 </dependency>
61 <dependency>
62 <groupId>org.apache.commons</groupId>
63 <artifactId>commons-math3</artifactId>
64 <version>3.6.1</version>
65 </dependency>
66 <dependency>
67 <groupId>org.hibernate</groupId>
68 <artifactId>hibernate</artifactId>
69 <version>3.2.6-alf-20131023</version>
70 </dependency>
71 <dependency>
72 <groupId>org.safehaus.jug</groupId>
73 <artifactId>jug</artifactId>
74 <version>2.0.0</version>
75 <classifier>asl</classifier>
76 </dependency>
77 <dependency>
78 <groupId>org.mybatis</groupId>
79 <artifactId>mybatis</artifactId>
80 <version>3.3.0</version>
81 </dependency>
82 <dependency>
83 <groupId>org.mybatis</groupId>
84 <artifactId>mybatis-spring</artifactId>
85 <version>1.2.5</version>
86 <exclusions>
87 <exclusion>
88 <groupId>org.mybatis</groupId>
89 <artifactId>mybatis</artifactId>
90 </exclusion>
91 </exclusions>
92 </dependency>
93 <dependency>
94 <groupId>log4j</groupId>
95 <artifactId>log4j</artifactId>
96 <version>1.2.17</version>
97 </dependency>
98 <dependency>
99 <groupId>org.json</groupId>
100 <artifactId>json</artifactId>
101 <version>20160212</version>
102 </dependency>
103 <dependency>
104 <groupId>org.springframework</groupId>
105 <artifactId>spring-orm</artifactId>
106 <version>${dependency.spring.version}</version>
107 </dependency>
108 <dependency>
109 <groupId>org.springframework</groupId>
110 <artifactId>spring-context</artifactId>
111 <version>${dependency.spring.version}</version>
112 </dependency>
113 <dependency>
114 <groupId>org.springframework</groupId>
115 <artifactId>spring-context-support</artifactId>
116 <version>${dependency.spring.version}</version>
117 </dependency>
118 <dependency>
119 <groupId>org.quartz-scheduler</groupId>
120 <artifactId>quartz</artifactId>
121 <version>1.8.3-alfresco-patched</version>
122 </dependency>
123 <dependency>
124 <groupId>org.alfresco.surf</groupId>
125 <artifactId>spring-surf-core-configservice</artifactId>
126 <version>${dependency.surf.version}</version>
127 </dependency>
128 <dependency>
129 <groupId>com.sun.xml.bind</groupId>
130 <artifactId>jaxb-xjc</artifactId>
131 <version>2.2.7</version>
132 </dependency>
133 <dependency>
134 <groupId>com.sun.xml.bind</groupId>
135 <artifactId>jaxb-impl</artifactId>
136 <version>2.2.7</version>
137 </dependency>
138 <dependency>
139 <groupId>dom4j</groupId>
140 <artifactId>dom4j</artifactId>
141 <version>1.6.1</version>
142 </dependency>
143 <dependency>
144 <groupId>org.codehaus.guessencoding</groupId>
145 <artifactId>guessencoding</artifactId>
146 <version>1.4</version>
147 </dependency>
148 <dependency>
149 <groupId>javax.transaction</groupId>
150 <artifactId>jta</artifactId>
151 <version>1.0.1b</version>
152 </dependency>
153 <dependency>
154 <groupId>joda-time</groupId>
155 <artifactId>joda-time</artifactId>
156 <version>2.9.3</version>
157 </dependency>
158 <dependency>
159 <groupId>org.apache.httpcomponents</groupId>
160 <artifactId>httpclient</artifactId>
161 <version>4.5.2</version>
162 </dependency>
163
164 <!-- provided dependencies -->
165 <dependency>
166 <groupId>javax.servlet</groupId>
167 <artifactId>servlet-api</artifactId>
168 <version>2.5</version>
169 <scope>provided</scope>
170 </dependency>
171
172 <!-- Test only dependencies, as popped up while running mvn test -->
173 <dependency>
174 <groupId>org.slf4j</groupId>
175 <artifactId>slf4j-log4j12</artifactId>
176 <version>1.7.21</version>
177 <scope>test</scope>
178 </dependency>
179 <dependency>
180 <groupId>org.springframework</groupId>
181 <artifactId>spring-test</artifactId>
182 <version>${dependency.spring.version}</version>
183 <scope>test</scope>
184 </dependency>
185 <dependency>
186 <groupId>junit</groupId>
187 <artifactId>junit</artifactId>
188 <version>4.12</version>
189 <scope>test</scope>
190 </dependency>
191 <dependency>
192 <groupId>org.mockito</groupId>
193 <artifactId>mockito-all</artifactId>
194 <version>1.10.19</version>
195 <scope>test</scope>
196 </dependency>
197 <dependency>
198 <groupId>commons-dbcp</groupId>
199 <artifactId>commons-dbcp</artifactId>
200 <version>1.4-DBCP330</version>
201 <scope>test</scope>
202 </dependency>
203 </dependencies>
204
205 <build>
206 <pluginManagement>
207 <plugins>
208 <plugin>
209 <artifactId>maven-release-plugin</artifactId>
210 <configuration>
211 <autoVersionSubmodules>true</autoVersionSubmodules>
212 <tagNameFormat>@{project.version}</tagNameFormat>
213 </configuration>
214 </plugin>
215 </plugins>
216 </pluginManagement>
217
218 <plugins>
219 <plugin>
220 <artifactId>maven-jar-plugin</artifactId>
221 <version>2.6</version>
222 <executions>
223 <execution>
224 <goals>
225 <goal>test-jar</goal>
226 </goals>
227 </execution>
228 </executions>
229 </plugin>
230 </plugins>
231 </build>
232
233 </project>
234
src/main/java/org/alfresco/api/AlfrescoPublicApi.java
File was created 1 /*
2 * Copyright (C) 2005-2013 Alfresco Software Limited.
3 *
4 * This file is part of Alfresco
5 *
6 * Alfresco is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Alfresco is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
18 */
19 package org.alfresco.api;
20
21 import java.lang.annotation.Documented;
22 import java.lang.annotation.ElementType;
23 import java.lang.annotation.Retention;
24 import java.lang.annotation.RetentionPolicy;
25 import java.lang.annotation.Target;
26
27 /**
28 * This annotation is used to denote a class or method as part
29 * of the public API. When a class or method is so designated then
30 * we will not change it within a release in a way that would make
31 * it no longer backwardly compatible with an earlier version within
32 * the release.
33 *
34 * @author Greg Melahn
35 */
36 @Target( {ElementType.TYPE,ElementType.METHOD} )
37 @Retention(RetentionPolicy.RUNTIME)
38 @Documented
39 public @interface AlfrescoPublicApi
40 {
41 }
42
src/main/java/org/alfresco/config/AlfrescoPropertiesPersister.java
File was created 1 /*
2 * Copyright (C) 2005-2012 Alfresco Software Limited.
3 *
4 * This file is part of Alfresco
5 *
6 * Alfresco is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Alfresco is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
18 */
19 package org.alfresco.config;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.Reader;
24 import java.util.Enumeration;
25 import java.util.Properties;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.springframework.util.DefaultPropertiesPersister;
30 import org.springframework.util.StringUtils;
31
32 /**
33 * Simple extension to the{@link DefaultPropertiesPersister} to strip trailing whitespace
34 * from incoming properties.
35 *
36 * @author shane frensley
37 * @see org.springframework.util.DefaultPropertiesPersister
38 */
39 public class AlfrescoPropertiesPersister extends DefaultPropertiesPersister
40 {
41
42 private static Log logger = LogFactory.getLog(AlfrescoPropertiesPersister.class);
43
44 @Override
45 public void load(Properties props, InputStream is) throws IOException
46 {
47 super.load(props, is);
48 strip(props);
49 }
50
51 @Override
52 public void load(Properties props, Reader reader) throws IOException
53 {
54 super.load(props, reader);
55 strip(props);
56 }
57
58 public void loadFromXml(Properties props, InputStream is) throws IOException
59 {
60 super.loadFromXml(props, is);
61 strip(props);
62 }
63
64 private void strip(Properties props)
65 {
66 for (Enumeration<Object> keys = props.keys(); keys.hasMoreElements();)
67 {
68 String key = (String) keys.nextElement();
69 String val = StringUtils.trimTrailingWhitespace(props.getProperty(key));
70 if (logger.isTraceEnabled())
71 {
72 logger.trace("Trimmed trailing whitespace for property " + key + " = " + val);
73 }
74 props.setProperty(key, val);
75 }
76 }
77 }
78
src/main/java/org/alfresco/config/JndiObjectFactoryBean.java
File was created 1 /*
2 * Copyright (C) 2005-2010 Alfresco Software Limited.
3 *
4 * This file is part of Alfresco
5 *
6 * Alfresco is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Alfresco is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
18 */
19 package org.alfresco.config;
20
21 import java.sql.Connection;
22
23 import javax.naming.NamingException;
24 import javax.sql.DataSource;
25
26 /**
27 * An extended version of JndiObjectFactoryBean that actually tests a JNDI data source before falling back to its
28 * default object. Allows continued backward compatibility with old-style datasource configuration.
29 *
30 * @author dward
31 */
32 public class JndiObjectFactoryBean extends org.springframework.jndi.JndiObjectFactoryBean
33 {
34
35 @Override
36 protected Object lookup() throws NamingException
37 {
38 Object candidate = super.lookup();
39 if (candidate instanceof DataSource)
40 {
41 Connection con = null;
42 try
43 {
44 con = ((DataSource) candidate).getConnection();
45 }
46 catch (Exception e)
47 {
48 NamingException e1 = new NamingException("Unable to get connection from " + getJndiName());
49 e1.setRootCause(e);
50 throw e1;
51 }
52 finally
53 {
54 try
55 {
56 if (con != null)
57 {
58 con.close();
59 }
60 }
61 catch (Exception e)
62 {
63 }
64 }
65 }
66 return candidate;
67 }
68 }
69
src/main/java/org/alfresco/config/JndiPropertiesFactoryBean.java
File was created 1 /*
2 * Copyright (C) 2005-2010 Alfresco Software Limited.
3 *
4 * This file is part of Alfresco
5 *
6 * Alfresco is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Alfresco is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
18 */
19 package org.alfresco.config;
20
21 import java.util.Properties;
22
23 import javax.naming.NamingException;
24
25 import org.springframework.jndi.JndiTemplate;
26
27 /**
28 * An extended {@link SystemPropertiesFactoryBean} that allows properties to be set through JNDI entries in
29 * java:comp/env/properties/*. The precedence given to system properties is still as per the superclass.
30 *
31 * @author dward
32 */
33 public class JndiPropertiesFactoryBean extends SystemPropertiesFactoryBean
34 {
35 private JndiTemplate jndiTemplate = new JndiTemplate();
36
37 @Override
38 protected void resolveMergedProperty(String propertyName, Properties props)
39 {
40 try
41 {
42 Object value = this.jndiTemplate.lookup("java:comp/env/properties/" + propertyName);
43 if (value != null)
44 {
45 String stringValue = value.toString();
46 if (stringValue.length() > 0)
47 {
48 // Unfortunately, JBoss 4 wrongly expects every env-entry declared in web.xml to have an
49 // env-entry-value (even though these are meant to be decided on deployment!). So we treat the empty
50 // string as null.
51 props.setProperty(propertyName, stringValue);
52 }
53 }
54 }
55 catch (NamingException e)
56 {
57 // Fall back to merged value in props
58 }
59 }
60 }
61
src/main/java/org/alfresco/config/JndiPropertyPlaceholderConfigurer.java
File was created 1 /*
2 * Copyright (C) 2005-2010 Alfresco Software Limited.
3 *
4 * This file is part of Alfresco
5 *
6 * Alfresco is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Alfresco is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
18 */
19 package org.alfresco.config;
20
21 import java.util.Properties;
22
23 import javax.naming.NamingException;
24
25 import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
26 import org.springframework.jndi.JndiTemplate;
27
28 /**
29 * An extended {@link PropertyPlaceholderConfigurer} that allows properties to be set through JNDI entries in
30 * java:comp/env/properties/*. The precedence given to system properties is still as per the superclass.
31 *
32 * @author dward
33 */
34 public class JndiPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer
35 {
36 private JndiTemplate jndiTemplate = new JndiTemplate();
37
38 @Override
39 protected String resolvePlaceholder(String placeholder, Properties props)
40 {
41 String result = null;
42 try
43 {
44 Object value = this.jndiTemplate.lookup("java:comp/env/properties/" + placeholder);
45 if (value != null)
46 {
47 result = value.toString();
48 }
49 }
50 catch (NamingException e)
51 {
52 }
53 // Unfortunately, JBoss 4 wrongly expects every env-entry declared in web.xml to have an env-entry-value (even
54 // though these are meant to be decided on deployment!). So we treat the empty string as null.
55 return result == null || result.length() == 0 ? super.resolvePlaceholder(placeholder, props) : result;
56 }
57 }
58
src/main/java/org/alfresco/config/NonBlockingLazyInitTargetSource.java
File was created 1 /*
2 * Copyright (C) 2005-2011 Alfresco Software Limited.
3 *
4 * This file is part of Alfresco
5 *
6 * Alfresco is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Alfresco is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
18 */
19 package org.alfresco.config;
20
21 import java.util.concurrent.locks.ReadWriteLock;
22 import java.util.concurrent.locks.ReentrantReadWriteLock;
23
24 import org.springframework.aop.target.AbstractBeanFactoryBasedTargetSource;
25 import org.springframework.beans.BeansException;
26
27 /**
28 * A non-blocking version of LazyInitTargetSource.
29 *
30 * @author dward
31 */
32 public class NonBlockingLazyInitTargetSource extends AbstractBeanFactoryBasedTargetSource
33 {
34
35 private static final long serialVersionUID = 4509578245779492037L;
36 private Object target;
37 private ReadWriteLock lock = new ReentrantReadWriteLock();
38
39 public Object getTarget() throws BeansException
40 {
41 this.lock.readLock().lock();
42 try
43 {
44 if (this.target != null)
45 {
46 return this.target;
47 }
48 }
49 finally
50 {
51 this.lock.readLock().unlock();
52 }
53 this.lock.writeLock().lock();
54 try
55 {
56 if (this.target == null)
57 {
58 this.target = getBeanFactory().getBean(getTargetBeanName());
59 }
60 return this.target;
61 }
62 finally
63 {
64 this.lock.writeLock().unlock();
65 }
66 }
67 }
68
src/main/java/org/alfresco/config/PathMatchingHelper.java
File was created 1 /*
2 * Copyright (C) 2005-2010 Alfresco Software Limited.
3 *
4 * This file is part of Alfresco
5 *
6 * Alfresco is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Alfresco is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
18 */
19 package org.alfresco.config;
20
21 import java.io.IOException;
22 import java.net.URL;
23 import java.util.Set;
24
25 import org.springframework.core.io.Resource;
26 import org.springframework.util.PathMatcher;
27
28 /**
29 * An interface for plug ins to JBossEnabledResourcePatternResolver that avoids direct dependencies on
30 * application server specifics.
31 *
32 * @author dward
33 */
34 public interface PathMatchingHelper
35 {
36 /**
37 * Indicates whether this helper is capable of searching the given URL (i.e. its protocol is supported).
38 *
39 * @param rootURL
40 * the root url to be searched
41 * @return <code>true</code> if this helper is capable of searching the given URL
42 */
43 public boolean canHandle(URL rootURL);
44
45 /**
46 * Gets the resource at the given URL.
47 *
48 * @param url URL
49 * @return the resource at the given URL
50 * @throws IOException
51 * for any error
52 */
53 public Resource getResource(URL url) throws IOException;
54
55 /**
56 * Gets the set of resources under the given URL whose path matches the given sub pattern.
57 *
58 * @param matcher
59 * the matcher
60 * @param rootURL
61 * the root URL to be searched
62 * @param subPattern
63 * the ant-style pattern to match
64 * @return the set of matching resources
65 * @throws IOException
66 * for any error
67 */
68 public Set<Resource> getResources(PathMatcher matcher, URL rootURL, String subPattern) throws IOException;
69 }
src/main/java/org/alfresco/config/SystemPropertiesFactoryBean.java
File was created 1 /*
2 * Copyright (C) 2005-2010 Alfresco Software Limited.
3 *
4 * This file is part of Alfresco
5 *
6 * Alfresco is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Alfresco is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
18 */
19 package org.alfresco.config;
20
21 import java.io.IOException;
22 import java.util.Collections;
23 import java.util.HashSet;
24 import java.util.Map;
25 import java.util.Properties;
26 import java.util.Set;
27
28 import org.springframework.beans.factory.config.PropertiesFactoryBean;
29 import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
30 import org.springframework.core.Constants;
31
32 /**
33 * Like the parent <code>PropertiesFactoryBean</code>, but overrides or augments the resulting property set with values
34 * from VM system properties. As with the Spring {@link PropertyPlaceholderConfigurer} the following modes are
35 * supported:
36 * <ul>
37 * <li><b>SYSTEM_PROPERTIES_MODE_NEVER: </b>Don't use system properties at all.</li>
38 * <li><b>SYSTEM_PROPERTIES_MODE_FALLBACK: </b>Fallback to a system property only for undefined properties.</li>
39 * <li><b>SYSTEM_PROPERTIES_MODE_OVERRIDE: (DEFAULT)</b>Use a system property if it is available.</li>
40 * </ul>
41 * Note that system properties will only be included in the property set if defaults for the property have already been
42 * defined using {@link #setProperties(Properties)} or {@link #setLocations(org.springframework.core.io.Resource[])} or
43 * their names have been included explicitly in the set passed to {@link #setSystemProperties(Set)}.
44 *
45 * @author Derek Hulley
46 */
47 public class SystemPropertiesFactoryBean extends PropertiesFactoryBean
48 {
49 private static final Constants constants = new Constants(PropertyPlaceholderConfigurer.class);
50
51 private int systemPropertiesMode = PropertyPlaceholderConfigurer.SYSTEM_PROPERTIES_MODE_OVERRIDE;
52 private Set<String> systemProperties = Collections.emptySet();
53
54 /**
55 * Set the system property mode by the name of the corresponding constant, e.g. "SYSTEM_PROPERTIES_MODE_OVERRIDE".
56 *
57 * @param constantName
58 * name of the constant
59 * @throws java.lang.IllegalArgumentException
60 * if an invalid constant was specified
61 * @see #setSystemPropertiesMode
62 */
63 public void setSystemPropertiesModeName(String constantName) throws IllegalArgumentException
64 {
65 this.systemPropertiesMode = SystemPropertiesFactoryBean.constants.asNumber(constantName).intValue();
66 }
67
68 /**
69 * Set how to check system properties.
70 *
71 * @see PropertyPlaceholderConfigurer#setSystemPropertiesMode(int)
72 */
73 public void setSystemPropertiesMode(int systemPropertiesMode)
74 {
75 this.systemPropertiesMode = systemPropertiesMode;
76 }
77
78 /**
79 * Set the names of the properties that can be considered for overriding.
80 *
81 * @param systemProperties
82 * a set of properties that can be fetched from the system properties
83 */
84 public void setSystemProperties(Set<String> systemProperties)
85 {
86 this.systemProperties = systemProperties;
87 }
88
89 @SuppressWarnings("unchecked")
90 @Override
91 protected Properties mergeProperties() throws IOException
92 {
93 // First do the default merge
94 Properties props = super.mergeProperties();
95
96 // Now resolve all the merged properties
97 if (this.systemPropertiesMode == PropertyPlaceholderConfigurer.SYSTEM_PROPERTIES_MODE_NEVER)
98 {
99 // If we are in never mode, we don't refer to system properties at all
100 for (String systemProperty : (Set<String>) (Set) props.keySet())
101 {
102 resolveMergedProperty(systemProperty, props);
103 }
104 }
105 else
106 {
107 // Otherwise, we allow unset properties to drift through from the systemProperties set and potentially set
108 // ones to be overriden by system properties
109 Set<String> propNames = new HashSet<String>((Set<String>) (Set) props.keySet());
110 propNames.addAll(this.systemProperties);
111 for (String systemProperty : propNames)
112 {
113 resolveMergedProperty(systemProperty, props);
114 if (this.systemPropertiesMode == PropertyPlaceholderConfigurer.SYSTEM_PROPERTIES_MODE_FALLBACK
115 && props.containsKey(systemProperty))
116 {
117 // It's already there
118 continue;
119 }
120 // Get the system value and assign if present
121 String systemPropertyValue = System.getProperty(systemProperty);
122 if (systemPropertyValue != null)
123 {
124 props.put(systemProperty, systemPropertyValue);
125 }
126 }
127 }
128 return props;
129 }
130
131 /**
132 * Override hook. Allows subclasses to resolve a merged property from an alternative source, whilst still respecting
133 * the chosen system property fallback path.
134 *
135 * @param systemProperty String
136 * @param props Properties
137 */
138 protected void resolveMergedProperty(String systemProperty, Properties props)
139 {
140 }
141 }
142
src/main/java/org/alfresco/config/SystemPropertiesSetterBean.java
File was created 1 /*
2 * Copyright (C) 2005-2010 Alfresco Software Limited.
3 *
4 * This file is part of Alfresco
5 *
6 * Alfresco is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Alfresco is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
18 */
19 package org.alfresco.config;
20
21 import java.util.HashMap;
22 import java.util.Map;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26
27 /**
28 * Takes a set of properties and pushes them into the Java environment. Usually, VM properties
29 * are required by the system (see {@link SystemPropertiesFactoryBean} and
30 * Spring's <tt>PropertyPlaceholderConfigurer</tt>); sometimes it is necessary to take properties
31 * available to Spring and push them onto the VM.
32 * <p>
33 * For simplicity, the system property, if present, will NOT be modified. Also, property placeholders
34 * (<b>${...}</b>), empty values and null values will be ignored.
35 * <p>
36 * Use the {@link #init()} method to push the properties.
37 *
38 * @author Derek Hulley
39 * @since V3.1
40 */
41 public class SystemPropertiesSetterBean
42 {
43 private static Log logger = LogFactory.getLog(SystemPropertiesSetterBean.class);
44
45 private Map<String, String> propertyMap;
46
47 SystemPropertiesSetterBean()
48 {
49 propertyMap = new HashMap<String, String>(3);
50 }
51
52 /**
53 * Set the properties that will be pushed onto the JVM.
54 *
55 * @param propertyMap a map of <b>property name</b> to <b>property value</b>
56 */
57 public void setPropertyMap(Map<String, String> propertyMap)
58 {
59 this.propertyMap = propertyMap;
60 }
61
62 public void init()
63 {
64 for (Map.Entry<String, String> entry : propertyMap.entrySet())
65 {
66 String name = entry.getKey();
67 String value = entry.getValue();
68 // Some values can be ignored
69 if (value == null || value.length() == 0)
70 {
71 continue;
72 }
73 if (value.startsWith("${") && value.endsWith("}"))
74 {
75 continue;
76 }
77 // Check the system properties
78 if (System.getProperty(name) != null)
79 {
80 // It was already there
81 if (logger.isDebugEnabled())
82 {
83 logger.debug("\n" +
84 "Not pushing up system property: \n" +
85 " Property: " + name + "\n" +
86 " Value already present: " + System.getProperty(name) + "\n" +
87 " Value provided: " + value);
88 }
89 continue;
90 }
91 System.setProperty(name, value);
92 }
93 }
94 }
95
src/main/java/org/alfresco/encoding/AbstractCharactersetFinder.java
File was created 1 /*
2 * Copyright (C) 2005-2010 Alfresco Software Limited.
3 *
4 * This file is part of Alfresco
5 *
6 * Alfresco is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Alfresco is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
18 */
19 package org.alfresco.encoding;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.nio.charset.Charset;
24 import java.util.Arrays;
25
26 import org.alfresco.error.AlfrescoRuntimeException;
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29
30 /**
31 * @since 2.1
32 * @author Derek Hulley
33 */
34 public abstract class AbstractCharactersetFinder implements CharactersetFinder
35 {
36 private static Log logger = LogFactory.getLog(AbstractCharactersetFinder.class);
37 private static boolean isDebugEnabled = logger.isDebugEnabled();
38
39 private int bufferSize;
40
41 public AbstractCharactersetFinder()
42 {
43 this.bufferSize = 8192;
44 }
45
46 /**
47 * Set the maximum number of bytes to read ahead when attempting to determine the characterset.
48 * Most characterset detectors are efficient and can process 8K of buffered data very quickly.
49 * Some, may need to be constrained a bit.
50 *
51 * @param bufferSize the number of bytes - default 8K.
52 */
53 public void setBufferSize(int bufferSize)
54 {
55 this.bufferSize = bufferSize;
56 }
57
58 /**
59 * {@inheritDoc}
60 * <p>
61 * The input stream is checked to ensure that it supports marks, after which
62 * a buffer is extracted, leaving the stream in its original state.
63 */
64 public final Charset detectCharset(InputStream is)
65 {
66 // Only support marking streams
67 if (!is.markSupported())
68 {
69 throw new IllegalArgumentException("The InputStream must support marks. Wrap the stream in a BufferedInputStream.");
70 }
71 try
72 {
73 int bufferSize = getBufferSize();
74 if (bufferSize < 0)
75 {
76 throw new RuntimeException("The required buffer size may not be negative: " + bufferSize);
77 }
78 // Mark the stream for just a few more than we actually will need
79 is.mark(bufferSize);
80 // Create a buffer to hold the data
81 byte[] buffer = new byte[bufferSize];
82 // Fill it
83 int read = is.read(buffer);
84 // Create an appropriately sized buffer
85 if (read > -1 && read < buffer.length)
86 {
87 byte[] copyBuffer = new byte[read];
88 System.arraycopy(buffer, 0, copyBuffer, 0, read);
89 buffer = copyBuffer;
90 }
91 // Detect
92 return detectCharset(buffer);
93 }
94 catch (IOException e)
95 {
96 // Attempt a reset
97 throw new AlfrescoRuntimeException("IOException while attempting to detect charset encoding.", e);
98 }
99 finally
100 {
101 try { is.reset(); } catch (Throwable ee) {}
102 }
103 }
104
105 public final Charset detectCharset(byte[] buffer)
106 {
107 try
108 {
109 Charset charset = detectCharsetImpl(buffer);
110 // Done
111 if (isDebugEnabled)
112 {
113 if (charset == null)
114 {
115 // Read a few characters for debug purposes
116 logger.debug("\n" +
117 "Failed to identify stream character set: \n" +
118 " Guessed 'chars': " + Arrays.toString(buffer));
119 }
120 else
121 {
122 // Read a few characters for debug purposes
123 logger.debug("\n" +
124 "Identified character set from stream:\n" +
125 " Charset: " + charset + "\n" +
126 " Detected chars: " + new String(buffer, charset.name()));
127 }
128 }
129 return charset;
130 }
131 catch (Throwable e)
132 {
133 logger.error("IOException while attempting to detect charset encoding.", e);
134 return null;
135 }
136 }
137
138 /**
139 * Some implementations may only require a few bytes to do detect the stream type,
140 * whilst others may be more efficient with larger buffers. In either case, the
141 * number of bytes actually present in the buffer cannot be enforced.
142 * <p>
143 * Only override this method if there is a very compelling reason to adjust the buffer
144 * size, and then consider handling the {@link #setBufferSize(int)} method by issuing a
145 * warning. This will prevent users from setting the buffer size when it has no effect.
146 *
147 * @return Returns the maximum desired size of the buffer passed
148 * to the {@link CharactersetFinder#detectCharset(byte[])} method.
149 *
150 * @see #setBufferSize(int)
151 */
152 protected int getBufferSize()
153 {
154 return bufferSize;
155 }
156
157 /**
158 * Worker method for implementations to override. All exceptions will be reported and
159 * absorbed and <tt>null</tt> returned.
160 * <p>
161 * The interface contract is that the data buffer must not be altered in any way.
162 *
163 * @param buffer the buffer of data no bigger than the requested
164 * {@linkplain #getBufferSize() best buffer size}. This can,
165 * very efficiently, be turned into an <tt>InputStream</tt> using a
166 * <tt>ByteArrayInputStream<tt>.
167 * @return Returns the charset or <tt>null</tt> if an accurate conclusion
168 * is not possible
169 * @throws Exception Any exception, checked or not
170 */
171 protected abstract Charset detectCharsetImpl(byte[] buffer) throws Exception;
172 }
173
src/main/java/org/alfresco/encoding/BomCharactersetFinder.java
File was created 1 /*
2 * Copyright (C) 2005-2010 Alfresco Software Limited.
3 *
4 * This file is part of Alfresco
5 *
6 * Alfresco is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Alfresco is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
18 */
19 package org.alfresco.encoding;
20
21 import java.io.ByteArrayInputStream;
22 import java.io.InputStreamReader;
23 import java.nio.charset.Charset;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27
28 /**
29 * Byte Order Marker encoding detection.
30 *
31 * @since 2.1
32 * @author Pacific Northwest National Lab
33 * @author Derek Hulley
34 */
35 public class BomCharactersetFinder extends AbstractCharactersetFinder
36 {
37 private static Log logger = LogFactory.getLog(BomCharactersetFinder.class);
38
39 @Override
40 public void setBufferSize(int bufferSize)
41 {
42 logger.warn("Setting the buffersize has no effect for charset finder: " + BomCharactersetFinder.class.getName());
43 }
44
45 /**
46 * @return Returns 64
47 */
48 @Override
49 protected int getBufferSize()
50 {
51 return 64;
52 }
53
54 /**
55 * Just searches the Byte Order Marker, i.e. the first three characters for a sign of
56 * the encoding.
57 */
58 protected Charset detectCharsetImpl(byte[] buffer) throws Exception
59 {
60 Charset charset = null;
61 ByteArrayInputStream bis = null;
62 try
63 {
64 bis = new ByteArrayInputStream(buffer);
65 bis.mark(3);
66 char[] byteHeader = new char[3];
67 InputStreamReader in = new InputStreamReader(bis);
68 int bytesRead = in.read(byteHeader);
69 bis.reset();
70
71 if (bytesRead < 2)
72 {
73 // ASCII
74 charset = Charset.forName("Cp1252");
75 }
76 else if (
77 byteHeader[0] == 0xFE &&
78 byteHeader[1] == 0xFF)
79 {
80 // UCS-2 Big Endian
81 charset = Charset.forName("UTF-16BE");
82 }
83 else if (
84 byteHeader[0] == 0xFF &&
85 byteHeader[1] == 0xFE)
86 {
87 // UCS-2 Little Endian
88 charset = Charset.forName("UTF-16LE");
89 }
90 else if (
91 bytesRead >= 3 &&
92 byteHeader[0] == 0xEF &&
93 byteHeader[1] == 0xBB &&
94 byteHeader[2] == 0xBF)
95 {
96 // UTF-8
97 charset = Charset.forName("UTF-8");
98 }
99 else
100 {
101 // No idea
102 charset = null;
103 }
104 // Done
105 return charset;
106 }
107 finally
108 {
109 if (bis != null)
110 {
111 try { bis.close(); } catch (Throwable e) {}
112 }
113 }
114 }
115 }
116
src/main/java/org/alfresco/encoding/CharactersetFinder.java
File was created 1 /*
2 * Copyright (C) 2005-2010 Alfresco Software Limited.
3 *
4 * This file is part of Alfresco
5 *
6 * Alfresco is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Alfresco is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
18 */
19 package org.alfresco.encoding;
20
21 import java.io.BufferedInputStream;
22 import java.io.InputStream;
23 import java.nio.charset.Charset;
24
25 /**
26 * Interface for classes that are able to read a text-based input stream and determine
27 * the character encoding.
28 * <p>
29 * There are quite a few libraries that do this, but none are perfect. It is therefore
30 * necessary to abstract the implementation to allow these finders to be configured in
31 * as required.
32 * <p>
33 * Implementations should have a default constructor and be completely thread safe and
34 * stateless. This will allow them to be constructed and held indefinitely to do the
35 * decoding work.
36 * <p>
37 * Where the encoding cannot be determined, it is left to the client to decide what to do.
38 * Some implementations may guess and encoding or use a default guess - it is up to the
39 * implementation to specify the behaviour.
40 *
41 * @since 2.1
42 * @author Derek Hulley
43 */
44 public interface CharactersetFinder
45 {
46 /**
47 * Attempt to detect the character set encoding for the give input stream. The input
48 * stream will not be altered or closed by this method, and must therefore support
49 * marking. If the input stream available doesn't support marking, then it can be wrapped with
50 * a {@link BufferedInputStream}.
51 * <p>
52 * The current state of the stream will be restored before the method returns.
53 *
54 * @param is an input stream that must support marking
55 * @return Returns the encoding of the stream,
56 * or <tt>null</tt> if encoding cannot be identified
57 */
58 public Charset detectCharset(InputStream is);
59
60 /**
61 * Attempt to detect the character set encoding for the given buffer.
62 *
63 * @param buffer the first <i>n</i> bytes of the character stream
64 * @return Returns the encoding of the buffer,
65 * or <tt>null</tt> if encoding cannot be identified
66 */
67 public Charset detectCharset(byte[] buffer);
68 }
69
src/main/java/org/alfresco/encoding/GuessEncodingCharsetFinder.java
File was created 1 /*
2 * Copyright (C) 2005-2010 Alfresco Software Limited.
3 *
4 * This file is part of Alfresco
5 *
6 * Alfresco is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Alfresco is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
18 */
19 package org.alfresco.encoding;
20
21 import java.nio.charset.Charset;
22 import java.nio.charset.CharsetDecoder;
23 import java.nio.charset.CharsetEncoder;
24
25 import com.glaforge.i18n.io.CharsetToolkit;
26
27 /**
28 * Uses the <a href="http://glaforge.free.fr/wiki/index.php?wiki=GuessEncoding">Guess Encoding</a>
29 * library.
30 *
31 * @since 2.1
32 * @author Derek Hulley
33 */
34 public class GuessEncodingCharsetFinder extends AbstractCharactersetFinder
35 {
36 /** Dummy charset to detect the default guess */
37 private static final Charset DUMMY_CHARSET = new DummyCharset();
38
39 @Override
40 protected Charset detectCharsetImpl(byte[] buffer) throws Exception
41 {
42 CharsetToolkit charsetToolkit = new CharsetToolkit(buffer, DUMMY_CHARSET);
43 charsetToolkit.setEnforce8Bit(true); // Force the default instead of a guess
44 Charset charset = charsetToolkit.guessEncoding();
45 if (charset == DUMMY_CHARSET)
46 {
47 return null;
48 }
49 else
50 {
51 return charset;
52 }
53 }
54
55 /**
56 * A dummy charset to detect a default hit.
57 */
58 public static class DummyCharset extends Charset
59 {
60 DummyCharset()
61 {
62 super("dummy", new String[] {});
63 }
64
65 @Override
66 public boolean contains(Charset cs)
67 {
68 throw new UnsupportedOperationException();
69 }
70
71 @Override
72 public CharsetDecoder newDecoder()
73 {
74 throw new UnsupportedOperationException();
75 }
76
77 @Override
78 public CharsetEncoder newEncoder()
79 {
80 throw new UnsupportedOperationException();
81 }
82
83 }
84 }
85
src/main/java/org/alfresco/encryption/AbstractEncryptor.java
File was created 1 /*
2 * Copyright (C) 2005-2011 Alfresco Software Limited.
3 *
4 * This file is part of Alfresco
5 *
6 * Alfresco is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Alfresco is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
18 */
19 package org.alfresco.encryption;
20
21 import java.io.ByteArrayInputStream;
22 import java.io.ByteArrayOutputStream;
23 import java.io.InputStream;
24 import java.io.ObjectInputStream;
25 import java.io.ObjectOutputStream;
26 import java.io.Serializable;
27 import java.security.AlgorithmParameters;
28 import java.security.InvalidKeyException;
29 import java.security.Key;
30
31 import javax.crypto.Cipher;
32 import javax.crypto.CipherInputStream;
33 import javax.crypto.SealedObject;
34
35 import org.alfresco.error.AlfrescoRuntimeException;
36 import org.alfresco.util.Pair;
37 import org.alfresco.util.PropertyCheck;
38 import org.apache.commons.logging.Log;
39 import org.apache.commons.logging.LogFactory;
40
41 /**
42 * Basic support for encryption engines.
43 *
44 * @since 4.0
45 */
46 public abstract class AbstractEncryptor implements Encryptor
47 {
48 protected static final Log logger = LogFactory.getLog(Encryptor.class);
49 protected String cipherAlgorithm;
50 protected String cipherProvider;
51
52 protected KeyProvider keyProvider;
53
54 /**
55 * Constructs with defaults
56 */
57 protected AbstractEncryptor()
58 {
59 }
60
61 /**
62 * @param keyProvider provides encryption keys based on aliases
63 */
64 public void setKeyProvider(KeyProvider keyProvider)
65 {
66 this.keyProvider = keyProvider;
67 }
68
69 public KeyProvider getKeyProvider()
70 {
71 return keyProvider;
72 }
73
74 public void init()
75 {
76 PropertyCheck.mandatory(this, "keyProvider", keyProvider);
77 }
78
79 /**
80 * Factory method to be written by implementations to construct <b>and initialize</b>
81 * physical ciphering objects.
82 *
83 * @param keyAlias the key alias
84 * @param params algorithm-specific parameters
85 * @param mode the cipher mode
86 * @return Cipher
87 */
88 protected abstract Cipher getCipher(String keyAlias, AlgorithmParameters params, int mode);
89
90 /**
91 * {@inheritDoc}
92 */
93 @Override
94 public Pair<byte[], AlgorithmParameters> encrypt(String keyAlias, AlgorithmParameters params, byte[] input)
95 {
96 Cipher cipher = getCipher(keyAlias, params, Cipher.ENCRYPT_MODE);
97 if (cipher == null)
98 {
99 return new Pair<byte[], AlgorithmParameters>(input, null);
100 }
101 try
102 {
103 byte[] output = cipher.doFinal(input);
104 params = cipher.getParameters();
105 return new Pair<byte[], AlgorithmParameters>(output, params);
106 }
107 catch (Throwable e)
108 {
109 // cipher.init(Cipher.ENCRYPT_MODE, key, params);
110 throw new AlfrescoRuntimeException("Decryption failed for key alias: " + keyAlias, e);
111 }
112 }
113
114 protected void resetCipher()
115 {
116
117 }
118
119 /**
120 * {@inheritDoc}
121 */
122 @Override
123 public byte[] decrypt(String keyAlias, AlgorithmParameters params, byte[] input)
124 {
125 Cipher cipher = getCipher(keyAlias, params, Cipher.DECRYPT_MODE);
126 if (cipher == null)
127 {
128 return input;
129 }
130 try
131 {
132 return cipher.doFinal(input);
133 }
134 catch (Throwable e)
135 {
136 throw new AlfrescoRuntimeException("Decryption failed for key alias: " + keyAlias, e);
137 }
138 }
139
140 /**
141 * {@inheritDoc}
142 */
143 @Override
144 public InputStream decrypt(String keyAlias, AlgorithmParameters params, InputStream input)
145 {
146 Cipher cipher = getCipher(keyAlias, params, Cipher.DECRYPT_MODE);
147 if (cipher == null)
148 {
149 return input;
150 }
151
152 try
153 {
154 return new CipherInputStream(input, cipher);
155 }
156 catch (Throwable e)
157 {
158 throw new AlfrescoRuntimeException("Decryption failed for key alias: " + keyAlias, e);
159 }
160 }
161
162 /**
163 * {@inheritDoc}
164 * <p/>
165 * Serializes and {@link #encrypt(String, AlgorithmParameters, byte[]) encrypts} the input data.
166 */
167 @Override
168 public Pair<byte[], AlgorithmParameters> encryptObject(String keyAlias, AlgorithmParameters params, Object input)
169 {
170 try
171 {
172 ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
173 ObjectOutputStream oos = new ObjectOutputStream(bos);
174 oos.writeObject(input);
175 byte[] unencrypted = bos.toByteArray();
176 return encrypt(keyAlias, params, unencrypted);
177 }
178 catch (Exception e)
179 {
180 throw new AlfrescoRuntimeException("Failed to serialize or encrypt object", e);
181 }
182 }
183
184 /**
185 * {@inheritDoc}
186 * <p/>
187 * {@link #decrypt(String, AlgorithmParameters, byte[]) Decrypts} and deserializes the input data
188 */
189 @Override
190 public Object decryptObject(String keyAlias, AlgorithmParameters params, byte[] input)
191 {
192 try
193 {
194 byte[] unencrypted = decrypt(keyAlias, params, input);
195 ByteArrayInputStream bis = new ByteArrayInputStream(unencrypted);
196 ObjectInputStream ois = new ObjectInputStream(bis);
197 Object obj = ois.readObject();
198 return obj;
199 }
200 catch (Exception e)
201 {
202 throw new AlfrescoRuntimeException("Failed to deserialize or decrypt object", e);
203 }
204 }
205
206 @Override
207 public Serializable sealObject(String keyAlias, AlgorithmParameters params, Serializable input)
208 {
209 if (input == null)
210 {
211 return null;
212 }
213 Cipher cipher = getCipher(keyAlias, params, Cipher.ENCRYPT_MODE);
214 if (cipher == null)
215 {
216 return input;
217 }
218 try
219 {
220 return new SealedObject(input, cipher);
221 }
222 catch (Exception e)
223 {
224 throw new AlfrescoRuntimeException("Failed to seal object", e);
225 }
226 }
227
228 @Override
229 public Serializable unsealObject(String keyAlias, Serializable input) throws InvalidKeyException
230 {
231 if (input == null)
232 {
233 return input;
234 }
235 // Don't unseal it if it is not sealed
236 if (!(input instanceof SealedObject))
237 {
238 return input;
239 }
240 // Get the Key, rather than a Cipher
241 Key key = keyProvider.getKey(keyAlias);
242 if (key == null)
243 {
244 // The client will be expecting to unseal the object
245 throw new IllegalStateException("No key matching " + keyAlias + ". Cannot unseal object.");
246 }
247 // Unseal it using the key
248 SealedObject sealedInput = (SealedObject) input;
249 try
250 {
251 Serializable output = (Serializable) sealedInput.getObject(key);
252 // Done
253 return output;
254 }
255 catch(InvalidKeyException e)
256 {
257 // let these through, can be useful to client code to know this is the cause
258 throw e;
259 }
260 catch (Exception e)
261 {
262 throw new AlfrescoRuntimeException("Failed to unseal object", e);
263 }
264 }
265
266 public void setCipherAlgorithm(String cipherAlgorithm)
267 {
268 this.cipherAlgorithm = cipherAlgorithm;
269 }
270
271 public String getCipherAlgorithm()
272 {
273 return this.cipherAlgorithm;
274 }
275
276 public void setCipherProvider(String cipherProvider)
277 {
278 this.cipherProvider = cipherProvider;
279 }
280
281 public String getCipherProvider()
282 {
283 return this.cipherProvider;
284 }
285
286 /**
287 * {@inheritDoc}
288 */
289 @Override
290 public AlgorithmParameters decodeAlgorithmParameters(byte[] encoded)
291 {
292 try
293 {
294 AlgorithmParameters p = null;
295 String algorithm = "DESede";
296 if(getCipherProvider() != null)
297 {
298 p = AlgorithmParameters.getInstance(algorithm, getCipherProvider());
299 }
300 else
301 {
302 p = AlgorithmParameters.getInstance(algorithm);
303 }
304 p.init(encoded);
305 return p;
306 }
307 catch(Exception e)
308 {
309 throw new AlfrescoRuntimeException("", e);
310 }
311 }
312 }
313
src/main/java/org/alfresco/encryption/AbstractKeyProvider.java
File was created 1 /*
2 * Copyright (C) 2005-2011 Alfresco Software Limited.
3 *
4 * This file is part of Alfresco
5 *
6 * Alfresco is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Alfresco is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
18 */
19 package org.alfresco.encryption;
20
21 /**
22 * Basic support for key providers
23 * <p/>
24 * TODO: This class will provide the alias name mapping so that use-cases can be mapped
25 * to different alias names in the keystore.
26 *
27 * @author Derek Hulley
28 * @since 4.0
29 */
30 public abstract class AbstractKeyProvider implements KeyProvider
31 {
32 /*
33 * Not a useless class.
34 */
35 }
36
src/main/java/org/alfresco/encryption/AlfrescoKeyStore.java
File was created 1 /*
2 * Copyright (C) 2005-2011 Alfresco Software Limited.
3 *
4 * This file is part of Alfresco
5 *
6 * Alfresco is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Alfresco is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
18 */
19 package org.alfresco.encryption;
20
21 import java.security.Key;
22 import java.util.Set;
23
24 import javax.net.ssl.KeyManager;
25 import javax.net.ssl.TrustManager;
26
27 /**
28 * Manages a Java Keystore for Alfresco, including caching keys where appropriate.
29 *
30 * @since 4.0
31 *
32 */
33 public interface AlfrescoKeyStore
34 {
35 public static final String KEY_KEYSTORE_PASSWORD = "keystore.password";
36
37 /**
38 * The name of the keystore.
39 *
40 * @return the name of the keystore.
41 */
42 public String getName();
43
44 /**
45 * Backup the keystore to the backup location. Write the keys to the backup keystore.
46 */
47 public void backup();
48
49 /**
50 * The key store parameters.
51 *
52 * @return KeyStoreParameters
53 */
54 public KeyStoreParameters getKeyStoreParameters();
55
56 /**
57 * The backup key store parameters.
58 *
59 * @return * @return
60
61 */
62 public KeyStoreParameters getBackupKeyStoreParameters();
63
64 /**
65 * Does the underlying key store exist?
66 *
67 * @return true if it exists, false otherwise
68 */
69 public boolean exists();
70
71 /**
72 * Return the key with the given key alias.
73 *
74 * @param keyAlias String
75 * @return Key
76 */
77 public Key getKey(String keyAlias);
78
79 /**
80 * Return the timestamp (in ms) of when the key was last loaded from the keystore on disk.
81 *
82 * @param keyAlias String
83 * @return long
84 */
85 public long getKeyTimestamp(String keyAlias);
86
87 /**
88 * Return the backup key with the given key alias.
89 *
90 * @param keyAlias String
91 * @return Key
92 */
93 public Key getBackupKey(String keyAlias);
94
95 /**
96 * Return all key aliases in the key store.
97 *
98 * @return Set<String>
99 */
100 public Set<String> getKeyAliases();
101
102 /**
103 * Create an array of key managers from keys in the key store.
104 *
105 * @return KeyManager[]
106 */
107 public KeyManager[] createKeyManagers();
108
109 /**
110 * Create an array of trust managers from certificates in the key store.
111 *
112 * @return TrustManager[]
113 */
114 public TrustManager[] createTrustManagers();
115
116 /**
117 * Create the key store if it doesn't exist.
118 * A key for each key alias will be written to the keystore on disk, either from the cached keys or, if not present, a key will be generated.
119 */
120 public void create();
121
122 /**
123 * Reload the keys from the key store.
124 */
125 public void reload() throws InvalidKeystoreException, MissingKeyException;
126
127 /**
128 * Check that the keys in the key store are valid i.e. that they match those registered.
129 */
130 public void validateKeys() throws InvalidKeystoreException, MissingKeyException;
131
132 }
133
src/main/java/org/alfresco/encryption/AlfrescoKeyStoreImpl.java
File was created 1 /*
2 * Copyright (C) 2005-2011 Alfresco Software Limited.
3 *
4 * This file is part of Alfresco
5 *
6 * Alfresco is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Alfresco is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
18 */
19 package org.alfresco.encryption;
20
21 import java.io.FileNotFoundException;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.security.InvalidKeyException;
27 import java.security.Key;
28 import java.security.KeyFactory;
29 import java.security.KeyStore;
30 import java.security.KeyStoreException;
31 import java.security.NoSuchAlgorithmException;
32 import java.security.PrivateKey;
33 import java.security.SecureRandom;
34 import java.security.UnrecoverableKeyException;
35 import java.security.cert.Certificate;
36 import java.security.cert.CertificateException;
37 import java.security.cert.CertificateFactory;
38 import java.security.cert.X509Certificate;
39 import java.security.spec.InvalidKeySpecException;
40 import java.security.spec.PKCS8EncodedKeySpec;
41 import java.util.Collection;
42 import java.util.Collections;
43 import java.util.HashMap;
44 import java.util.HashSet;
45 import java.util.Map;
46 import java.util.Map.Entry;
47 import java.util.Properties;
48 import java.util.Set;
49 import java.util.StringTokenizer;
50 import java.util.concurrent.locks.ReentrantReadWriteLock;
51 import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
52 import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
53
54 import javax.crypto.SecretKey;
55 import javax.crypto.SecretKeyFactory;
56 import javax.crypto.spec.DESedeKeySpec;
57 import javax.management.openmbean.KeyAlreadyExistsException;
58 import javax.net.ssl.KeyManager;
59 import javax.net.ssl.KeyManagerFactory;
60 import javax.net.ssl.TrustManager;
61 import javax.net.ssl.TrustManagerFactory;
62
63 import org.alfresco.encryption.EncryptionKeysRegistry.KEY_STATUS;
64 import org.alfresco.error.AlfrescoRuntimeException;
65 import org.alfresco.util.PropertyCheck;
66 import org.apache.commons.codec.binary.Base64;
67 import org.apache.commons.logging.Log;
68 import org.apache.commons.logging.LogFactory;
69
70 /**
71 * This wraps a Java Keystore and caches the encryption keys. It manages the loading and caching of the encryption keys
72 * and their registration with and validation against the encryption key registry.
73 *
74 * @since 4.0
75 *
76 */
77 public class AlfrescoKeyStoreImpl implements AlfrescoKeyStore
78 {
79 private static final Log logger = LogFactory.getLog(AlfrescoKeyStoreImpl.class);
80
81 protected KeyStoreParameters keyStoreParameters;
82 protected KeyStoreParameters backupKeyStoreParameters;
83 protected KeyResourceLoader keyResourceLoader;
84 protected EncryptionKeysRegistry encryptionKeysRegistry;
85
86 protected KeyMap keys;
87 protected KeyMap backupKeys;
88 protected final WriteLock writeLock;
89 protected final ReadLock readLock;
90
91 private Set<String> keysToValidate;
92 protected boolean validateKeyChanges = false;
93
94 public AlfrescoKeyStoreImpl()
95 {
96 ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
97 writeLock = lock.writeLock();
98 readLock = lock.readLock();
99 this.keys = new KeyMap();
100 this.backupKeys = new KeyMap();
101 }
102
103 public AlfrescoKeyStoreImpl(KeyStoreParameters keyStoreParameters, KeyResourceLoader keyResourceLoader)
104 {
105 this();
106
107 this.keyResourceLoader = keyResourceLoader;
108 this.keyStoreParameters = keyStoreParameters;
109
110 safeInit();
111 }
112
113 public void init()
114 {
115 writeLock.lock();
116 try
117 {
118 safeInit();
119 }
120 finally
121 {
122 writeLock.unlock();
123 }
124 }
125
126 public void setEncryptionKeysRegistry(
127 EncryptionKeysRegistry encryptionKeysRegistry)
128 {
129 this.encryptionKeysRegistry = encryptionKeysRegistry;
130 }
131
132 public void setValidateKeyChanges(boolean validateKeyChanges)
133 {
134 this.validateKeyChanges = validateKeyChanges;
135 }
136
137 public void setKeysToValidate(Set<String> keysToValidate)
138 {
139 this.keysToValidate = keysToValidate;
140 }
141
142 public void setKeyStoreParameters(KeyStoreParameters keyStoreParameters)
143 {
144 this.keyStoreParameters = keyStoreParameters;
145 }
146
147 public void setBackupKeyStoreParameters(
148 KeyStoreParameters backupKeyStoreParameters)
149 {
150 this.backupKeyStoreParameters = backupKeyStoreParameters;
151 }
152
153 public void setKeyResourceLoader(KeyResourceLoader keyResourceLoader)
154 {
155 this.keyResourceLoader = keyResourceLoader;
156 }
157
158 public KeyStoreParameters getKeyStoreParameters()
159 {
160 return keyStoreParameters;
161 }
162
163 public KeyStoreParameters getBackupKeyStoreParameters()
164 {
165 return backupKeyStoreParameters;
166 }
167
168 public KeyResourceLoader getKeyResourceLoader()
169 {
170 return keyResourceLoader;
171 }
172
173 /**
174 * {@inheritDoc}
175 */
176 @Override
177 public String getName()
178 {
179 return keyStoreParameters.getName();
180 }
181
182 /**
183 * {@inheritDoc}
184 */
185 @Override
186 public void validateKeys() throws InvalidKeystoreException, MissingKeyException
187 {
188 validateKeys(keys, backupKeys);
189 }
190
191 /**
192 * {@inheritDoc}
193 */
194 @Override
195 public boolean exists()
196 {
197 return keyStoreExists(getKeyStoreParameters().getLocation());
198 }
199
200 /**
201 * {@inheritDoc}
202 */
203 @Override
204 public void reload() throws InvalidKeystoreException, MissingKeyException
205 {
206 KeyMap keys = loadKeyStore(getKeyStoreParameters());
207 KeyMap backupKeys = loadKeyStore(getBackupKeyStoreParameters());
208
209 validateKeys(keys, backupKeys);
210
211 // all ok, reload the keys
212 writeLock.lock();
213 try
214 {
215 this.keys = keys;
216 this.backupKeys = backupKeys;
217 }
218 finally
219 {
220 writeLock.unlock();
221 }
222 }
223
224 /**
225 * {@inheritDoc}
226 */
227 @Override
228 public Set<String> getKeyAliases()
229 {
230 return new HashSet<String>(keys.getKeyAliases());
231 }
232
233 /**
234 * {@inheritDoc}
235 */
236 @Override
237 public void backup()
238 {
239 writeLock.lock();
240 try
241 {
242 for(String keyAlias : keys.getKeyAliases())
243 {
244 backupKeys.setKey(keyAlias, keys.getKey(keyAlias));
245 }
246 createKeyStore(backupKeyStoreParameters, backupKeys);
247 }
248 finally
249 {
250 writeLock.unlock();
251 }
252 }
253
254 /**
255 * {@inheritDoc}
256 */
257 @Override
258 public void create()
259 {
260 createKeyStore(keyStoreParameters, keys);
261 }
262
263 /**
264 * {@inheritDoc}
265 */
266 @Override
267 public Key getKey(String keyAlias)
268 {
269 readLock.lock();
270 try
271 {
272 return keys.getCachedKey(keyAlias).getKey();
273 }
274 finally
275 {
276 readLock.unlock();
277 }
278 }
279
280 /**
281 * {@inheritDoc}
282 */
283 @Override
284 public long getKeyTimestamp(String keyAlias)
285 {
286 readLock.lock();
287 try
288 {
289 CachedKey cachedKey = keys.getCachedKey(keyAlias);
290 return cachedKey.getTimestamp();
291 }
292 finally
293 {
294 readLock.unlock();
295 }
296 }
297
298 /**
299 * {@inheritDoc}
300 */
301 @Override
302 public Key getBackupKey(String keyAlias)
303 {
304 readLock.lock();
305 try
306 {
307 return backupKeys.getCachedKey(keyAlias).getKey();
308 }
309 finally
310 {
311 readLock.unlock();
312 }
313 }
314
315 /**
316 * {@inheritDoc}
317 */
318 @Override
319 public KeyManager[] createKeyManagers()
320 {
321 KeyInfoManager keyInfoManager = null;
322
323 try
324 {
325 keyInfoManager = getKeyInfoManager(getKeyMetaDataFileLocation());
326 KeyStore ks = loadKeyStore(keyStoreParameters, keyInfoManager);
327
328 logger.debug("Initializing key managers");
329 KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
330
331 String keyStorePassword = keyInfoManager.getKeyStorePassword();
332 kmfactory.init(ks, keyStorePassword != null ? keyStorePassword.toCharArray(): null);
333 return kmfactory.getKeyManagers();
334 }
335 catch(Throwable e)
336 {
337 throw new AlfrescoRuntimeException("Unable to create key manager", e);
338 }
339 finally
340 {
341 if(keyInfoManager != null)
342 {
343 keyInfoManager.clear();
344 }
345 }
346 }
347
348 /**
349 * {@inheritDoc}
350 */
351 @Override
352 public TrustManager[] createTrustManagers()
353 {
354 KeyInfoManager keyInfoManager = null;
355
356 try
357 {
358 keyInfoManager = getKeyInfoManager(getKeyMetaDataFileLocation());
359 KeyStore ks = loadKeyStore(getKeyStoreParameters(), keyInfoManager);
360
361 logger.debug("Initializing trust managers");
362 TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(
363 TrustManagerFactory.getDefaultAlgorithm());
364 tmfactory.init(ks);
365 return tmfactory.getTrustManagers();
366 }
367 catch(Throwable e)
368 {
369 throw new AlfrescoRuntimeException("Unable to create key manager", e);
370 }
371 finally
372 {
373 if(keyInfoManager != null)
374 {
375 keyInfoManager.clear();
376 }
377 }
378 }
379
380 protected String getKeyMetaDataFileLocation()
381 {
382 return keyStoreParameters.getKeyMetaDataFileLocation();
383 }
384
385 protected InputStream getKeyStoreStream(String location) throws FileNotFoundException
386 {
387 if(location == null)
388 {
389 return null;
390 }
391 return keyResourceLoader.getKeyStore(location);
392 }
393
394 protected OutputStream getKeyStoreOutStream() throws FileNotFoundException
395 {
396 return new FileOutputStream(getKeyStoreParameters().getLocation());
397 }
398
399 protected KeyInfoManager getKeyInfoManager(String metadataFileLocation) throws FileNotFoundException, IOException
400 {
401 return new KeyInfoManager(metadataFileLocation, keyResourceLoader);
402 }
403
404 protected KeyMap cacheKeys(KeyStore ks, KeyInfoManager keyInfoManager)
405 throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException
406 {
407 KeyMap keys = new KeyMap();
408
409 // load and cache the keys
410 for(Entry<String, KeyInformation> keyEntry : keyInfoManager.getKeyInfo().entrySet())
411 {
412 String keyAlias = keyEntry.getKey();
413
414 KeyInformation keyInfo = keyInfoManager.getKeyInformation(keyAlias);
415 String passwordStr = keyInfo != null ? keyInfo.getPassword() : null;
416
417 // Null is an acceptable value (means no key)
418 Key key = null;
419
420 // Attempt to get the key
421 key = ks.getKey(keyAlias, passwordStr == null ? null : passwordStr.toCharArray());
422 if(key != null)
423 {
424 keys.setKey(keyAlias, key);
425 }
426 // Key loaded
427 if (logger.isDebugEnabled())
428 {
429 logger.debug(
430 "Retrieved key from keystore: \n" +
431 " Location: " + getKeyStoreParameters().getLocation() + "\n" +
432 " Provider: " + getKeyStoreParameters().getProvider() + "\n" +
433 " Type: " + getKeyStoreParameters().getType() + "\n" +
434 " Alias: " + keyAlias + "\n" +
435 " Password?: " + (passwordStr != null));
436
437 Certificate[] certs = ks.getCertificateChain(keyAlias);
438 if(certs != null)
439 {
440 logger.debug("Certificate chain '" + keyAlias + "':");
441 for(int c = 0; c < certs.length; c++)
442 {
443 if(certs[c] instanceof X509Certificate)
444 {
445 X509Certificate cert = (X509Certificate)certs[c];
446 logger.debug(" Certificate " + (c + 1) + ":");
447 logger.debug(" Subject DN: " + cert.getSubjectDN());
448 logger.debug(" Signature Algorithm: " + cert.getSigAlgName());
449 logger.debug(" Valid from: " + cert.getNotBefore() );
450 logger.debug(" Valid until: " + cert.getNotAfter());
451 logger.debug(" Issuer: " + cert.getIssuerDN());
452 }
453 }
454 }
455 }
456 }
457
458 return keys;
459 }
460
461 protected KeyStore initialiseKeyStore(String type, String provider)
462 {
463 KeyStore ks = null;
464
465 try
466 {
467 if(provider == null || provider.equals(""))
468 {
469 ks = KeyStore.getInstance(type);
470 }
471 else
472 {
473 ks = KeyStore.getInstance(type, provider);
474 }
475
476 ks.load(null, null);
477
478 return ks;
479 }
480 catch(Throwable e)
481 {
482 throw new AlfrescoRuntimeException("Unable to intialise key store", e);
483 }
484 }
485
486 protected KeyStore loadKeyStore(KeyStoreParameters keyStoreParameters, KeyInfoManager keyInfoManager)
487 {
488 String pwdKeyStore = null;
489
490 try
491 {
492 KeyStore ks = initialiseKeyStore(keyStoreParameters.getType(), keyStoreParameters.getProvider());
493
494 // Load it up
495 InputStream is = getKeyStoreStream(keyStoreParameters.getLocation());
496 if (is != null)
497 {
498 try
499 {
500 // Get the keystore password
501 pwdKeyStore = keyInfoManager.getKeyStorePassword();
502 ks.load(is, pwdKeyStore == null ? null : pwdKeyStore.toCharArray());
503 }
504 finally
505 {
506 try {is.close(); } catch (Throwable e) {}
507 }
508 }
509 else
510 {
511 // this is ok, the keystore will contain no keys.
512 logger.warn("Keystore file doesn't exist: " + keyStoreParameters.getLocation());
513 }
514
515 return ks;
516 }
517 catch(Throwable e)
518 {
519 throw new AlfrescoRuntimeException("Unable to load key store: " + keyStoreParameters.getLocation(), e);
520 }
521 finally
522 {
523 pwdKeyStore = null;
524 }
525 }
526
527 /**
528 * Initializes class
529 */
530 private void safeInit()
531 {
532 PropertyCheck.mandatory(this, "location", getKeyStoreParameters().getLocation());
533
534 // Make sure we choose the default type, if required
535 if(getKeyStoreParameters().getType() == null)
536 {
537 keyStoreParameters.setType(KeyStore.getDefaultType());
538 }
539
540 writeLock.lock();
541 try
542 {
543 keys = loadKeyStore(keyStoreParameters);
544 backupKeys = loadKeyStore(backupKeyStoreParameters);
545 }
546 finally
547 {
548 writeLock.unlock();
549 }
550 }
551
552 private KeyMap loadKeyStore(KeyStoreParameters keyStoreParameters)
553 {
554 InputStream is = null;
555 KeyInfoManager keyInfoManager = null;
556 KeyStore ks = null;
557
558 if(keyStoreParameters == null)
559 {
560 // empty key map
561 return new KeyMap();
562 }
563
564 try
565 {
566 keyInfoManager = getKeyInfoManager(keyStoreParameters.getKeyMetaDataFileLocation());
567 ks = loadKeyStore(keyStoreParameters, keyInfoManager);
568 // Loaded
569 }
570 catch (Throwable e)
571 {
572 throw new AlfrescoRuntimeException(
573 "Failed to initialize keystore: \n" +
574 " Location: " + getKeyStoreParameters().getLocation() + "\n" +
575 " Provider: " + getKeyStoreParameters().getProvider() + "\n" +
576 " Type: " + getKeyStoreParameters().getType(),
577 e);
578 }
579 finally
580 {
581 if(keyInfoManager != null)
582 {
583 keyInfoManager.clearKeyStorePassword();
584 }
585
586 if (is != null)
587 {
588 try
589 {
590 is.close();
591 }
592 catch (Throwable e)
593 {
594
595 }
596 }
597 }
598
599 try
600 {
601 // cache the keys from the keystore
602 KeyMap keys = cacheKeys(ks, keyInfoManager);
603
604 if(logger.isDebugEnabled())
605 {
606 logger.debug(
607 "Initialized keystore: \n" +
608 " Location: " + getKeyStoreParameters().getLocation() + "\n" +
609 " Provider: " + getKeyStoreParameters().getProvider() + "\n" +
610 " Type: " + getKeyStoreParameters().getType() + "\n" +
611 keys.numKeys() + " keys found");
612 }
613
614 return keys;
615 }
616 catch(Throwable e)
617 {
618 throw new AlfrescoRuntimeException(
619 "Failed to retrieve keys from keystore: \n" +
620 " Location: " + getKeyStoreParameters().getLocation() + "\n" +
621 " Provider: " + getKeyStoreParameters().getProvider() + "\n" +
622 " Type: " + getKeyStoreParameters().getType() + "\n",
623 e);
624 }
625 finally
626 {
627 // Clear key information
628 keyInfoManager.clear();
629 }
630 }
631
632 protected void createKey(String keyAlias)
633 {
634 KeyInfoManager keyInfoManager = null;
635
636 try
637 {
638 keyInfoManager = getKeyInfoManager(getKeyMetaDataFileLocation());
639 Key key = getSecretKey(keyInfoManager.getKeyInformation(keyAlias));
640 encryptionKeysRegistry.registerKey(keyAlias, key);
641 keys.setKey(keyAlias, key);
642
643 KeyStore ks = loadKeyStore(getKeyStoreParameters(), keyInfoManager);
644 ks.setKeyEntry(keyAlias, key, keyInfoManager.getKeyInformation(keyAlias).getPassword().toCharArray(), null);
645 OutputStream keyStoreOutStream = getKeyStoreOutStream();
646 ks.store(keyStoreOutStream, keyInfoManager.getKeyStorePassword().toCharArray());
647 // Workaround for MNT-15005
648 keyStoreOutStream.close();
649
650 logger.info("Created key: " + keyAlias + "\n in key store: \n" +
651 " Location: " + getKeyStoreParameters().getLocation() + "\n" +
652 " Provider: " + getKeyStoreParameters().getProvider() + "\n" +
653 " Type: " + getKeyStoreParameters().getType());
654 }
655 catch(Throwable e)
656 {
657 throw new AlfrescoRuntimeException(
658 "Failed to create key: " + keyAlias + "\n in key store: \n" +
659 " Location: " + getKeyStoreParameters().getLocation() + "\n" +
660 " Provider: " + getKeyStoreParameters().getProvider() + "\n" +
661 " Type: " + getKeyStoreParameters().getType(),
662 e);
663 }
664 finally
665 {
666 if(keyInfoManager != null)
667 {
668 keyInfoManager.clear();
669 }
670 }
671 }
672
673 protected void createKeyStore(KeyStoreParameters keyStoreParameters, KeyMap keys)
674 {
675 KeyInfoManager keyInfoManager = null;
676
677 try
678 {
679 if(!keyStoreExists(keyStoreParameters.getLocation()))
680 {
681 keyInfoManager = getKeyInfoManager(keyStoreParameters.getKeyMetaDataFileLocation());
682 KeyStore ks = initialiseKeyStore(keyStoreParameters.getType(), keyStoreParameters.getProvider());
683
684 String keyStorePassword = keyInfoManager.getKeyStorePassword();
685 if(keyStorePassword == null)
686 {
687 throw new AlfrescoRuntimeException("Key store password is null for keystore at location "
688 + getKeyStoreParameters().getLocation()
689 + ", key store meta data location" + getKeyMetaDataFileLocation());
690 }
691
692 for(String keyAlias : keys.getKeyAliases())
693 {
694 KeyInformation keyInfo = keyInfoManager.getKeyInformation(keyAlias);
695
696 Key key = keys.getKey(keyAlias);
697 if(key == null)
698 {
699 logger.warn("Key with alias " + keyAlias + " is null when creating keystore at location " + keyStoreParameters.getLocation());
700 }
701 else
702 {
703 ks.setKeyEntry(keyAlias, key, keyInfo.getPassword().toCharArray(), null);
704 }
705 }
706
707 // try
708 // {
709 // throw new Exception("Keystore creation: " + );
710 // }
711 // catch(Throwable e)
712 // {
713 // logger.debug(e.getMessage());
714 // e.printStackTrace();
715 // }
716
717 OutputStream keyStoreOutStream = getKeyStoreOutStream();
718 ks.store(keyStoreOutStream, keyStorePassword.toCharArray());
719 // Workaround for MNT-15005
720 keyStoreOutStream.close();
721 }
722 else
723 {
724 logger.warn("Can't create key store " + keyStoreParameters.getLocation() + ", already exists.");
725 }
726 }
727 catch(Throwable e)
728 {
729 throw new AlfrescoRuntimeException(
730 "Failed to create keystore: \n" +
731 " Location: " + keyStoreParameters.getLocation() + "\n" +
732 " Provider: " + keyStoreParameters.getProvider() + "\n" +
733 " Type: " + keyStoreParameters.getType(),
734 e);
735 }
736 finally
737 {
738 if(keyInfoManager != null)
739 {
740 keyInfoManager.clear();
741 }
742 }
743 }
744
745 /*
746 * For testing
747 */
748 // void createBackup()
749 // {
750 // createKeyStore(backupKeyStoreParameters, backupKeys);
751 // }
752
753 private byte[] generateKeyData()
754 {
755 try
756 {
757 SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
758 byte bytes[] = new byte[DESedeKeySpec.DES_EDE_KEY_LEN];
759 random.nextBytes(bytes);
760 return bytes;
761 }
762 catch(Exception e)
763 {
764 throw new RuntimeException("Unable to generate secret key", e);
765 }
766 }
767
768 protected Key getSecretKey(KeyInformation keyInformation) throws NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException
769 {
770 byte[] keyData = keyInformation.getKeyData();
771
772 if(keyData == null)
773 {
774 if(keyInformation.getKeyAlgorithm().equals("DESede"))
775 {
776 // no key data provided, generate key data automatically
777 keyData = generateKeyData();
778 }
779 else
780 {
781 throw new AlfrescoRuntimeException("Unable to generate secret key: key algorithm is not DESede and no keyData provided");
782 }
783 }
784
785 DESedeKeySpec keySpec = new DESedeKeySpec(keyData);
786 SecretKeyFactory kf = SecretKeyFactory.getInstance(keyInformation.getKeyAlgorithm());
787 SecretKey secretKey = kf.generateSecret(keySpec);
788 return secretKey;
789 }
790
791 void importPrivateKey(String keyAlias, String keyPassword, InputStream fl, InputStream certstream)
792 throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, CertificateException, KeyStoreException
793 {
794 KeyInfoManager keyInfoManager = null;
795
796 writeLock.lock();
797 try
798 {
799 keyInfoManager = getKeyInfoManager(getKeyMetaDataFileLocation());
800 KeyStore ks = loadKeyStore(getKeyStoreParameters(), keyInfoManager);
801
802 // loading Key
803 byte[] keyBytes = new byte[fl.available()];
804 KeyFactory kf = KeyFactory.getInstance("RSA");
805 fl.read(keyBytes, 0, fl.available());
806 fl.close();
807 PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec(keyBytes);
808 PrivateKey key = kf.generatePrivate(keysp);
809
810 // loading CertificateChain
811 CertificateFactory cf = CertificateFactory.getInstance("X.509");
812
813 @SuppressWarnings("rawtypes")
814 Collection c = cf.generateCertificates(certstream) ;
815 Certificate[] certs = new Certificate[c.toArray().length];
816
817 certs = (Certificate[])c.toArray(new Certificate[0]);
818
819 // storing keystore
820 ks.setKeyEntry(keyAlias, key, keyPassword.toCharArray(), certs);
821
822 if(logger.isDebugEnabled())
823 {
824 logger.debug("Key and certificate stored.");
825 logger.debug("Alias:"+ keyAlias);
826 }
827 OutputStream keyStoreOutStream = getKeyStoreOutStream();
828 ks.store(keyStoreOutStream, keyPassword.toCharArray());
829 // Workaround for MNT-15005
830 keyStoreOutStream.close();
831 }
832 finally
833 {
834 if(keyInfoManager != null)
835 {
836 keyInfoManager.clear();
837 }
838
839 writeLock.unlock();
840 }
841 }
842
843 public boolean backupExists()
844 {
845 return keyStoreExists(getBackupKeyStoreParameters().getLocation());
846 }
847
848 protected boolean keyStoreExists(String location)
849 {
850 try
851 {
852 InputStream is = getKeyStoreStream(location);
853 if (is == null)
854 {
855 return false;
856 }
857 else
858 {
859 try { is.close(); } catch (Throwable e) {}
860 return true;
861 }
862 }
863 catch(FileNotFoundException e)
864 {
865 return false;
866 }
867 }
868
869 /*
870 * Validates the keystore keys against the key registry, throwing exceptions if the keys have been unintentionally changed.
871 *
872 * For each key to validate:
873 *
874 * (i) no main key, no backup key, the key is registered for the main keystore -> error, must re-instate the keystore
875 * (ii) no main key, no backup key, the key is not registered -> create the main key store and register the key
876 * (iii) main key exists but is not registered -> register the key
877 * (iv) main key exists, no backup key, the key is registered -> check that the key has not changed - if it has, throw an exception
878 * (v) main key exists, backup key exists, the key is registered -> check in the registry that the backup key has not changed and then re-register main key
879 */
880 protected void validateKeys(KeyMap keys, KeyMap backupKeys) throws InvalidKeystoreException, MissingKeyException
881 {
882 if(!validateKeyChanges)
883 {
884 return;
885 }
886
887 writeLock.lock();
888 try
889 {
890 // check for the existence of a key store first
891 for(String keyAlias : keysToValidate)
892 {
893 if(keys.getKey(keyAlias) == null)
894 {
895 if(backupKeys.getKey(keyAlias) == null)
896 {
897 if(encryptionKeysRegistry.isKeyRegistered(keyAlias))
898 {
899 // The key is registered and neither key nor backup key exist -> throw
900 // an exception indicating that the key is missing and the keystore should
901 // be re-instated.
902 throw new MissingKeyException(keyAlias, getKeyStoreParameters().getLocation());
903 }
904 else
905 {
906 // Neither the key nor the backup key exist, so create the key
907 createKey(keyAlias);
908 }
909 }
910 }
911 else
912 {
913 if(!encryptionKeysRegistry.isKeyRegistered(keyAlias))
914 {
915 // The key is not registered, so register it
916 encryptionKeysRegistry.registerKey(keyAlias, keys.getKey(keyAlias));
917 }
918 else if(backupKeys.getKey(keyAlias) == null && encryptionKeysRegistry.checkKey(keyAlias, keys.getKey(keyAlias)) == KEY_STATUS.CHANGED)
919 {
920 // A key has been changed, indicating that the keystore has been un-intentionally changed.
921 // Note: this will halt the application bootstrap.
922 throw new InvalidKeystoreException("The key with alias " + keyAlias + " has been changed, re-instate the previous keystore");
923 }
924 else if(backupKeys.getKey(keyAlias) != null && encryptionKeysRegistry.isKeyRegistered(keyAlias))
925 {
926 // Both key and backup key exist and the key is registered.
927 if(encryptionKeysRegistry.checkKey(keyAlias, backupKeys.getKey(keyAlias)) == KEY_STATUS.OK)
928 {
929 // The registered key is the backup key so lets re-register the key in the main key store.
930 // Unregister the existing (now backup) key and re-register the main key.
931 encryptionKeysRegistry.unregisterKey(keyAlias);
932 encryptionKeysRegistry.registerKey(keyAlias, keys.getKey(keyAlias));
933 }
934 }
935 }
936 }
937 }
938 finally
939 {
940 writeLock.unlock();
941 }
942 }
943
944 public static class KeyInformation
945 {
946 protected String alias;
947 protected byte[] keyData;
948 protected String password;
949 protected String keyAlgorithm;
950
951 public KeyInformation(String alias, byte[] keyData, String password, String keyAlgorithm)
952 {
953 super();
954 this.alias = alias;
955 this.keyData = keyData;
956 this.password = password;
957 this.keyAlgorithm = keyAlgorithm;
958 }
959
960 public String getAlias()
961 {
962 return alias;
963 }
964
965 public byte[] getKeyData()
966 {
967 return keyData;
968 }
969
970 public String getPassword()
971 {
972 return password;
973 }
974
975 public String getKeyAlgorithm()
976 {
977 return keyAlgorithm;
978 }
979 }
980
981 /*
982 * Caches key meta data information such as password, seed.
983 *
984 */
985 public static class KeyInfoManager
986 {
987 private KeyResourceLoader keyResourceLoader;
988 private String metadataFileLocation;
989 private Properties keyProps;
990 private String keyStorePassword = null;
991 private Map<String, KeyInformation> keyInfo;
992
993 /**
994 * For testing.
995 *
996 * @param passwords
997 */
998 KeyInfoManager(Map<String, String> passwords, KeyResourceLoader keyResourceLoader)
999 {
1000 this.keyResourceLoader = keyResourceLoader;
1001 keyInfo = new HashMap<String, KeyInformation>(2);
1002 for(Map.Entry<String, String> password : passwords.entrySet())
1003 {
1004 keyInfo.put(password.getKey(), new KeyInformation(password.getKey(), null, password.getValue(), null));
1005 }
1006 }
1007
1008 KeyInfoManager(String metadataFileLocation, KeyResourceLoader keyResourceLoader) throws IOException, FileNotFoundException
1009 {
1010 this.keyResourceLoader = keyResourceLoader;
1011 this.metadataFileLocation = metadataFileLocation;
1012 keyInfo = new HashMap<String, KeyInformation>(2);
1013 loadKeyMetaData();
1014 }
1015
1016 public Map<String, KeyInformation> getKeyInfo()
1017 {
1018 // TODO defensively copy
1019 return keyInfo;
1020 }
1021
1022 /**
1023 * Set the map of key meta data (including passwords to access the keystore).
1024 * <p/>
1025 * Where required, <tt>null</tt> values must be inserted into the map to indicate the presence
1026 * of a key that is not protected by a password. They entry for {@link #KEY_KEYSTORE_PASSWORD}
1027 * is required if the keystore is password protected.
1028 */
1029 protected void loadKeyMetaData() throws IOException, FileNotFoundException
1030 {
1031 keyProps = keyResourceLoader.loadKeyMetaData(metadataFileLocation);
1032 if(keyProps != null)
1033 {
1034 String aliases = keyProps.getProperty("aliases");
1035 if(aliases == null)
1036 {
1037 throw new AlfrescoRuntimeException("Passwords file must contain an aliases key");
1038 }
1039
1040 this.keyStorePassword = keyProps.getProperty(KEY_KEYSTORE_PASSWORD);
1041
1042 StringTokenizer st = new StringTokenizer(aliases, ",");
1043 while(st.hasMoreTokens())
1044 {
1045 String keyAlias = st.nextToken();
1046 keyInfo.put(keyAlias, loadKeyInformation(keyAlias));
1047 }
1048 }
1049 else
1050 {
1051 // TODO
1052 //throw new FileNotFoundException("Cannot find key metadata file " + getKeyMetaDataFileLocation());
1053 }
1054 }
1055
1056 public void clear()
1057 {
1058 this.keyStorePassword = null;
1059 if(this.keyProps != null)
1060 {
1061 this.keyProps.clear();
1062 }
1063 }
1064
1065 public void removeKeyInformation(String keyAlias)
1066 {
1067 this.keyProps.remove(keyAlias);
1068 }
1069
1070 protected KeyInformation loadKeyInformation(String keyAlias)
1071 {
1072 String keyPassword = keyProps.getProperty(keyAlias + ".password");
1073 String keyData = keyProps.getProperty(keyAlias + ".keyData");
1074 String keyAlgorithm = keyProps.getProperty(keyAlias + ".algorithm");
1075
1076 byte[] keyDataBytes = null;
1077 if(keyData != null && !keyData.equals(""))
1078 {
1079 keyDataBytes = Base64.decodeBase64(keyData);
1080 }
1081 KeyInformation keyInfo = new KeyInformation(keyAlias, keyDataBytes, keyPassword, keyAlgorithm);
1082 return keyInfo;
1083 }
1084
1085 public String getKeyStorePassword()
1086 {
1087 return keyStorePassword;
1088 }
1089
1090 public void clearKeyStorePassword()
1091 {
1092 this.keyStorePassword = null;
1093 }
1094
1095 public KeyInformation getKeyInformation(String keyAlias)
1096 {
1097 return keyInfo.get(keyAlias);
1098 }
1099 }
1100 }
1101
src/main/java/org/alfresco/encryption/CachedKey.java
File was created 1 /*
2 * Copyright (C) 2005-2011 Alfresco Software Limited.
3 *
4 * This file is part of Alfresco
5 *
6 * Alfresco is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Alfresco is distributed in the hope that it will be useful,
12