Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 397a090f841745e24e3e1dbfc4fafc39cd4d720f (plain) (blame)
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/*******************************************************************************
 * Copyright (c) 2008 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.equinox.internal.security.win32;

import java.io.IOException;
import java.security.SecureRandom;

import javax.crypto.spec.PBEKeySpec;

import org.eclipse.equinox.internal.security.auth.AuthPlugin;
import org.eclipse.equinox.internal.security.auth.nls.SecAuthMessages;
import org.eclipse.equinox.internal.security.storage.Base64;
import org.eclipse.equinox.internal.security.win32.nls.WinCryptoMessages;
import org.eclipse.equinox.security.storage.ISecurePreferences;
import org.eclipse.equinox.security.storage.StorageException;
import org.eclipse.equinox.security.storage.provider.IPreferencesContainer;
import org.eclipse.equinox.security.storage.provider.IProviderHints;
import org.eclipse.equinox.security.storage.provider.PasswordProvider;

/**
 * Provides interface with native Windows data protection API. This provider
 * auto-generates separate passwords for each secure preferences tree.
 */
public class WinCrypto extends PasswordProvider {

	native public byte[] windecrypt(byte[] encryptedText);

	native public byte[] winencrypt(byte[] clearText);

	static {
		System.loadLibrary("jnicrypt");
	}
	
	private final static String WIN_PROVIDER_NODE = "/org.eclipse.equinox.secure.storage/windows";
	private final static String PASSWORD_KEY = "encryptedPassword";

	/**
	 * The length of the randomly generated password in bytes
	 */
	private final static int PASSWORD_LENGTH = 250;

	public PBEKeySpec getPassword(IPreferencesContainer container, int passwordType) {
		byte[] encryptedPassword;
		if ((passwordType & CREATE_NEW_PASSWORD) == 0)
			encryptedPassword = getEncryptedPassword(container);
		else
			encryptedPassword = null;
		
		if (encryptedPassword != null) {
			byte[] decryptedPassword = windecrypt(encryptedPassword);
			if (decryptedPassword != null) {
				String password = new String(decryptedPassword);
				return new PBEKeySpec(password.toCharArray());
			} else {
				StorageException e = new StorageException(StorageException.ENCRYPTION_ERROR, WinCryptoMessages.decryptPasswordFailed);
				AuthPlugin.getDefault().logError(WinCryptoMessages.decryptPasswordFailed, e);

				if (container.hasOption(IProviderHints.PROMPT_USER)) {
					Object promptHint = container.getOption(IProviderHints.PROMPT_USER);
					if (promptHint instanceof Boolean) {
						boolean canPrompt = ((Boolean) promptHint).booleanValue();
						if (!canPrompt)
							return null;
					}
				}
				try {
					if (!WinCryptoUI.canRecreatePassword())
						return null;
				} catch (NoClassDefFoundError exception) {
					return null;
				}
				// follow down with new password generation
			}
		}

		// add info message in the log
		AuthPlugin.getDefault().logMessage(WinCryptoMessages.newPasswordGenerated);
		
		byte[] rawPassword = new byte[PASSWORD_LENGTH];
		SecureRandom random = new SecureRandom();
		random.setSeed(System.currentTimeMillis());
		random.nextBytes(rawPassword);
		String password = Base64.encode(rawPassword);
		if (savePassword(password, container))
			return new PBEKeySpec(password.toCharArray());
		else
			return null;
	}

	private byte[] getEncryptedPassword(IPreferencesContainer container) {
		ISecurePreferences node = container.getPreferences().node(WIN_PROVIDER_NODE);
		String passwordHint; 
		try {
			passwordHint = node.get(PASSWORD_KEY, null);
		} catch (StorageException e) { // should never happen in this scenario
			AuthPlugin.getDefault().logError(WinCryptoMessages.decryptPasswordFailed, e);
			return null;
		}
		if (passwordHint == null)
			return null;
		return Base64.decode(passwordHint);
	}

	private boolean savePassword(String password, IPreferencesContainer container){
		byte[] data = winencrypt(password.getBytes());
		if (data == null) { // this is bad. Something wrong with OS or JNI.
			StorageException e = new StorageException(StorageException.ENCRYPTION_ERROR, WinCryptoMessages.encryptPasswordFailed);
			AuthPlugin.getDefault().logError(WinCryptoMessages.encryptPasswordFailed, e);
			return false;
		}
		String encodedEncryptyedPassword = Base64.encode(data);
		ISecurePreferences node = container.getPreferences().node(WIN_PROVIDER_NODE);
		try {
			node.put(PASSWORD_KEY, encodedEncryptyedPassword, false); // note we don't recursively try to encrypt
		} catch (StorageException e) { // should never happen in this scenario
			AuthPlugin.getDefault().logError(SecAuthMessages.errorOnSave, e);
			return false;
		}
		try {
			node.flush(); // save right away
		} catch (IOException e) {
			AuthPlugin.getDefault().logError(SecAuthMessages.errorOnSave, e);
			return false;
		}
		return true;
	}

	public boolean retryOnError(Exception e, IPreferencesContainer container) {
		// It would be rather dangerous to allow this password to be changed
		// as it would permanently trash all entries in the secure storage.
		// Rather applications using get...() should handle exceptions and offer to overwrite 
		// data on an entry-by-entry scale.
		return false;
	}

}

Back to the top