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

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

fix #15992 - force Windows to update its root CA trust store before we search for known CA in it

  • Property svn:eol-style set to native
File size: 40.6 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.IOException;
35import java.io.InputStream;
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.InvalidPathException;
45import java.nio.file.Path;
46import java.security.InvalidKeyException;
47import java.security.KeyFactory;
48import java.security.KeyStore;
49import java.security.KeyStoreException;
50import java.security.MessageDigest;
51import java.security.NoSuchAlgorithmException;
52import java.security.NoSuchProviderException;
53import java.security.PublicKey;
54import java.security.SignatureException;
55import java.security.cert.Certificate;
56import java.security.cert.CertificateException;
57import java.security.cert.X509Certificate;
58import java.security.spec.InvalidKeySpecException;
59import java.security.spec.X509EncodedKeySpec;
60import java.util.ArrayList;
61import java.util.Arrays;
62import java.util.Collection;
63import java.util.Enumeration;
64import java.util.List;
65import java.util.Locale;
66import java.util.Properties;
67import java.util.concurrent.ExecutionException;
68
69import javax.swing.JOptionPane;
70
71import org.openstreetmap.josm.Main;
72import org.openstreetmap.josm.data.StructUtils;
73import org.openstreetmap.josm.data.StructUtils.StructEntry;
74import org.openstreetmap.josm.data.StructUtils.WriteExplicitly;
75import org.openstreetmap.josm.io.CertificateAmendment.NativeCertAmend;
76import org.openstreetmap.josm.spi.preferences.Config;
77
78/**
79 * {@code PlatformHook} implementation for Microsoft Windows systems.
80 * @since 1023
81 */
82public class PlatformHookWindows implements PlatformHook {
83
84 /**
85 * Simple data class to hold information about a font.
86 *
87 * Used for fontconfig.properties files.
88 */
89 public static class FontEntry {
90 /**
91 * The character subset. Basically a free identifier, but should be unique.
92 */
93 @StructEntry
94 public String charset;
95
96 /**
97 * Platform font name.
98 */
99 @StructEntry
100 @WriteExplicitly
101 public String name = "";
102
103 /**
104 * File name.
105 */
106 @StructEntry
107 @WriteExplicitly
108 public String file = "";
109
110 /**
111 * Constructs a new {@code FontEntry}.
112 */
113 public FontEntry() {
114 // Default constructor needed for construction by reflection
115 }
116
117 /**
118 * Constructs a new {@code FontEntry}.
119 * @param charset The character subset. Basically a free identifier, but should be unique
120 * @param name Platform font name
121 * @param file File name
122 */
123 public FontEntry(String charset, String name, String file) {
124 this.charset = charset;
125 this.name = name;
126 this.file = file;
127 }
128 }
129
130 private static final byte[] INSECURE_PUBLIC_KEY = new byte[] {
131 0x30, (byte) 0x82, 0x1, 0x22, 0x30, 0xd, 0x6, 0x9, 0x2a, (byte) 0x86, 0x48,
132 (byte) 0x86, (byte) 0xf7, 0xd, 0x1, 0x1, 0x1, 0x5, 0x0, 0x3, (byte) 0x82, 0x1, 0xf, 0x0,
133 0x30, (byte) 0x82, 0x01, 0x0a, 0x02, (byte) 0x82, 0x01, 0x01, 0x00, (byte) 0x95, (byte) 0x95, (byte) 0x88,
134 (byte) 0x84, (byte) 0xc8, (byte) 0xd9, 0x6b, (byte) 0xc5, (byte) 0xda, 0x0b, 0x69, (byte) 0xbf, (byte) 0xfc,
135 0x7e, (byte) 0xb9, (byte) 0x96, 0x2c, (byte) 0xeb, (byte) 0x8f, (byte) 0xbc, 0x6e, 0x40, (byte) 0xe6, (byte) 0xe2,
136 (byte) 0xfc, (byte) 0xf1, 0x7f, 0x73, (byte) 0xa7, (byte) 0x9d, (byte) 0xde, (byte) 0xc7, (byte) 0x88, 0x57, 0x51,
137 (byte) 0x84, (byte) 0xed, (byte) 0x96, (byte) 0xfb, (byte) 0xe1, 0x38, (byte) 0xef, 0x08, 0x2b, (byte) 0xf3,
138 (byte) 0xc7, (byte) 0xc3, 0x5d, (byte) 0xfe, (byte) 0xf9, 0x51, (byte) 0xe6, 0x29, (byte) 0xfc, (byte) 0xe5, 0x0d,
139 (byte) 0xa1, 0x0d, (byte) 0xa8, (byte) 0xb4, (byte) 0xae, 0x26, 0x18, 0x19, 0x4d, 0x6c, 0x0c, 0x3b, 0x12, (byte) 0xba,
140 (byte) 0xbc, 0x5f, 0x32, (byte) 0xb3, (byte) 0xbe, (byte) 0x9d, 0x17, 0x0d, 0x4d, 0x2f, 0x1a, 0x48, (byte) 0xb7,
141 (byte) 0xac, (byte) 0xf7, 0x1a, 0x43, 0x01, (byte) 0x97, (byte) 0xf4, (byte) 0xf8, 0x4c, (byte) 0xbb, 0x6a, (byte) 0xbc,
142 0x33, (byte) 0xe1, 0x73, 0x1e, (byte) 0x86, (byte) 0xfb, 0x2e, (byte) 0xb1, 0x63, 0x75, (byte) 0x85, (byte) 0xdc,
143 (byte) 0x82, 0x6c, 0x28, (byte) 0xf1, (byte) 0xe3, (byte) 0x90, 0x63, (byte) 0x9d, 0x3d, 0x48, (byte) 0x8a, (byte) 0x8c,
144 0x47, (byte) 0xe2, 0x10, 0x0b, (byte) 0xef, (byte) 0x91, (byte) 0x94, (byte) 0xb0, 0x6c, 0x4c, (byte) 0x80, 0x76, 0x03,
145 (byte) 0xe1, (byte) 0xb6, (byte) 0x90, (byte) 0x87, (byte) 0xd9, (byte) 0xae, (byte) 0xf4, (byte) 0x8e, (byte) 0xe0,
146 (byte) 0x9f, (byte) 0xe7, 0x3a, 0x2c, 0x2f, 0x21, (byte) 0xd4, 0x46, (byte) 0xba, (byte) 0x95, 0x70, (byte) 0xa9, 0x5b,
147 0x20, 0x2a, (byte) 0xfa, 0x52, 0x3e, (byte) 0x9d, (byte) 0xd9, (byte) 0xef, 0x28, (byte) 0xc5, (byte) 0xd1, 0x60,
148 (byte) 0x89, 0x68, 0x6e, 0x7f, (byte) 0xd7, (byte) 0x9e, (byte) 0x89, 0x4c, (byte) 0xeb, 0x4d, (byte) 0xd2, (byte) 0xc6,
149 (byte) 0xf4, 0x2d, 0x02, 0x5d, (byte) 0xda, (byte) 0xde, 0x33, (byte) 0xfe, (byte) 0xc1, 0x7e, (byte) 0xde, 0x4f, 0x1f,
150 (byte) 0x9b, 0x6e, 0x6f, 0x0f, 0x66, 0x71, 0x19, (byte) 0xe9, 0x43, 0x3c, (byte) 0x83, 0x0a, 0x0f, 0x28, 0x21, (byte) 0xc8,
151 0x38, (byte) 0xd3, 0x4e, 0x48, (byte) 0xdf, (byte) 0xd4, (byte) 0x99, (byte) 0xb5, (byte) 0xc6, (byte) 0x8d, (byte) 0xd4,
152 (byte) 0xc1, 0x69, 0x58, 0x79, (byte) 0x82, 0x32, (byte) 0x82, (byte) 0xd4, (byte) 0x86, (byte) 0xe2, 0x04, 0x08, 0x63,
153 (byte) 0x87, (byte) 0xf0, 0x2a, (byte) 0xf6, (byte) 0xec, 0x3e, 0x51, 0x0f, (byte) 0xda, (byte) 0xb4, 0x67, 0x19, 0x5e,
154 0x16, 0x02, (byte) 0x9f, (byte) 0xf1, 0x19, 0x0c, 0x3e, (byte) 0xb8, 0x04, 0x49, 0x07, 0x53, 0x02, 0x03, 0x01, 0x00, 0x01
155 };
156
157 private static final String WINDOWS_ROOT = "Windows-ROOT";
158
159 private static final String CURRENT_VERSION = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
160
161 private String oSBuildNumber;
162
163 @Override
164 public Platform getPlatform() {
165 return Platform.WINDOWS;
166 }
167
168 @Override
169 public void afterPrefStartupHook() {
170 extendFontconfig("fontconfig.properties.src");
171 }
172
173 @Override
174 public void startupHook(JavaExpirationCallback callback) {
175 checkExpiredJava(callback);
176 }
177
178 @Override
179 public void openUrl(String url) throws IOException {
180 Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
181 }
182
183 @Override
184 public void initSystemShortcuts() {
185 // CHECKSTYLE.OFF: LineLength
186 //Shortcut.registerSystemCut("system:menuexit", tr("reserved"), VK_Q, CTRL_DOWN_MASK);
187 Shortcut.registerSystemShortcut("system:duplicate", tr("reserved"), VK_D, CTRL_DOWN_MASK); // not really system, but to avoid odd results
188
189 // Windows 7 shortcuts: http://windows.microsoft.com/en-US/windows7/Keyboard-shortcuts
190
191 // Shortcuts with setAutomatic(): items with automatic shortcuts will not be added to the menu bar at all
192
193 // Don't know why Ctrl-Alt-Del isn't even listed on official Microsoft support page
194 Shortcut.registerSystemShortcut("system:reset", tr("reserved"), VK_DELETE, CTRL_DOWN_MASK | ALT_DOWN_MASK).setAutomatic();
195
196 // Ease of Access keyboard shortcuts
197 Shortcut.registerSystemShortcut("microsoft-reserved-01", tr("reserved"), VK_PRINTSCREEN, ALT_DOWN_MASK | SHIFT_DOWN_MASK).setAutomatic(); // Turn High Contrast on or off
198 Shortcut.registerSystemShortcut("microsoft-reserved-02", tr("reserved"), VK_NUM_LOCK, ALT_DOWN_MASK | SHIFT_DOWN_MASK).setAutomatic(); // Turn Mouse Keys on or off
199 //Shortcut.registerSystemCut("microsoft-reserved-03", tr("reserved"), VK_U, );// Open the Ease of Access Center (TODO: Windows-U, how to handle it in Java ?)
200
201 // General keyboard shortcuts
202 //Shortcut.registerSystemShortcut("system:help", tr("reserved"), VK_F1, 0); // Display Help
203 Shortcut.registerSystemShortcut("system:copy", tr("reserved"), VK_C, CTRL_DOWN_MASK); // Copy the selected item
204 Shortcut.registerSystemShortcut("system:cut", tr("reserved"), VK_X, CTRL_DOWN_MASK); // Cut the selected item
205 Shortcut.registerSystemShortcut("system:paste", tr("reserved"), VK_V, CTRL_DOWN_MASK); // Paste the selected item
206 Shortcut.registerSystemShortcut("system:undo", tr("reserved"), VK_Z, CTRL_DOWN_MASK); // Undo an action
207 Shortcut.registerSystemShortcut("system:redo", tr("reserved"), VK_Y, CTRL_DOWN_MASK); // Redo an action
208 //Shortcut.registerSystemCut("microsoft-reserved-10", tr("reserved"), VK_DELETE, 0); // Delete the selected item and move it to the Recycle Bin
209 //Shortcut.registerSystemCut("microsoft-reserved-11", tr("reserved"), VK_DELETE, SHIFT_DOWN_MASK); // Delete the selected item without moving it to the Recycle Bin first
210 //Shortcut.registerSystemCut("system:rename", tr("reserved"), VK_F2, 0); // Rename the selected item
211 Shortcut.registerSystemShortcut("system:movefocusright", tr("reserved"), VK_RIGHT, CTRL_DOWN_MASK); // Move the cursor to the beginning of the next word
212 Shortcut.registerSystemShortcut("system:movefocusleft", tr("reserved"), VK_LEFT, CTRL_DOWN_MASK); // Move the cursor to the beginning of the previous word
213 Shortcut.registerSystemShortcut("system:movefocusdown", tr("reserved"), VK_DOWN, CTRL_DOWN_MASK); // Move the cursor to the beginning of the next paragraph
214 Shortcut.registerSystemShortcut("system:movefocusup", tr("reserved"), VK_UP, CTRL_DOWN_MASK); // Move the cursor to the beginning of the previous paragraph
215 //Shortcut.registerSystemCut("microsoft-reserved-17", tr("reserved"), VK_RIGHT, CTRL_DOWN_MASK | SHIFT_DOWN_MASK); // Select a block of text
216 //Shortcut.registerSystemCut("microsoft-reserved-18", tr("reserved"), VK_LEFT, CTRL_DOWN_MASK | SHIFT_DOWN_MASK); // Select a block of text
217 //Shortcut.registerSystemCut("microsoft-reserved-19", tr("reserved"), VK_DOWN, CTRL_DOWN_MASK | SHIFT_DOWN_MASK); // Select a block of text
218 //Shortcut.registerSystemCut("microsoft-reserved-20", tr("reserved"), VK_UP, CTRL_DOWN_MASK | SHIFT_DOWN_MASK); // Select a block of text
219 //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
220 //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
221 //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
222 //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
223 //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 ?)
224 //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 ?)
225 //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 ?)
226 //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 ?)
227 Shortcut.registerSystemShortcut("system:selectall", tr("reserved"), VK_A, CTRL_DOWN_MASK); // Select all items in a document or window
228 //Shortcut.registerSystemCut("system:search", tr("reserved"), VK_F3, 0); // Search for a file or folder
229 Shortcut.registerSystemShortcut("microsoft-reserved-31", tr("reserved"), VK_ENTER, ALT_DOWN_MASK).setAutomatic(); // Display properties for the selected item
230 Shortcut.registerSystemShortcut("system:exit", tr("reserved"), VK_F4, ALT_DOWN_MASK).setAutomatic(); // Close the active item, or exit the active program
231 Shortcut.registerSystemShortcut("microsoft-reserved-33", tr("reserved"), VK_SPACE, ALT_DOWN_MASK).setAutomatic(); // Open the shortcut menu for the active window
232 //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)
233 Shortcut.registerSystemShortcut("microsoft-reserved-35", tr("reserved"), VK_TAB, ALT_DOWN_MASK).setAutomatic(); // Switch between open items
234 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
235 //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 ?)
236 //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 ?)
237 Shortcut.registerSystemShortcut("microsoft-reserved-39", tr("reserved"), VK_ESCAPE, ALT_DOWN_MASK).setAutomatic(); // Cycle through items in the order in which they were opened
238 //Shortcut.registerSystemCut("microsoft-reserved-40", tr("reserved"), VK_F6, 0); // Cycle through screen elements in a window or on the desktop
239 //Shortcut.registerSystemCut("microsoft-reserved-41", tr("reserved"), VK_F4, 0); // Display the address bar list in Windows Explorer
240 Shortcut.registerSystemShortcut("microsoft-reserved-42", tr("reserved"), VK_F10, SHIFT_DOWN_MASK); // Display the shortcut menu for the selected item
241 Shortcut.registerSystemShortcut("microsoft-reserved-43", tr("reserved"), VK_ESCAPE, CTRL_DOWN_MASK).setAutomatic(); // Open the Start menu
242 //Shortcut.registerSystemShortcut("microsoft-reserved-44", tr("reserved"), VK_F10, 0); // Activate the menu bar in the active program
243 //Shortcut.registerSystemCut("microsoft-reserved-45", tr("reserved"), VK_RIGHT, 0); // Open the next menu to the right, or open a submenu
244 //Shortcut.registerSystemCut("microsoft-reserved-46", tr("reserved"), VK_LEFT, 0); // Open the next menu to the left, or close a submenu
245 //Shortcut.registerSystemCut("microsoft-reserved-47", tr("reserved"), VK_F5, 0); // Refresh the active window
246 //Shortcut.registerSystemCut("microsoft-reserved-48", tr("reserved"), VK_UP, ALT_DOWN_MASK); // View the folder one level up in Windows Explorer
247 //Shortcut.registerSystemCut("microsoft-reserved-49", tr("reserved"), VK_ESCAPE, 0); // Cancel the current task
248 Shortcut.registerSystemShortcut("microsoft-reserved-50", tr("reserved"), VK_ESCAPE, CTRL_DOWN_MASK | SHIFT_DOWN_MASK).setAutomatic(); // Open Task Manager
249 Shortcut.registerSystemShortcut("microsoft-reserved-51", tr("reserved"), VK_SHIFT, ALT_DOWN_MASK).setAutomatic(); // Switch the input language when multiple input languages are enabled
250 Shortcut.registerSystemShortcut("microsoft-reserved-52", tr("reserved"), VK_SHIFT, CTRL_DOWN_MASK).setAutomatic(); // Switch the keyboard layout when multiple keyboard layouts are enabled
251 //Shortcut.registerSystemCut("microsoft-reserved-53", tr("reserved"), ); // Change the reading direction of text in right-to-left reading languages (TODO: unclear)
252 // CHECKSTYLE.ON: LineLength
253 }
254
255 @Override
256 public String getDefaultStyle() {
257 return "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
258 }
259
260 @Override
261 public boolean rename(File from, File to) {
262 if (to.exists())
263 Utils.deleteFile(to);
264 return from.renameTo(to);
265 }
266
267 @Override
268 public String getOSDescription() {
269 return Utils.strip(System.getProperty("os.name")) + ' ' +
270 ((System.getenv("ProgramFiles(x86)") == null) ? "32" : "64") + "-Bit";
271 }
272
273 /**
274 * Returns the Windows product name from registry (example: "Windows 10 Pro")
275 * @return the Windows product name from registry
276 * @throws IllegalAccessException if Java language access control is enforced and the underlying method is inaccessible
277 * @throws InvocationTargetException if the underlying method throws an exception
278 * @since 12744
279 */
280 public static String getProductName() throws IllegalAccessException, InvocationTargetException {
281 return WinRegistry.readString(HKEY_LOCAL_MACHINE, CURRENT_VERSION, "ProductName");
282 }
283
284 /**
285 * Returns the Windows release identifier from registry (example: "1703")
286 * @return the Windows release identifier from registry
287 * @throws IllegalAccessException if Java language access control is enforced and the underlying method is inaccessible
288 * @throws InvocationTargetException if the underlying method throws an exception
289 * @since 12744
290 */
291 public static String getReleaseId() throws IllegalAccessException, InvocationTargetException {
292 return WinRegistry.readString(HKEY_LOCAL_MACHINE, CURRENT_VERSION, "ReleaseId");
293 }
294
295 /**
296 * Returns the Windows current build number from registry (example: "15063")
297 * @return the Windows current build number from registry
298 * @throws IllegalAccessException if Java language access control is enforced and the underlying method is inaccessible
299 * @throws InvocationTargetException if the underlying method throws an exception
300 * @since 12744
301 */
302 public static String getCurrentBuild() throws IllegalAccessException, InvocationTargetException {
303 return WinRegistry.readString(HKEY_LOCAL_MACHINE, CURRENT_VERSION, "CurrentBuild");
304 }
305
306 private static String buildOSBuildNumber() {
307 StringBuilder sb = new StringBuilder();
308 try {
309 sb.append(getProductName());
310 String releaseId = getReleaseId();
311 if (releaseId != null) {
312 sb.append(' ').append(releaseId);
313 }
314 sb.append(" (").append(getCurrentBuild()).append(')');
315 } catch (ReflectiveOperationException | JosmRuntimeException e) {
316 Logging.error(e);
317 }
318 return sb.toString();
319 }
320
321 @Override
322 public String getOSBuildNumber() {
323 if (oSBuildNumber == null) {
324 oSBuildNumber = buildOSBuildNumber();
325 }
326 return oSBuildNumber;
327 }
328
329 /**
330 * Loads Windows-ROOT keystore.
331 * @return Windows-ROOT keystore
332 * @throws NoSuchAlgorithmException if the algorithm used to check the integrity of the keystore cannot be found
333 * @throws CertificateException if any of the certificates in the keystore could not be loaded
334 * @throws IOException if there is an I/O or format problem with the keystore data, if a password is required but not given
335 * @throws KeyStoreException if no Provider supports a KeyStore implementation for the type "Windows-ROOT"
336 * @since 7343
337 */
338 public static KeyStore getRootKeystore() throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException {
339 KeyStore ks = KeyStore.getInstance(WINDOWS_ROOT);
340 ks.load(null, null);
341 return ks;
342 }
343
344 /**
345 * Removes potential insecure certificates installed with previous versions of JOSM on Windows.
346 * @throws NoSuchAlgorithmException on unsupported signature algorithms
347 * @throws CertificateException if any of the certificates in the Windows keystore could not be loaded
348 * @throws KeyStoreException if no Provider supports a KeyStoreSpi implementation for the type "Windows-ROOT"
349 * @throws IOException if there is an I/O or format problem with the keystore data, if a password is required but not given
350 * @since 7335
351 */
352 public static void removeInsecureCertificates() throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
353 // 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)
354 PublicKey insecurePubKey = null;
355 try {
356 insecurePubKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(INSECURE_PUBLIC_KEY));
357 } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
358 Logging.error(e);
359 return;
360 }
361 KeyStore ks = getRootKeystore();
362 Enumeration<String> en = ks.aliases();
363 Collection<String> insecureCertificates = new ArrayList<>();
364 while (en.hasMoreElements()) {
365 String alias = en.nextElement();
366 // Look for certificates associated with a private key
367 if (ks.isKeyEntry(alias)) {
368 try {
369 ks.getCertificate(alias).verify(insecurePubKey);
370 // If no exception, this is a certificate signed with the insecure key -> remove it
371 insecureCertificates.add(alias);
372 } catch (InvalidKeyException | NoSuchProviderException | SignatureException e) {
373 // If exception this is not a certificate related to JOSM, just trace it
374 Logging.trace(alias + " --> " + e.getClass().getName());
375 Logging.trace(e);
376 }
377 }
378 }
379 // Remove insecure certificates
380 if (!insecureCertificates.isEmpty()) {
381 StringBuilder message = new StringBuilder("<html>");
382 message.append(tr("A previous version of JOSM has installed a custom certificate "+
383 "in order to provide HTTPS support for Remote Control:"))
384 .append("<br><ul>");
385 for (String alias : insecureCertificates) {
386 message.append("<li>")
387 .append(alias)
388 .append("</li>");
389 }
390 message.append("</ul>")
391 .append(tr("It appears it could be an important <b>security risk</b>.<br><br>"+
392 "You are now going to be prompted by Windows to remove this insecure certificate.<br>"+
393 "For your own safety, <b>please click Yes</b> in next dialog."))
394 .append("</html>");
395 JOptionPane.showMessageDialog(Main.parent, message.toString(), tr("Warning"), JOptionPane.WARNING_MESSAGE);
396 for (String alias : insecureCertificates) {
397 Logging.warn(tr("Removing insecure certificate from {0} keystore: {1}", WINDOWS_ROOT, alias));
398 try {
399 ks.deleteEntry(alias);
400 } catch (KeyStoreException e) {
401 Logging.log(Logging.LEVEL_ERROR, tr("Unable to remove insecure certificate from keystore: {0}", e.getMessage()), e);
402 }
403 }
404 }
405 }
406
407 @Override
408 public boolean setupHttpsCertificate(String entryAlias, KeyStore.TrustedCertificateEntry trustedCert)
409 throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
410 KeyStore ks = getRootKeystore();
411 // Look for certificate to install
412 try {
413 String alias = ks.getCertificateAlias(trustedCert.getTrustedCertificate());
414 if (alias != null) {
415 // JOSM certificate found, return
416 Logging.debug(tr("JOSM localhost certificate found in {0} keystore: {1}", WINDOWS_ROOT, alias));
417 return false;
418 }
419 } catch (ArrayIndexOutOfBoundsException e) {
420 // catch error of JDK-8172244 as bug seems to not be fixed anytime soon
421 Logging.log(Logging.LEVEL_ERROR, "JDK-8172244 occured. Abort HTTPS setup", e);
422 return false;
423 }
424 if (!GraphicsEnvironment.isHeadless()) {
425 // JOSM certificate not found, warn user
426 StringBuilder message = new StringBuilder("<html>");
427 message.append(tr("Remote Control is configured to provide HTTPS support.<br>"+
428 "This requires to add a custom certificate generated by JOSM to the Windows Root CA store.<br><br>"+
429 "You are now going to be prompted by Windows to confirm this operation.<br>"+
430 "To enable proper HTTPS support, <b>please click Yes</b> in next dialog.<br><br>"+
431 "If unsure, you can also click No then disable HTTPS support in Remote Control preferences."))
432 .append("</html>");
433 JOptionPane.showMessageDialog(Main.parent, message.toString(),
434 tr("HTTPS support in Remote Control"), JOptionPane.INFORMATION_MESSAGE);
435 }
436 // install it to Windows-ROOT keystore, used by IE, Chrome and Safari, but not by Firefox
437 Logging.info(tr("Adding JOSM localhost certificate to {0} keystore", WINDOWS_ROOT));
438 ks.setEntry(entryAlias, trustedCert, null);
439 return true;
440 }
441
442 @Override
443 public X509Certificate getX509Certificate(NativeCertAmend certAmend)
444 throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
445 // Make a web request to target site to force Windows to update if needed its trust root store from its certificate trust list
446 // A better, but a lot more complex method might be to get certificate list from Windows Registry with PowerShell
447 // using (Get-ItemProperty -Path 'HKLM:\\SOFTWARE\\Microsoft\\SystemCertificates\\AuthRoot\\AutoUpdate').EncodedCtl)
448 // then decode it using CertUtil -dump or calling CertCreateCTLContext API using JNI, and finally find and decode the certificate
449 try {
450 // https://stackoverflow.com/a/41618979/2257172
451 Utils.execOutput(Arrays.asList("powershell", "-Command",
452 "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;" +
453 "Invoke-WebRequest " + certAmend.getWebSite()));
454 } catch (ExecutionException | InterruptedException e) {
455 Logging.error(e);
456 }
457 // Get Windows Trust Root Store
458 KeyStore ks = getRootKeystore();
459 // Search by alias (fast)
460 Certificate result = ks.getCertificate(certAmend.getWinAlias());
461 if (result instanceof X509Certificate) {
462 return (X509Certificate) result;
463 }
464 // If not found, search by SHA-256 (slower)
465 MessageDigest md = MessageDigest.getInstance("SHA-256");
466 for (Enumeration<String> aliases = ks.aliases(); aliases.hasMoreElements();) {
467 result = ks.getCertificate(aliases.nextElement());
468 if (result instanceof X509Certificate
469 && certAmend.getSha256().equalsIgnoreCase(Utils.toHexString(md.digest(result.getEncoded())))) {
470 return (X509Certificate) result;
471 }
472 }
473 // Not found
474 return null;
475 }
476
477 @Override
478 public File getDefaultCacheDirectory() {
479 String p = System.getenv("LOCALAPPDATA");
480 if (p == null || p.isEmpty()) {
481 // Fallback for Windows OS earlier than Windows Vista, where the variable is not defined
482 p = System.getenv("APPDATA");
483 }
484 return new File(new File(p, Main.pref.getJOSMDirectoryBaseName()), "cache");
485 }
486
487 @Override
488 public File getDefaultPrefDirectory() {
489 return new File(System.getenv("APPDATA"), Main.pref.getJOSMDirectoryBaseName());
490 }
491
492 @Override
493 public File getDefaultUserDataDirectory() {
494 // Use preferences directory by default
495 return Config.getDirs().getPreferencesDirectory(false);
496 }
497
498 /**
499 * <p>Add more fallback fonts to the Java runtime, in order to get
500 * support for more scripts.</p>
501 *
502 * <p>The font configuration in Java doesn't include some Indic scripts,
503 * even though MS Windows ships with fonts that cover these unicode ranges.</p>
504 *
505 * <p>To fix this, the fontconfig.properties template is copied to the JOSM
506 * cache folder. Then, the additional entries are added to the font
507 * configuration. Finally the system property "sun.awt.fontconfig" is set
508 * to the customized fontconfig.properties file.</p>
509 *
510 * <p>This is a crude hack, but better than no font display at all for these languages.
511 * There is no guarantee, that the template file
512 * ($JAVA_HOME/lib/fontconfig.properties.src) matches the default
513 * configuration (which is in a binary format).
514 * Furthermore, the system property "sun.awt.fontconfig" is undocumented and
515 * may no longer work in future versions of Java.</p>
516 *
517 * <p>Related Java bug: <a href="https://bugs.openjdk.java.net/browse/JDK-8008572">JDK-8008572</a></p>
518 *
519 * @param templateFileName file name of the fontconfig.properties template file
520 */
521 protected void extendFontconfig(String templateFileName) {
522 String customFontconfigFile = Config.getPref().get("fontconfig.properties", null);
523 if (customFontconfigFile != null) {
524 Utils.updateSystemProperty("sun.awt.fontconfig", customFontconfigFile);
525 return;
526 }
527 if (!Config.getPref().getBoolean("font.extended-unicode", true))
528 return;
529
530 String javaLibPath = System.getProperty("java.home") + File.separator + "lib";
531 Path templateFile = FileSystems.getDefault().getPath(javaLibPath, templateFileName);
532 if (!Files.isReadable(templateFile)) {
533 Logging.warn("extended font config - unable to find font config template file {0}", templateFile.toString());
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(System.getenv("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}
Note: See TracBrowser for help on using the repository browser.