1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.cms.security;
35
36 import info.magnolia.cms.core.SystemProperty;
37 import info.magnolia.cms.exchange.ActivationManager;
38 import info.magnolia.context.MgnlContext;
39 import info.magnolia.objectfactory.Components;
40
41 import java.io.File;
42 import java.io.FileInputStream;
43 import java.io.FileNotFoundException;
44 import java.io.FileWriter;
45 import java.io.IOException;
46 import java.security.InvalidKeyException;
47 import java.security.KeyFactory;
48 import java.security.KeyPair;
49 import java.security.KeyPairGenerator;
50 import java.security.NoSuchAlgorithmException;
51 import java.security.NoSuchProviderException;
52 import java.security.PrivateKey;
53 import java.security.PublicKey;
54 import java.security.Security;
55 import java.security.spec.InvalidKeySpecException;
56 import java.security.spec.PKCS8EncodedKeySpec;
57 import java.security.spec.X509EncodedKeySpec;
58 import java.text.SimpleDateFormat;
59 import java.util.Date;
60 import java.util.Properties;
61
62 import javax.crypto.BadPaddingException;
63 import javax.crypto.Cipher;
64 import javax.crypto.IllegalBlockSizeException;
65 import javax.crypto.NoSuchPaddingException;
66 import javax.jcr.RepositoryException;
67 import javax.jcr.Session;
68
69 import org.apache.commons.lang.StringUtils;
70 import org.bouncycastle.jce.provider.BouncyCastleProvider;
71
72
73
74
75
76
77 public class SecurityUtil {
78
79 private static final String PRIVATE_KEY = "key.private";
80 private static final String PUBLIC_KEY = "key.public";
81 private static final String KEY_LOCATION_PROPERTY = "magnolia.author.key.location";
82
83
84
85
86
87 private static final String ALGORITHM = "RSA";
88
89 static {
90 Security.addProvider(new BouncyCastleProvider());
91 }
92
93
94
95
96 public static boolean isAnonymous() {
97 User user = MgnlContext.getUser();
98 return (user != null && UserManager.ANONYMOUS_USER.equals(user.getName()));
99 }
100
101 public static boolean isAuthenticated() {
102 User user = MgnlContext.getUser();
103 return (user != null && !UserManager.ANONYMOUS_USER.equals(user.getName()));
104 }
105
106 public static String decrypt(String pass) throws SecurityException {
107 return decrypt(pass, getPublicKey());
108 }
109
110 public static String decrypt(String message, String encodedKey) throws SecurityException {
111 try {
112 if (StringUtils.isBlank(encodedKey)) {
113 throw new SecurityException("Activation key was not found. Please make sure your instance is correctly configured.");
114 }
115
116
117 byte[] binaryKey = hexToByteArray(encodedKey);
118
119
120 Cipher pkCipher = Cipher.getInstance(ALGORITHM, "BC");
121 try {
122
123 X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(binaryKey);
124 KeyFactory kf = KeyFactory.getInstance(ALGORITHM, "BC");
125 PublicKey pk = kf.generatePublic(publicKeySpec);
126 pkCipher.init(Cipher.DECRYPT_MODE, pk);
127
128 } catch (InvalidKeySpecException e) {
129
130 PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(binaryKey);
131 KeyFactory kf = KeyFactory.getInstance(ALGORITHM, "BC");
132 PrivateKey pk = kf.generatePrivate(privateKeySpec);
133 pkCipher.init(Cipher.DECRYPT_MODE, pk);
134 }
135
136
137 String[] chunks = StringUtils.split(message, ";");
138 if (chunks == null) {
139 throw new SecurityException("The encrypted information is corrupted or incomplete. Please make sure someone is not trying to intercept or modify encrypted message.");
140 }
141 StringBuilder clearText = new StringBuilder();
142 for (String chunk : chunks) {
143 byte[] byteChunk = hexToByteArray(chunk);
144 clearText.append(new String(pkCipher.doFinal(byteChunk), "UTF-8"));
145 }
146 return clearText.toString();
147 } catch (NumberFormatException e) {
148 throw new SecurityException("The encrypted information is corrupted or incomplete. Please make sure someone is not trying to intercept or modify encrypted message.", e);
149 } catch (IOException e) {
150 throw new SecurityException("Failed to read authentication string. Please use Java version with cryptography support.", e);
151 } catch (NoSuchAlgorithmException e) {
152 throw new SecurityException("Failed to read authentication string. Please use Java version with cryptography support.", e);
153 } catch (NoSuchPaddingException e) {
154 throw new SecurityException("Failed to read authentication string. Please use Java version with cryptography support.", e);
155 } catch (InvalidKeySpecException e) {
156 throw new SecurityException("Failed to read authentication string. Please use Java version with cryptography support.", e);
157 } catch (InvalidKeyException e) {
158 throw new SecurityException("Failed to read authentication string. Please use Java version with cryptography support.", e);
159 } catch (NoSuchProviderException e) {
160 throw new SecurityException("Failed to find encryption provider. Please use Java version with cryptography support.", e);
161 } catch (IllegalBlockSizeException e) {
162 throw new SecurityException("Failed to decrypt message. It might have been corrupted during transport.", e);
163 } catch (BadPaddingException e) {
164 throw new SecurityException("Failed to decrypt message. It might have been corrupted during transport.", e);
165 }
166
167 }
168
169 public static String encrypt(String pass) throws SecurityException {
170 String encodedKey = getPrivateKey();
171 return encrypt(pass, encodedKey);
172 }
173
174 public static String encrypt(String message, String encodedKey) {
175 try {
176
177
178 if (StringUtils.isBlank(encodedKey)) {
179 throw new SecurityException("Activation key was not found. Please make sure your instance is correctly configured.");
180 }
181 byte[] binaryKey = hexToByteArray(encodedKey);
182
183
184 Cipher pkCipher = Cipher.getInstance(ALGORITHM, "BC");
185 try {
186
187 PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(binaryKey);
188 KeyFactory kf = KeyFactory.getInstance(ALGORITHM, "BC");
189 PrivateKey pk = kf.generatePrivate(privateKeySpec);
190
191 pkCipher.init(Cipher.ENCRYPT_MODE, pk);
192 } catch (InvalidKeySpecException e) {
193
194 X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(binaryKey);
195 KeyFactory kf = KeyFactory.getInstance(ALGORITHM, "BC");
196 PublicKey pk = kf.generatePublic(publicKeySpec);
197
198 pkCipher.init(Cipher.ENCRYPT_MODE, pk);
199 }
200
201
202 byte[] bytes = message.getBytes("UTF-8");
203
204 int start = 0;
205 StringBuilder chaos = new StringBuilder();
206 while (start < bytes.length) {
207 byte[] tmp = new byte[Math.min(bytes.length - start, binaryKey.length / 8)];
208 System.arraycopy(bytes, start, tmp, 0, tmp.length);
209 start += tmp.length;
210 byte[] encrypted = pkCipher.doFinal(tmp);
211 chaos.append(byteArrayToHex(encrypted));
212 chaos.append(";");
213 }
214 chaos.setLength(chaos.length() - 1);
215
216 return chaos.toString();
217
218 } catch (IOException e) {
219 throw new SecurityException("Failed to create authentication string. Please use Java version with cryptography support.", e);
220 } catch (NoSuchAlgorithmException e) {
221 throw new SecurityException("Failed to create authentication string. Please use Java version with cryptography support.", e);
222 } catch (NoSuchPaddingException e) {
223 throw new SecurityException("Failed to create authentication string. Please use Java version with cryptography support.", e);
224 } catch (InvalidKeySpecException e) {
225 throw new SecurityException("Failed to create authentication string. Please use Java version with cryptography support.", e);
226 } catch (InvalidKeyException e) {
227 throw new SecurityException("Failed to create authentication string. Please use Java version with cryptography support.", e);
228 } catch (NoSuchProviderException e) {
229 throw new SecurityException("Failed to find encryption provider. Please use Java version with cryptography support.", e);
230 } catch (IllegalBlockSizeException e) {
231 throw new SecurityException("Failed to encrypt string. Please use Java version with cryptography support.", e);
232 } catch (BadPaddingException e) {
233 throw new SecurityException("Failed to encrypt string. Please use Java version with cryptography support.", e);
234 }
235 }
236
237
238 public static String getPrivateKey() {
239 String path = SystemProperty.getProperty("magnolia.author.key.location");
240 try {
241 Properties defaultProps = new Properties();
242 if (path == null || !new File(path).exists()) {
243 throw new SecurityException("Private key store doesn't exist");
244 }
245 FileInputStream in = new FileInputStream(path);
246 defaultProps.load(in);
247 in.close();
248 return defaultProps.getProperty(PRIVATE_KEY);
249 } catch (FileNotFoundException e) {
250 throw new SecurityException("Failed to retrieve private key. Please make sure the key is located in " + path, e);
251 } catch (IOException e) {
252 throw new SecurityException("Failed to retrieve private key. Please make sure the key is located in " + path, e);
253 }
254 }
255
256 public static void updateKeys(MgnlKeyPair keys) {
257
258 if (keys.getPrivateKey() != null) {
259 String path = SystemProperty.getProperty(KEY_LOCATION_PROPERTY);
260 try {
261 Properties defaultProps = new Properties();
262 defaultProps.put(PRIVATE_KEY, keys.getPrivateKey());
263 defaultProps.put(PUBLIC_KEY, keys.getPublicKey());
264 File keystore = new File(path);
265 keystore.getParentFile().mkdirs();
266 FileWriter writer = new FileWriter(keystore);
267 String date = new SimpleDateFormat("dd.MMM.yyyy hh:mm").format(new Date());
268 defaultProps.store(writer, "generated " + date + " by " + MgnlContext.getUser().getName());
269 writer.close();
270 } catch (FileNotFoundException e) {
271 throw new SecurityException("Failed to store private key. Please make sure the key is located in " + path, e);
272 } catch (IOException e) {
273 throw new SecurityException("Failed to store private key. Please make sure the key is located in " + path, e);
274 }
275 }
276 try {
277 Session session = MgnlContext.getSystemContext().getJCRSession("config");
278 session.getNode("/server/activation").setProperty("publicKey", keys.getPublicKey());
279 session.save();
280 } catch (RepositoryException e) {
281 throw new SecurityException("Failed to store public key.", e);
282 }
283 }
284
285 public static String getPublicKey() {
286 ActivationManager aman = Components.getComponentProvider().getComponent(ActivationManager.class);
287 return aman.getPublicKey();
288 }
289
290 private static final String HEX = "0123456789ABCDEF";
291
292 public static String byteArrayToHex(byte[] raw) {
293 if (raw == null) {
294 return null;
295 }
296 final StringBuilder hex = new StringBuilder(2 * raw.length);
297 for (final byte b : raw) {
298 hex.append(HEX.charAt((b & 0xF0) >> 4))
299 .append(HEX.charAt((b & 0x0F)));
300 }
301 return hex.toString();
302 }
303
304 public static byte[] hexToByteArray(String s) {
305 byte[] b = new byte[s.length() / 2];
306 for (int i = 0; i < b.length; i++) {
307 int index = i * 2;
308 int v = Integer.parseInt(s.substring(index, index + 2), 16);
309 b[i] = (byte) v;
310 }
311 return b;
312 }
313
314 public static MgnlKeyPair generateKeyPair(int keyLength) throws NoSuchAlgorithmException {
315 KeyPairGenerator kgen = KeyPairGenerator.getInstance(ALGORITHM);
316 kgen.initialize(keyLength);
317 KeyPair key = kgen.genKeyPair();
318 return new MgnlKeyPair(byteArrayToHex(key.getPrivate().getEncoded()), byteArrayToHex(key.getPublic().getEncoded()));
319 }
320 }