source: josm/trunk/src/org/openstreetmap/josm/tools/PlatformHookWindows.java@ 12846

Last change on this file since 12846 was 12846, checked in by bastiK, 7 years ago

see #15229 - use Config.getPref() wherever possible

  • Property svn:eol-style set to native
File size: 39.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import static java.awt.event.InputEvent.ALT_DOWN_MASK;
5import static java.awt.event.InputEvent.CTRL_DOWN_MASK;
6import static java.awt.event.InputEvent.SHIFT_DOWN_MASK;
7import static java.awt.event.KeyEvent.VK_A;
8import static java.awt.event.KeyEvent.VK_C;
9import static java.awt.event.KeyEvent.VK_D;
10import static java.awt.event.KeyEvent.VK_DELETE;
11import static java.awt.event.KeyEvent.VK_DOWN;
12import static java.awt.event.KeyEvent.VK_ENTER;
13import static java.awt.event.KeyEvent.VK_ESCAPE;
14import static java.awt.event.KeyEvent.VK_F10;
15import static java.awt.event.KeyEvent.VK_F4;
16import static java.awt.event.KeyEvent.VK_LEFT;
17import static java.awt.event.KeyEvent.VK_NUM_LOCK;
18import static java.awt.event.KeyEvent.VK_PRINTSCREEN;
19import static java.awt.event.KeyEvent.VK_RIGHT;
20import static java.awt.event.KeyEvent.VK_SHIFT;
21import static java.awt.event.KeyEvent.VK_SPACE;
22import static java.awt.event.KeyEvent.VK_TAB;
23import static java.awt.event.KeyEvent.VK_UP;
24import static java.awt.event.KeyEvent.VK_V;
25import static java.awt.event.KeyEvent.VK_X;
26import static java.awt.event.KeyEvent.VK_Y;
27import static java.awt.event.KeyEvent.VK_Z;
28import static org.openstreetmap.josm.tools.I18n.tr;
29import static org.openstreetmap.josm.tools.WinRegistry.HKEY_LOCAL_MACHINE;
30
31import java.awt.GraphicsEnvironment;
32import java.io.BufferedWriter;
33import java.io.File;
34import java.io.FileInputStream;
35import java.io.IOException;
36import java.io.OutputStream;
37import java.io.OutputStreamWriter;
38import java.io.Writer;
39import java.lang.reflect.InvocationTargetException;
40import java.nio.charset.StandardCharsets;
41import java.nio.file.DirectoryStream;
42import java.nio.file.FileSystems;
43import java.nio.file.Files;
44import java.nio.file.Path;
45import java.security.InvalidKeyException;
46import java.security.KeyFactory;
47import java.security.KeyStore;
48import java.security.KeyStoreException;
49import java.security.MessageDigest;
50import java.security.NoSuchAlgorithmException;
51import java.security.NoSuchProviderException;
52import java.security.PublicKey;
53import java.security.SignatureException;
54import java.security.cert.Certificate;
55import java.security.cert.CertificateException;
56import java.security.cert.X509Certificate;
57import java.security.spec.InvalidKeySpecException;
58import java.security.spec.X509EncodedKeySpec;
59import java.util.ArrayList;
60import java.util.Collection;
61import java.util.Enumeration;
62import java.util.List;
63import java.util.Locale;
64import java.util.Properties;
65
66import javax.swing.JOptionPane;
67
68import org.openstreetmap.josm.Main;
69import org.openstreetmap.josm.data.Preferences;
70import org.openstreetmap.josm.io.CertificateAmendment.CertAmend;
71import org.openstreetmap.josm.spi.preferences.Config;
72
73/**
74 * {@code PlatformHook} implementation for Microsoft Windows systems.
75 * @since 1023
76 */
77public class PlatformHookWindows implements PlatformHook {
78
79 /**
80 * Simple data class to hold information about a font.
81 *
82 * Used for fontconfig.properties files.
83 */
84 public static class FontEntry {
85 /**
86 * The character subset. Basically a free identifier, but should be unique.
87 */
88 @Preferences.pref
89 public String charset;
90
91 /**
92 * Platform font name.
93 */
94 @Preferences.pref
95 @Preferences.writeExplicitly
96 public String name = "";
97
98 /**
99 * File name.
100 */
101 @Preferences.pref
102 @Preferences.writeExplicitly
103 public String file = "";
104
105 /**
106 * Constructs a new {@code FontEntry}.
107 */
108 public FontEntry() {
109 // Default constructor needed for construction by reflection
110 }
111
112 /**
113 * Constructs a new {@code FontEntry}.
114 * @param charset The character subset. Basically a free identifier, but should be unique
115 * @param name Platform font name
116 * @param file File name
117 */
118 public FontEntry(String charset, String name, String file) {
119 this.charset = charset;
120 this.name = name;
121 this.file = file;
122 }
123 }
124
125 private static final byte[] INSECURE_PUBLIC_KEY = new byte[] {
126 0x30, (byte) 0x82, 0x1, 0x22, 0x30, 0xd, 0x6, 0x9, 0x2a, (byte) 0x86, 0x48,
127 (byte) 0x86, (byte) 0xf7, 0xd, 0x1, 0x1, 0x1, 0x5, 0x0, 0x3, (byte) 0x82, 0x1, 0xf, 0x0,
128 0x30, (byte) 0x82, 0x01, 0x0a, 0x02, (byte) 0x82, 0x01, 0x01, 0x00, (byte) 0x95, (byte) 0x95, (byte) 0x88,
129 (byte) 0x84, (byte) 0xc8, (byte) 0xd9, 0x6b, (byte) 0xc5, (byte) 0xda, 0x0b, 0x69, (byte) 0xbf, (byte) 0xfc,
130 0x7e, (byte) 0xb9, (byte) 0x96, 0x2c, (byte) 0xeb, (byte) 0x8f, (byte) 0xbc, 0x6e, 0x40, (byte) 0xe6, (byte) 0xe2,
131 (byte) 0xfc, (byte) 0xf1, 0x7f, 0x73, (byte) 0xa7, (byte) 0x9d, (byte) 0xde, (byte) 0xc7, (byte) 0x88, 0x57, 0x51,
132 (byte) 0x84, (byte) 0xed, (byte) 0x96, (byte) 0xfb, (byte) 0xe1, 0x38, (byte) 0xef, 0x08, 0x2b, (byte) 0xf3,
133 (byte) 0xc7, (byte) 0xc3, 0x5d, (byte) 0xfe, (byte) 0xf9, 0x51, (byte) 0xe6, 0x29, (byte) 0xfc, (byte) 0xe5, 0x0d,
134 (byte) 0xa1, 0x0d, (byte) 0xa8, (byte) 0xb4, (byte) 0xae, 0x26, 0x18, 0x19, 0x4d, 0x6c, 0x0c, 0x3b, 0x12, (byte) 0xba,
135 (byte) 0xbc, 0x5f, 0x32, (byte) 0xb3, (byte) 0xbe, (byte) 0x9d, 0x17, 0x0d, 0x4d, 0x2f, 0x1a, 0x48, (byte) 0xb7,
136 (byte) 0xac, (byte) 0xf7, 0x1a, 0x43, 0x01, (byte) 0x97, (byte) 0xf4, (byte) 0xf8, 0x4c, (byte) 0xbb, 0x6a, (byte) 0xbc,
137 0x33, (byte) 0xe1, 0x73, 0x1e, (byte) 0x86, (byte) 0xfb, 0x2e, (byte) 0xb1, 0x63, 0x75, (byte) 0x85, (byte) 0xdc,
138 (byte) 0x82, 0x6c, 0x28, (byte) 0xf1, (byte) 0xe3, (byte) 0x90, 0x63, (byte) 0x9d, 0x3d, 0x48, (byte) 0x8a, (byte) 0x8c,
139 0x47, (byte) 0xe2, 0x10, 0x0b, (byte) 0xef, (byte) 0x91, (byte) 0x94, (byte) 0xb0, 0x6c, 0x4c, (byte) 0x80, 0x76, 0x03,
140 (byte) 0xe1, (byte) 0xb6, (byte) 0x90, (byte) 0x87, (byte) 0xd9, (byte) 0xae, (byte) 0xf4, (byte) 0x8e, (byte) 0xe0,
141 (byte) 0x9f, (byte) 0xe7, 0x3a, 0x2c, 0x2f, 0x21, (byte) 0xd4, 0x46, (byte) 0xba, (byte) 0x95, 0x70, (byte) 0xa9, 0x5b,
142 0x20, 0x2a, (byte) 0xfa, 0x52, 0x3e, (byte) 0x9d, (byte) 0xd9, (byte) 0xef, 0x28, (byte) 0xc5, (byte) 0xd1, 0x60,
143 (byte) 0x89, 0x68, 0x6e, 0x7f, (byte) 0xd7, (byte) 0x9e, (byte) 0x89, 0x4c, (byte) 0xeb, 0x4d, (byte) 0xd2, (byte) 0xc6,
144 (byte) 0xf4, 0x2d, 0x02, 0x5d, (byte) 0xda, (byte) 0xde, 0x33, (byte) 0xfe, (byte) 0xc1, 0x7e, (byte) 0xde, 0x4f, 0x1f,
145 (byte) 0x9b, 0x6e, 0x6f, 0x0f, 0x66, 0x71, 0x19, (byte) 0xe9, 0x43, 0x3c, (byte) 0x83, 0x0a, 0x0f, 0x28, 0x21, (byte) 0xc8,
146 0x38, (byte) 0xd3, 0x4e, 0x48, (byte) 0xdf, (byte) 0xd4, (byte) 0x99, (byte) 0xb5, (byte) 0xc6, (byte) 0x8d, (byte) 0xd4,
147 (byte) 0xc1, 0x69, 0x58, 0x79, (byte) 0x82, 0x32, (byte) 0x82, (byte) 0xd4, (byte) 0x86, (byte) 0xe2, 0x04, 0x08, 0x63,
148 (byte) 0x87, (byte) 0xf0, 0x2a, (byte) 0xf6, (byte) 0xec, 0x3e, 0x51, 0x0f, (byte) 0xda, (byte) 0xb4, 0x67, 0x19, 0x5e,
149 0x16, 0x02, (byte) 0x9f, (byte) 0xf1, 0x19, 0x0c, 0x3e, (byte) 0xb8, 0x04, 0x49, 0x07, 0x53, 0x02, 0x03, 0x01, 0x00, 0x01
150 };
151
152 private static final String WINDOWS_ROOT = "Windows-ROOT";
153
154 private static final String CURRENT_VERSION = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
155
156 private String oSBuildNumber;
157
158 @Override
159 public Platform getPlatform() {
160 return Platform.WINDOWS;
161 }
162
163 @Override
164 public void afterPrefStartupHook() {
165 extendFontconfig("fontconfig.properties.src");
166 }
167
168 @Override
169 public void startupHook(JavaExpirationCallback callback) {
170 checkExpiredJava(callback);
171 }
172
173 @Override
174 public void openUrl(String url) throws IOException {
175 Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
176 }
177
178 @Override
179 public void initSystemShortcuts() {
180 // CHECKSTYLE.OFF: LineLength
181 //Shortcut.registerSystemCut("system:menuexit", tr("reserved"), VK_Q, CTRL_DOWN_MASK);
182 Shortcut.registerSystemShortcut("system:duplicate", tr("reserved"), VK_D, CTRL_DOWN_MASK); // not really system, but to avoid odd results
183
184 // Windows 7 shortcuts: http://windows.microsoft.com/en-US/windows7/Keyboard-shortcuts
185
186 // Shortcuts with setAutomatic(): items with automatic shortcuts will not be added to the menu bar at all
187
188 // Don't know why Ctrl-Alt-Del isn't even listed on official Microsoft support page
189 Shortcut.registerSystemShortcut("system:reset", tr("reserved"), VK_DELETE, CTRL_DOWN_MASK | ALT_DOWN_MASK).setAutomatic();
190
191 // Ease of Access keyboard shortcuts
192 Shortcut.registerSystemShortcut("microsoft-reserved-01", tr("reserved"), VK_PRINTSCREEN, ALT_DOWN_MASK | SHIFT_DOWN_MASK).setAutomatic(); // Turn High Contrast on or off
193 Shortcut.registerSystemShortcut("microsoft-reserved-02", tr("reserved"), VK_NUM_LOCK, ALT_DOWN_MASK | SHIFT_DOWN_MASK).setAutomatic(); // Turn Mouse Keys on or off
194 //Shortcut.registerSystemCut("microsoft-reserved-03", tr("reserved"), VK_U, );// Open the Ease of Access Center (TODO: Windows-U, how to handle it in Java ?)
195
196 // General keyboard shortcuts
197 //Shortcut.registerSystemShortcut("system:help", tr("reserved"), VK_F1, 0); // Display Help
198 Shortcut.registerSystemShortcut("system:copy", tr("reserved"), VK_C, CTRL_DOWN_MASK); // Copy the selected item
199 Shortcut.registerSystemShortcut("system:cut", tr("reserved"), VK_X, CTRL_DOWN_MASK); // Cut the selected item
200 Shortcut.registerSystemShortcut("system:paste", tr("reserved"), VK_V, CTRL_DOWN_MASK); // Paste the selected item
201 Shortcut.registerSystemShortcut("system:undo", tr("reserved"), VK_Z, CTRL_DOWN_MASK); // Undo an action
202 Shortcut.registerSystemShortcut("system:redo", tr("reserved"), VK_Y, CTRL_DOWN_MASK); // Redo an action
203 //Shortcut.registerSystemCut("microsoft-reserved-10", tr("reserved"), VK_DELETE, 0); // Delete the selected item and move it to the Recycle Bin
204 //Shortcut.registerSystemCut("microsoft-reserved-11", tr("reserved"), VK_DELETE, SHIFT_DOWN_MASK); // Delete the selected item without moving it to the Recycle Bin first
205 //Shortcut.registerSystemCut("system:rename", tr("reserved"), VK_F2, 0); // Rename the selected item
206 Shortcut.registerSystemShortcut("system:movefocusright", tr("reserved"), VK_RIGHT, CTRL_DOWN_MASK); // Move the cursor to the beginning of the next word
207 Shortcut.registerSystemShortcut("system:movefocusleft", tr("reserved"), VK_LEFT, CTRL_DOWN_MASK); // Move the cursor to the beginning of the previous word
208 Shortcut.registerSystemShortcut("system:movefocusdown", tr("reserved"), VK_DOWN, CTRL_DOWN_MASK); // Move the cursor to the beginning of the next paragraph
209 Shortcut.registerSystemShortcut("system:movefocusup", tr("reserved"), VK_UP, CTRL_DOWN_MASK); // Move the cursor to the beginning of the previous paragraph
210 //Shortcut.registerSystemCut("microsoft-reserved-17", tr("reserved"), VK_RIGHT, CTRL_DOWN_MASK | SHIFT_DOWN_MASK); // Select a block of text
211 //Shortcut.registerSystemCut("microsoft-reserved-18", tr("reserved"), VK_LEFT, CTRL_DOWN_MASK | SHIFT_DOWN_MASK); // Select a block of text
212 //Shortcut.registerSystemCut("microsoft-reserved-19", tr("reserved"), VK_DOWN, CTRL_DOWN_MASK | SHIFT_DOWN_MASK); // Select a block of text
213 //Shortcut.registerSystemCut("microsoft-reserved-20", tr("reserved"), VK_UP, CTRL_DOWN_MASK | SHIFT_DOWN_MASK); // Select a block of text
214 //Shortcut.registerSystemCut("microsoft-reserved-21", tr("reserved"), VK_RIGHT, SHIFT_DOWN_MASK); // Select more than one item in a window or on the desktop, or select text within a document
215 //Shortcut.registerSystemCut("microsoft-reserved-22", tr("reserved"), VK_LEFT, SHIFT_DOWN_MASK); // Select more than one item in a window or on the desktop, or select text within a document
216 //Shortcut.registerSystemCut("microsoft-reserved-23", tr("reserved"), VK_DOWN, SHIFT_DOWN_MASK); // Select more than one item in a window or on the desktop, or select text within a document
217 //Shortcut.registerSystemCut("microsoft-reserved-24", tr("reserved"), VK_UP, SHIFT_DOWN_MASK); // Select more than one item in a window or on the desktop, or select text within a document
218 //Shortcut.registerSystemCut("microsoft-reserved-25", tr("reserved"), VK_RIGHT+, CTRL_DOWN_MASK); // Select multiple individual items in a window or on the desktop (TODO: ctrl+arrow+spacebar, how to handle it in Java ?)
219 //Shortcut.registerSystemCut("microsoft-reserved-26", tr("reserved"), VK_LEFT+, CTRL_DOWN_MASK); // Select multiple individual items in a window or on the desktop (TODO: ctrl+arrow+spacebar, how to handle it in Java ?)
220 //Shortcut.registerSystemCut("microsoft-reserved-27", tr("reserved"), VK_DOWN+, CTRL_DOWN_MASK); // Select multiple individual items in a window or on the desktop (TODO: ctrl+arrow+spacebar, how to handle it in Java ?)
221 //Shortcut.registerSystemCut("microsoft-reserved-28", tr("reserved"), VK_UP+, CTRL_DOWN_MASK); // Select multiple individual items in a window or on the desktop (TODO: ctrl+arrow+spacebar, how to handle it in Java ?)
222 Shortcut.registerSystemShortcut("system:selectall", tr("reserved"), VK_A, CTRL_DOWN_MASK); // Select all items in a document or window
223 //Shortcut.registerSystemCut("system:search", tr("reserved"), VK_F3, 0); // Search for a file or folder
224 Shortcut.registerSystemShortcut("microsoft-reserved-31", tr("reserved"), VK_ENTER, ALT_DOWN_MASK).setAutomatic(); // Display properties for the selected item
225 Shortcut.registerSystemShortcut("system:exit", tr("reserved"), VK_F4, ALT_DOWN_MASK).setAutomatic(); // Close the active item, or exit the active program
226 Shortcut.registerSystemShortcut("microsoft-reserved-33", tr("reserved"), VK_SPACE, ALT_DOWN_MASK).setAutomatic(); // Open the shortcut menu for the active window
227 //Shortcut.registerSystemCut("microsoft-reserved-34", tr("reserved"), VK_F4, CTRL_DOWN_MASK); // Close the active document (in programs that allow you to have multiple documents open simultaneously)
228 Shortcut.registerSystemShortcut("microsoft-reserved-35", tr("reserved"), VK_TAB, ALT_DOWN_MASK).setAutomatic(); // Switch between open items
229 Shortcut.registerSystemShortcut("microsoft-reserved-36", tr("reserved"), VK_TAB, CTRL_DOWN_MASK | ALT_DOWN_MASK).setAutomatic(); // Use the arrow keys to switch between open items
230 //Shortcut.registerSystemCut("microsoft-reserved-37", tr("reserved"), VK_TAB, ); // Cycle through programs on the taskbar by using Aero Flip 3-D (TODO: Windows-Tab, how to handle it in Java ?)
231 //Shortcut.registerSystemCut("microsoft-reserved-38", tr("reserved"), VK_TAB, CTRL_DOWN_MASK | ); // Use the arrow keys to cycle through programs on the taskbar by using Aero Flip 3-D (TODO: Ctrl-Windows-Tab, how to handle it in Java ?)
232 Shortcut.registerSystemShortcut("microsoft-reserved-39", tr("reserved"), VK_ESCAPE, ALT_DOWN_MASK).setAutomatic(); // Cycle through items in the order in which they were opened
233 //Shortcut.registerSystemCut("microsoft-reserved-40", tr("reserved"), VK_F6, 0); // Cycle through screen elements in a window or on the desktop
234 //Shortcut.registerSystemCut("microsoft-reserved-41", tr("reserved"), VK_F4, 0); // Display the address bar list in Windows Explorer
235 Shortcut.registerSystemShortcut("microsoft-reserved-42", tr("reserved"), VK_F10, SHIFT_DOWN_MASK); // Display the shortcut menu for the selected item
236 Shortcut.registerSystemShortcut("microsoft-reserved-43", tr("reserved"), VK_ESCAPE, CTRL_DOWN_MASK).setAutomatic(); // Open the Start menu
237 //Shortcut.registerSystemShortcut("microsoft-reserved-44", tr("reserved"), VK_F10, 0); // Activate the menu bar in the active program
238 //Shortcut.registerSystemCut("microsoft-reserved-45", tr("reserved"), VK_RIGHT, 0); // Open the next menu to the right, or open a submenu
239 //Shortcut.registerSystemCut("microsoft-reserved-46", tr("reserved"), VK_LEFT, 0); // Open the next menu to the left, or close a submenu
240 //Shortcut.registerSystemCut("microsoft-reserved-47", tr("reserved"), VK_F5, 0); // Refresh the active window
241 //Shortcut.registerSystemCut("microsoft-reserved-48", tr("reserved"), VK_UP, ALT_DOWN_MASK); // View the folder one level up in Windows Explorer
242 //Shortcut.registerSystemCut("microsoft-reserved-49", tr("reserved"), VK_ESCAPE, 0); // Cancel the current task
243 Shortcut.registerSystemShortcut("microsoft-reserved-50", tr("reserved"), VK_ESCAPE, CTRL_DOWN_MASK | SHIFT_DOWN_MASK).setAutomatic(); // Open Task Manager
244 Shortcut.registerSystemShortcut("microsoft-reserved-51", tr("reserved"), VK_SHIFT, ALT_DOWN_MASK).setAutomatic(); // Switch the input language when multiple input languages are enabled
245 Shortcut.registerSystemShortcut("microsoft-reserved-52", tr("reserved"), VK_SHIFT, CTRL_DOWN_MASK).setAutomatic(); // Switch the keyboard layout when multiple keyboard layouts are enabled
246 //Shortcut.registerSystemCut("microsoft-reserved-53", tr("reserved"), ); // Change the reading direction of text in right-to-left reading languages (TODO: unclear)
247 // CHECKSTYLE.ON: LineLength
248 }
249
250 @Override
251 public String getDefaultStyle() {
252 return "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
253 }
254
255 @Override
256 public boolean rename(File from, File to) {
257 if (to.exists())
258 Utils.deleteFile(to);
259 return from.renameTo(to);
260 }
261
262 @Override
263 public String getOSDescription() {
264 return Utils.strip(System.getProperty("os.name")) + ' ' +
265 ((System.getenv("ProgramFiles(x86)") == null) ? "32" : "64") + "-Bit";
266 }
267
268 /**
269 * Returns the Windows product name from registry (example: "Windows 10 Pro")
270 * @return the Windows product name from registry
271 * @throws IllegalAccessException if Java language access control is enforced and the underlying method is inaccessible
272 * @throws InvocationTargetException if the underlying method throws an exception
273 * @since 12744
274 */
275 public static String getProductName() throws IllegalAccessException, InvocationTargetException {
276 return WinRegistry.readString(HKEY_LOCAL_MACHINE, CURRENT_VERSION, "ProductName");
277 }
278
279 /**
280 * Returns the Windows release identifier from registry (example: "1703")
281 * @return the Windows release identifier from registry
282 * @throws IllegalAccessException if Java language access control is enforced and the underlying method is inaccessible
283 * @throws InvocationTargetException if the underlying method throws an exception
284 * @since 12744
285 */
286 public static String getReleaseId() throws IllegalAccessException, InvocationTargetException {
287 return WinRegistry.readString(HKEY_LOCAL_MACHINE, CURRENT_VERSION, "ReleaseId");
288 }
289
290 /**
291 * Returns the Windows current build number from registry (example: "15063")
292 * @return the Windows current build number from registry
293 * @throws IllegalAccessException if Java language access control is enforced and the underlying method is inaccessible
294 * @throws InvocationTargetException if the underlying method throws an exception
295 * @since 12744
296 */
297 public static String getCurrentBuild() throws IllegalAccessException, InvocationTargetException {
298 return WinRegistry.readString(HKEY_LOCAL_MACHINE, CURRENT_VERSION, "CurrentBuild");
299 }
300
301 private static String buildOSBuildNumber() {
302 StringBuilder sb = new StringBuilder();
303 try {
304 sb.append(getProductName());
305 String releaseId = getReleaseId();
306 if (releaseId != null) {
307 sb.append(' ').append(releaseId);
308 }
309 sb.append(" (").append(getCurrentBuild()).append(')');
310 } catch (ReflectiveOperationException e) {
311 Logging.error(e);
312 }
313 return sb.toString();
314 }
315
316 @Override
317 public String getOSBuildNumber() {
318 if (oSBuildNumber == null) {
319 oSBuildNumber = buildOSBuildNumber();
320 }
321 return oSBuildNumber;
322 }
323
324 /**
325 * Loads Windows-ROOT keystore.
326 * @return Windows-ROOT keystore
327 * @throws NoSuchAlgorithmException if the algorithm used to check the integrity of the keystore cannot be found
328 * @throws CertificateException if any of the certificates in the keystore could not be loaded
329 * @throws IOException if there is an I/O or format problem with the keystore data, if a password is required but not given
330 * @throws KeyStoreException if no Provider supports a KeyStore implementation for the type "Windows-ROOT"
331 * @since 7343
332 */
333 public static KeyStore getRootKeystore() throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException {
334 KeyStore ks = KeyStore.getInstance(WINDOWS_ROOT);
335 ks.load(null, null);
336 return ks;
337 }
338
339 /**
340 * Removes potential insecure certificates installed with previous versions of JOSM on Windows.
341 * @throws NoSuchAlgorithmException on unsupported signature algorithms
342 * @throws CertificateException if any of the certificates in the Windows keystore could not be loaded
343 * @throws KeyStoreException if no Provider supports a KeyStoreSpi implementation for the type "Windows-ROOT"
344 * @throws IOException if there is an I/O or format problem with the keystore data, if a password is required but not given
345 * @since 7335
346 */
347 public static void removeInsecureCertificates() throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
348 // We offered before a public private key we need now to remove from Windows PCs as it might be a huge security risk (see #10230)
349 PublicKey insecurePubKey = null;
350 try {
351 insecurePubKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(INSECURE_PUBLIC_KEY));
352 } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
353 Logging.error(e);
354 return;
355 }
356 KeyStore ks = getRootKeystore();
357 Enumeration<String> en = ks.aliases();
358 Collection<String> insecureCertificates = new ArrayList<>();
359 while (en.hasMoreElements()) {
360 String alias = en.nextElement();
361 // Look for certificates associated with a private key
362 if (ks.isKeyEntry(alias)) {
363 try {
364 ks.getCertificate(alias).verify(insecurePubKey);
365 // If no exception, this is a certificate signed with the insecure key -> remove it
366 insecureCertificates.add(alias);
367 } catch (InvalidKeyException | NoSuchProviderException | SignatureException e) {
368 // If exception this is not a certificate related to JOSM, just trace it
369 Logging.trace(alias + " --> " + e.getClass().getName());
370 Logging.trace(e);
371 }
372 }
373 }
374 // Remove insecure certificates
375 if (!insecureCertificates.isEmpty()) {
376 StringBuilder message = new StringBuilder("<html>");
377 message.append(tr("A previous version of JOSM has installed a custom certificate "+
378 "in order to provide HTTPS support for Remote Control:"))
379 .append("<br><ul>");
380 for (String alias : insecureCertificates) {
381 message.append("<li>")
382 .append(alias)
383 .append("</li>");
384 }
385 message.append("</ul>")
386 .append(tr("It appears it could be an important <b>security risk</b>.<br><br>"+
387 "You are now going to be prompted by Windows to remove this insecure certificate.<br>"+
388 "For your own safety, <b>please click Yes</b> in next dialog."))
389 .append("</html>");
390 JOptionPane.showMessageDialog(Main.parent, message.toString(), tr("Warning"), JOptionPane.WARNING_MESSAGE);
391 for (String alias : insecureCertificates) {
392 Logging.warn(tr("Removing insecure certificate from {0} keystore: {1}", WINDOWS_ROOT, alias));
393 try {
394 ks.deleteEntry(alias);
395 } catch (KeyStoreException e) {
396 Logging.log(Logging.LEVEL_ERROR, tr("Unable to remove insecure certificate from keystore: {0}", e.getMessage()), e);
397 }
398 }
399 }
400 }
401
402 @Override
403 public boolean setupHttpsCertificate(String entryAlias, KeyStore.TrustedCertificateEntry trustedCert)
404 throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
405 KeyStore ks = getRootKeystore();
406 // Look for certificate to install
407 try {
408 String alias = ks.getCertificateAlias(trustedCert.getTrustedCertificate());
409 if (alias != null) {
410 // JOSM certificate found, return
411 Logging.debug(tr("JOSM localhost certificate found in {0} keystore: {1}", WINDOWS_ROOT, alias));
412 return false;
413 }
414 } catch (ArrayIndexOutOfBoundsException e) {
415 // catch error of JDK-8172244 as bug seems to not be fixed anytime soon
416 Logging.log(Logging.LEVEL_ERROR, "JDK-8172244 occured. Abort HTTPS setup", e);
417 return false;
418 }
419 if (!GraphicsEnvironment.isHeadless()) {
420 // JOSM certificate not found, warn user
421 StringBuilder message = new StringBuilder("<html>");
422 message.append(tr("Remote Control is configured to provide HTTPS support.<br>"+
423 "This requires to add a custom certificate generated by JOSM to the Windows Root CA store.<br><br>"+
424 "You are now going to be prompted by Windows to confirm this operation.<br>"+
425 "To enable proper HTTPS support, <b>please click Yes</b> in next dialog.<br><br>"+
426 "If unsure, you can also click No then disable HTTPS support in Remote Control preferences."))
427 .append("</html>");
428 JOptionPane.showMessageDialog(Main.parent, message.toString(),
429 tr("HTTPS support in Remote Control"), JOptionPane.INFORMATION_MESSAGE);
430 }
431 // install it to Windows-ROOT keystore, used by IE, Chrome and Safari, but not by Firefox
432 Logging.info(tr("Adding JOSM localhost certificate to {0} keystore", WINDOWS_ROOT));
433 ks.setEntry(entryAlias, trustedCert, null);
434 return true;
435 }
436
437 @Override
438 public X509Certificate getX509Certificate(CertAmend certAmend)
439 throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
440 KeyStore ks = getRootKeystore();
441 // Search by alias (fast)
442 Certificate result = ks.getCertificate(certAmend.getId());
443 if (result instanceof X509Certificate) {
444 return (X509Certificate) result;
445 }
446 // If not found, search by SHA-256 (slower)
447 MessageDigest md = MessageDigest.getInstance("SHA-256");
448 for (Enumeration<String> aliases = ks.aliases(); aliases.hasMoreElements();) {
449 result = ks.getCertificate(aliases.nextElement());
450 if (result instanceof X509Certificate
451 && certAmend.getSha256().equalsIgnoreCase(Utils.toHexString(md.digest(result.getEncoded())))) {
452 return (X509Certificate) result;
453 }
454 }
455 // Not found
456 return null;
457 }
458
459 @Override
460 public File getDefaultCacheDirectory() {
461 String p = System.getenv("LOCALAPPDATA");
462 if (p == null || p.isEmpty()) {
463 // Fallback for Windows OS earlier than Windows Vista, where the variable is not defined
464 p = System.getenv("APPDATA");
465 }
466 return new File(new File(p, Main.pref.getJOSMDirectoryBaseName()), "cache");
467 }
468
469 @Override
470 public File getDefaultPrefDirectory() {
471 return new File(System.getenv("APPDATA"), Main.pref.getJOSMDirectoryBaseName());
472 }
473
474 @Override
475 public File getDefaultUserDataDirectory() {
476 // Use preferences directory by default
477 return Main.pref.getPreferencesDirectory();
478 }
479
480 /**
481 * <p>Add more fallback fonts to the Java runtime, in order to get
482 * support for more scripts.</p>
483 *
484 * <p>The font configuration in Java doesn't include some Indic scripts,
485 * even though MS Windows ships with fonts that cover these unicode ranges.</p>
486 *
487 * <p>To fix this, the fontconfig.properties template is copied to the JOSM
488 * cache folder. Then, the additional entries are added to the font
489 * configuration. Finally the system property "sun.awt.fontconfig" is set
490 * to the customized fontconfig.properties file.</p>
491 *
492 * <p>This is a crude hack, but better than no font display at all for these languages.
493 * There is no guarantee, that the template file
494 * ($JAVA_HOME/lib/fontconfig.properties.src) matches the default
495 * configuration (which is in a binary format).
496 * Furthermore, the system property "sun.awt.fontconfig" is undocumented and
497 * may no longer work in future versions of Java.</p>
498 *
499 * <p>Related Java bug: <a href="https://bugs.openjdk.java.net/browse/JDK-8008572">JDK-8008572</a></p>
500 *
501 * @param templateFileName file name of the fontconfig.properties template file
502 */
503 protected void extendFontconfig(String templateFileName) {
504 String customFontconfigFile = Config.getPref().get("fontconfig.properties", null);
505 if (customFontconfigFile != null) {
506 Utils.updateSystemProperty("sun.awt.fontconfig", customFontconfigFile);
507 return;
508 }
509 if (!Config.getPref().getBoolean("font.extended-unicode", true))
510 return;
511
512 String javaLibPath = System.getProperty("java.home") + File.separator + "lib";
513 Path templateFile = FileSystems.getDefault().getPath(javaLibPath, templateFileName);
514 if (!Files.isReadable(templateFile)) {
515 Logging.warn("extended font config - unable to find font config template file {0}", templateFile.toString());
516 return;
517 }
518 try (FileInputStream fis = new FileInputStream(templateFile.toFile())) {
519 Properties props = new Properties();
520 props.load(fis);
521 byte[] content = Files.readAllBytes(templateFile);
522 File cachePath = Main.pref.getCacheDirectory();
523 Path fontconfigFile = cachePath.toPath().resolve("fontconfig.properties");
524 OutputStream os = Files.newOutputStream(fontconfigFile);
525 os.write(content);
526 try (Writer w = new BufferedWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8))) {
527 Collection<FontEntry> extrasPref = Main.pref.getListOfStructs(
528 "font.extended-unicode.extra-items", getAdditionalFonts(), FontEntry.class);
529 Collection<FontEntry> extras = new ArrayList<>();
530 w.append("\n\n# Added by JOSM to extend unicode coverage of Java font support:\n\n");
531 List<String> allCharSubsets = new ArrayList<>();
532 for (FontEntry entry: extrasPref) {
533 Collection<String> fontsAvail = getInstalledFonts();
534 if (fontsAvail != null && fontsAvail.contains(entry.file.toUpperCase(Locale.ENGLISH))) {
535 if (!allCharSubsets.contains(entry.charset)) {
536 allCharSubsets.add(entry.charset);
537 extras.add(entry);
538 } else {
539 Logging.trace("extended font config - already registered font for charset ''{0}'' - skipping ''{1}''",
540 entry.charset, entry.name);
541 }
542 } else {
543 Logging.trace("extended font config - Font ''{0}'' not found on system - skipping", entry.name);
544 }
545 }
546 for (FontEntry entry: extras) {
547 allCharSubsets.add(entry.charset);
548 if ("".equals(entry.name)) {
549 continue;
550 }
551 String key = "allfonts." + entry.charset;
552 String value = entry.name;
553 String prevValue = props.getProperty(key);
554 if (prevValue != null && !prevValue.equals(value)) {
555 Logging.warn("extended font config - overriding ''{0}={1}'' with ''{2}''", key, prevValue, value);
556 }
557 w.append(key + '=' + value + '\n');
558 }
559 w.append('\n');
560 for (FontEntry entry: extras) {
561 if ("".equals(entry.name) || "".equals(entry.file)) {
562 continue;
563 }
564 String key = "filename." + entry.name.replace(' ', '_');
565 String value = entry.file;
566 String prevValue = props.getProperty(key);
567 if (prevValue != null && !prevValue.equals(value)) {
568 Logging.warn("extended font config - overriding ''{0}={1}'' with ''{2}''", key, prevValue, value);
569 }
570 w.append(key + '=' + value + '\n');
571 }
572 w.append('\n');
573 String fallback = props.getProperty("sequence.fallback");
574 if (fallback != null) {
575 w.append("sequence.fallback=" + fallback + ',' + Utils.join(",", allCharSubsets) + '\n');
576 } else {
577 w.append("sequence.fallback=" + Utils.join(",", allCharSubsets) + '\n');
578 }
579 }
580 Utils.updateSystemProperty("sun.awt.fontconfig", fontconfigFile.toString());
581 } catch (IOException ex) {
582 Logging.error(ex);
583 }
584 }
585
586 /**
587 * Get a list of fonts that are installed on the system.
588 *
589 * Must be done without triggering the Java Font initialization.
590 * (See {@link #extendFontconfig(java.lang.String)}, have to set system
591 * property first, which is then read by sun.awt.FontConfiguration upon initialization.)
592 *
593 * @return list of file names
594 */
595 protected Collection<String> getInstalledFonts() {
596 // Cannot use GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()
597 // because we have to set the system property before Java initializes its fonts.
598 // Use more low-level method to find the installed fonts.
599 List<String> fontsAvail = new ArrayList<>();
600 Path fontPath = FileSystems.getDefault().getPath(System.getenv("SYSTEMROOT"), "Fonts");
601 try (DirectoryStream<Path> ds = Files.newDirectoryStream(fontPath)) {
602 for (Path p : ds) {
603 Path filename = p.getFileName();
604 if (filename != null) {
605 fontsAvail.add(filename.toString().toUpperCase(Locale.ENGLISH));
606 }
607 }
608 fontsAvail.add(""); // for devanagari
609 } catch (IOException ex) {
610 Logging.log(Logging.LEVEL_ERROR, ex);
611 Logging.warn("extended font config - failed to load available Fonts");
612 fontsAvail = null;
613 }
614 return fontsAvail;
615 }
616
617 /**
618 * Get default list of additional fonts to add to the configuration.
619 *
620 * Java will choose thee first font in the list that can render a certain character.
621 *
622 * @return list of FontEntry objects
623 */
624 protected Collection<FontEntry> getAdditionalFonts() {
625 Collection<FontEntry> def = new ArrayList<>(33);
626 def.add(new FontEntry("devanagari", "", "")); // just include in fallback list font already defined in template
627
628 // Windows scripts: https://msdn.microsoft.com/en-us/goglobal/bb688099.aspx
629 // IE default fonts: https://msdn.microsoft.com/en-us/library/ie/dn467844(v=vs.85).aspx
630
631 // Windows 10 and later
632 def.add(new FontEntry("historic", "Segoe UI Historic", "SEGUIHIS.TTF")); // historic charsets
633
634 // Windows 8/8.1 and later
635 def.add(new FontEntry("javanese", "Javanese Text", "JAVATEXT.TTF")); // ISO 639: jv
636 def.add(new FontEntry("leelawadee", "Leelawadee", "LEELAWAD.TTF")); // ISO 639: bug
637 def.add(new FontEntry("myanmar", "Myanmar Text", "MMRTEXT.TTF")); // ISO 639: my
638 def.add(new FontEntry("nirmala", "Nirmala UI", "NIRMALA.TTF")); // ISO 639: sat,srb
639 def.add(new FontEntry("segoeui", "Segoe UI", "SEGOEUI.TTF")); // ISO 639: lis
640 def.add(new FontEntry("emoji", "Segoe UI Emoji", "SEGUIEMJ.TTF")); // emoji symbol characters
641
642 // Windows 7 and later
643 def.add(new FontEntry("nko_tifinagh_vai_osmanya", "Ebrima", "EBRIMA.TTF")); // ISO 639: ber. Nko only since Win 8
644 def.add(new FontEntry("khmer1", "Khmer UI", "KHMERUI.TTF")); // ISO 639: km
645 def.add(new FontEntry("lao1", "Lao UI", "LAOUI.TTF")); // ISO 639: lo
646 def.add(new FontEntry("tai_le", "Microsoft Tai Le", "TAILE.TTF")); // ISO 639: khb
647 def.add(new FontEntry("new_tai_lue", "Microsoft New Tai Lue", "NTHAILU.TTF")); // ISO 639: khb
648
649 // Windows Vista and later:
650 def.add(new FontEntry("ethiopic", "Nyala", "NYALA.TTF")); // ISO 639: am,gez,ti
651 def.add(new FontEntry("tibetan", "Microsoft Himalaya", "HIMALAYA.TTF")); // ISO 639: bo,dz
652 def.add(new FontEntry("cherokee", "Plantagenet Cherokee", "PLANTC.TTF")); // ISO 639: chr
653 def.add(new FontEntry("unified_canadian", "Euphemia", "EUPHEMIA.TTF")); // ISO 639: cr,in
654 def.add(new FontEntry("khmer2", "DaunPenh", "DAUNPENH.TTF")); // ISO 639: km
655 def.add(new FontEntry("khmer3", "MoolBoran", "MOOLBOR.TTF")); // ISO 639: km
656 def.add(new FontEntry("lao_thai", "DokChampa", "DOKCHAMP.TTF")); // ISO 639: lo
657 def.add(new FontEntry("mongolian", "Mongolian Baiti", "MONBAITI.TTF")); // ISO 639: mn
658 def.add(new FontEntry("oriya", "Kalinga", "KALINGA.TTF")); // ISO 639: or
659 def.add(new FontEntry("sinhala", "Iskoola Pota", "ISKPOTA.TTF")); // ISO 639: si
660 def.add(new FontEntry("yi", "Yi Baiti", "MSYI.TTF")); // ISO 639: ii
661
662 // Windows XP and later
663 def.add(new FontEntry("gujarati", "Shruti", "SHRUTI.TTF"));
664 def.add(new FontEntry("kannada", "Tunga", "TUNGA.TTF"));
665 def.add(new FontEntry("gurmukhi", "Raavi", "RAAVI.TTF"));
666 def.add(new FontEntry("telugu", "Gautami", "GAUTAMI.TTF"));
667 def.add(new FontEntry("bengali", "Vrinda", "VRINDA.TTF")); // since XP SP2
668 def.add(new FontEntry("syriac", "Estrangelo Edessa", "ESTRE.TTF")); // ISO 639: arc
669 def.add(new FontEntry("thaana", "MV Boli", "MVBOLI.TTF")); // ISO 639: dv
670 def.add(new FontEntry("malayalam", "Kartika", "KARTIKA.TTF")); // ISO 639: ml; since XP SP2
671
672 // Windows 2000 and later
673 def.add(new FontEntry("tamil", "Latha", "LATHA.TTF"));
674
675 // Comes with MS Office & Outlook 2000. Good unicode coverage, so add if available.
676 def.add(new FontEntry("arialuni", "Arial Unicode MS", "ARIALUNI.TTF"));
677
678 return def;
679 }
680}
Note: See TracBrowser for help on using the repository browser.