-
Notifications
You must be signed in to change notification settings - Fork 55
/
WinCrypto.java
137 lines (120 loc) · 4.89 KB
/
WinCrypto.java
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
/*******************************************************************************
* Copyright (c) 2008, 2024 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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.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("jnicrypt64");
}
private static final String WIN_PROVIDER_NODE = "/org.eclipse.equinox.secure.storage/windows64";
private static final String PASSWORD_KEY = "encryptedPassword";
/**
* The length of the randomly generated password in bytes
*/
private static final int PASSWORD_LENGTH = 250;
@Override
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);
return null;
}
}
// 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);
try {
String passwordHint = node.get(PASSWORD_KEY, null);
if (passwordHint != null) {
return Base64.decode(passwordHint);
}
} catch (StorageException e) { // should never happen in this scenario
AuthPlugin.getDefault().logError(WinCryptoMessages.decryptPasswordFailed, e);
}
return null;
}
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;
}
@Override
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;
}
}