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

Last change on this file since 13670 was 13647, checked in by Don-vip, 6 years ago

see #16204 - Allow to start and close JOSM in WebStart sandbox mode (where every external access is denied). This was very useful to reproduce some very tricky bugs that occured in real life but were almost impossible to diagnose.

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