--- a/core.ui/src/org/netbeans/core/ui/options/general/GeneralOptionsModel.java
+++ a/core.ui/src/org/netbeans/core/ui/options/general/GeneralOptionsModel.java
@@ -187,7 +187,6 @@
return ProxySettings.getAuthenticationUsername ();
}
- //TODO: not used yet - store valu just in case if modified
void setAuthenticationUsername (String username) {
getProxyPreferences ().put (ProxySettings.PROXY_AUTHENTICATION_USERNAME, username);
}
@@ -196,9 +195,8 @@
return ProxySettings.getAuthenticationPassword ();
}
- //TODO: not used yet - store valu just in case if modified
void setAuthenticationPassword(char [] password) {
- getProxyPreferences().put(ProxySettings.PROXY_AUTHENTICATION_PASSWORD, new String(password));
+ ProxySettings.setAuthenticationPassword(password);
}
// private helper methods ..................................................
--- a/hudson/nbproject/project.xml
+++ a/hudson/nbproject/project.xml
@@ -51,6 +51,14 @@
+ org.netbeans.modules.keyring
+
+
+
+ 1.0
+
+
+ org.netbeans.modules.options.api
--- a/hudson/src/org/netbeans/modules/hudson/ui/Bundle.properties
+++ a/hudson/src/org/netbeans/modules/hudson/ui/Bundle.properties
@@ -41,3 +41,6 @@
FormLogin.userLabel.text=&Username:
FormLogin.passLabel.text=&Password:
FormLogin.log_in=Log in to Hudson
+# {0} - server location
+# {1} - user name
+FormLogin.password_description=Password for {1} on {0}
--- a/hudson/src/org/netbeans/modules/hudson/ui/FormLogin.java
+++ a/hudson/src/org/netbeans/modules/hudson/ui/FormLogin.java
@@ -42,6 +42,7 @@
import java.net.URL;
import java.util.prefs.Preferences;
import javax.swing.JPanel;
+import org.netbeans.api.keyring.Keyring;
import org.netbeans.modules.hudson.impl.HudsonManagerImpl;
import org.netbeans.modules.hudson.spi.PasswordAuthorizer;
import org.openide.DialogDescriptor;
@@ -73,6 +74,10 @@
String username = loginPrefs().get(server, null);
if (username != null) {
panel.userField.setText(username);
+ char[] savedPassword = Keyring.read(server);
+ if (savedPassword != null) {
+ panel.passField.setText(new String(savedPassword));
+ }
}
panel.locationField.setText(home.toString());
DialogDescriptor dd = new DialogDescriptor(panel, NbBundle.getMessage(FormLogin.class, "FormLogin.log_in"));
@@ -83,6 +88,7 @@
loginPrefs().put(server, username);
String password = new String(panel.passField.getPassword());
panel.passField.setText("");
+ Keyring.save(server, password.toCharArray(), NbBundle.getMessage(FormLogin.class, "FormLogin.password_description", home, username));
return new String[] {username, password};
}
}
--- a/kenai.ui/nbproject/project.xml
+++ a/kenai.ui/nbproject/project.xml
@@ -94,6 +94,14 @@
+ org.netbeans.modules.keyring
+
+
+
+ 1.0
+
+
+ org.netbeans.modules.mercurial
--- a/kenai.ui/src/org/netbeans/modules/kenai/ui/LoginPanel.form
+++ a/kenai.ui/src/org/netbeans/modules/kenai/ui/LoginPanel.form
@@ -34,7 +34,7 @@
-
+
@@ -66,7 +66,7 @@
-
+
@@ -164,6 +164,7 @@
+
--- a/kenai.ui/src/org/netbeans/modules/kenai/ui/LoginPanel.java
+++ a/kenai.ui/src/org/netbeans/modules/kenai/ui/LoginPanel.java
@@ -200,6 +200,7 @@
lblPassword.setLabelFor(password);
org.openide.awt.Mnemonics.setLocalizedText(lblPassword, org.openide.util.NbBundle.getMessage(LoginPanel.class, "LoginPanel.lblPassword.text")); // NOI18N
+ chkRememberMe.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(chkRememberMe, org.openide.util.NbBundle.getMessage(LoginPanel.class, "LoginPanel.chkRememberMe.text")); // NOI18N
chkRememberMe.setToolTipText(org.openide.util.NbBundle.getMessage(LoginPanel.class, "LoginPanel.chkRememberMe.toolTipText")); // NOI18N
chkRememberMe.addActionListener(new java.awt.event.ActionListener() {
@@ -237,7 +238,7 @@
.add(layout.createSequentialGroup()
.add(lblKenaiLogoLeft)
.add(0, 0, 0)
- .add(lblKenaiLogoCenter, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 196, Short.MAX_VALUE)
+ .add(lblKenaiLogoCenter, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 202, Short.MAX_VALUE)
.add(0, 0, 0)
.add(lblKenaiLogoRight))
.add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
@@ -262,7 +263,7 @@
.addContainerGap())
.add(layout.createSequentialGroup()
.addContainerGap()
- .add(progressBar, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 418, Short.MAX_VALUE)
+ .add(progressBar, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 424, Short.MAX_VALUE)
.addContainerGap())
);
layout.setVerticalGroup(
--- a/kenai.ui/src/org/netbeans/modules/kenai/ui/spi/Bundle.properties
+++ a/kenai.ui/src/org/netbeans/modules/kenai/ui/spi/Bundle.properties
@@ -0,0 +1,39 @@
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+#
+# The contents of this file are subject to the terms of either the GNU
+# General Public License Version 2 only ("GPL") or the Common
+# Development and Distribution License("CDDL") (collectively, the
+# "License"). You may not use this file except in compliance with the
+# License. You can obtain a copy of the License at
+# http://www.netbeans.org/cddl-gplv2.html
+# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+# specific language governing permissions and limitations under the
+# License. When distributing the software, include this License Header
+# Notice in each file and include the License file at
+# nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Sun in the GPL Version 2 section of the License file that
+# accompanied this code. If applicable, add the following below the
+# License Header, with the fields enclosed by brackets [] replaced by
+# your own identifying information:
+# "Portions Copyrighted [year] [name of copyright owner]"
+#
+# If you wish your version of this file to be governed by only the CDDL
+# or only the GPL Version 2, indicate your decision by adding
+# "[Contributor] elects to include this software in this distribution
+# under the [CDDL or GPL Version 2] license." If you do not indicate a
+# single choice of license, a recipient has the option to distribute
+# your version of this file under either the CDDL, the GPL Version 2 or
+# to extend the choice of license to its licensees as provided above.
+# However, if you add GPL Version 2 code and therefore, elected the GPL
+# Version 2 license, then the option applies only if the new code is
+# made subject to such option by the copyright holder.
+#
+# Contributor(s):
+#
+# Portions Copyrighted 2009 Sun Microsystems, Inc.
+
+# {0} - kenai.com or similar
+UIUtils.password_keyring_description=Password for {0}
--- a/kenai.ui/src/org/netbeans/modules/kenai/ui/spi/Scrambler.java
+++ a/kenai.ui/src/org/netbeans/modules/kenai/ui/spi/Scrambler.java
@@ -45,17 +45,9 @@
import java.io.ByteArrayOutputStream;
-/**
- * Scrambles text (the password) using the standard scheme described in the
- * CVS protocol version 1.10. This encoding is trivial and should not be
- * used for security, but rather as a mechanism for avoiding inadvertant
- * compromise.
- * @author Robert Greig, Tomas Stupka
- */
+@Deprecated
class Scrambler {
- private static final char [] characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray(); // NOI18N
-
/**
* The mapping array
*/
@@ -313,21 +305,6 @@
return instance;
}
- /**
- * Scramble text, turning it into a String of scrambled data
- * @return a String of scrambled data
- */
- public String scramble(String text) {
- StringBuffer buf = new StringBuffer("A"); //NOI18N
-
- if (text != null) {
- for (int i = 0; i < text.length(); ++i) {
- buf.append(scramble(text.charAt(i)));
- }
- }
- return new String(encode(buf.toString().getBytes()));
- }
-
public String descramble(String scrambledText) {
StringBuffer buf = new StringBuffer();
if (scrambledText != null) {
@@ -348,51 +325,6 @@
private byte[] decode(String str) {
return decode64(str);
}
-
- private byte[] encode(byte[] encode) {
- return encode64(encode).getBytes();
- }
-
- private static String encode64(byte [] data) {
- return encode64(data, false);
- }
-
- private static String encode64(byte [] data, boolean useNewlines) {
- int length = data.length;
- StringBuffer sb = new StringBuffer(data.length * 3 / 2);
-
- int end = length - 3;
- int i = 0;
- int lineCount = 0;
-
- while (i <= end) {
- int d = ((((int) data[i]) & 0xFF) << 16) | ((((int) data[i + 1]) & 0xFF) << 8) | (((int) data[i + 2]) & 0xFF);
- sb.append(characters[(d >> 18) & 0x3F]);
- sb.append(characters[(d >> 12) & 0x3F]);
- sb.append(characters[(d >> 6) & 0x3F]);
- sb.append(characters[d & 0x3F]);
- i += 3;
-
- if (useNewlines && lineCount++ >= 14) {
- lineCount = 0;
- sb.append(System.getProperty("line.separator"));
- }
- }
-
- if (i == length - 2) {
- int d = ((((int) data[i]) & 0xFF) << 16) | ((((int) data[i + 1]) & 0xFF) << 8);
- sb.append(characters[(d >> 18) & 0x3F]);
- sb.append(characters[(d >> 12) & 0x3F]);
- sb.append(characters[(d >> 6) & 0x3F]);
- sb.append("="); // NOI18N
- } else if (i == length - 1) {
- int d = (((int) data[i]) & 0xFF) << 16;
- sb.append(characters[(d >> 18) & 0x3F]);
- sb.append(characters[(d >> 12) & 0x3F]);
- sb.append("=="); // NOI18N
- }
- return sb.toString();
- }
private static byte [] decode64(String s) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
--- a/kenai.ui/src/org/netbeans/modules/kenai/ui/spi/UIUtils.java
+++ a/kenai.ui/src/org/netbeans/modules/kenai/ui/spi/UIUtils.java
@@ -57,6 +57,7 @@
import javax.swing.JLabel;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
+import org.netbeans.api.keyring.Keyring;
import org.netbeans.modules.kenai.api.Kenai;
import org.netbeans.modules.kenai.api.KenaiException;
import org.netbeans.modules.kenai.api.KenaiUser;
@@ -125,11 +126,15 @@
if (uname==null) {
return false;
}
- String password=preferences.get(getPrefName(KENAI_PASSWORD_PREF), null); // NOI18N
PresenceIndicator.getDefault().init();
try {
KenaiConnection.getDefault();
- Kenai.getDefault().login(uname, Scrambler.getInstance().descramble(password).toCharArray(), force?true:Boolean.parseBoolean(preferences.get(getPrefName(ONLINE_STATUS_PREF), String.valueOf(Utilities.isChatSupported()))));
+ char[] password = loadPassword(preferences);
+ if (password == null) {
+ return false;
+ }
+ Kenai.getDefault().login(uname, password,
+ force ? true : Boolean.parseBoolean(preferences.get(getPrefName(ONLINE_STATUS_PREF), String.valueOf(Utilities.isChatSupported()))));
} catch (KenaiException ex) {
return false;
}
@@ -137,6 +142,24 @@
}
/**
+ * Loads password from the keyring. For settings compatibility,
+ * can also interpret and upgrade old insecure storage.
+ */
+ @SuppressWarnings("deprecation")
+ private static char[] loadPassword(Preferences preferences) {
+ String passwordPref = getPrefName(KENAI_PASSWORD_PREF);
+ String scrambledPassword = preferences.get(passwordPref, null); // NOI18N
+ char[] newPassword = Keyring.read(passwordPref);
+ if (scrambledPassword != null) {
+ preferences.remove(passwordPref);
+ if (newPassword == null) {
+ return Scrambler.getInstance().descramble(scrambledPassword).toCharArray();
+ }
+ }
+ return newPassword;
+ }
+
+ /**
* Invokes login dialog
* @return true, if user was succesfully logged in
*/
@@ -186,13 +209,16 @@
}
}
});
+ String passwordPref = getPrefName(KENAI_PASSWORD_PREF);
if (loginPanel.isStorePassword()) {
preferences.put(getPrefName(KENAI_USERNAME_PREF), loginPanel.getUsername()); // NOI18N
- preferences.put(getPrefName(KENAI_PASSWORD_PREF), Scrambler.getInstance().scramble(new String(loginPanel.getPassword()))); // NOI18N
+ Keyring.save(passwordPref, loginPanel.getPassword(),
+ NbBundle.getMessage(UIUtils.class, "UIUtils.password_keyring_description", Kenai.getDefault().getUrl().getHost()));
} else {
preferences.remove(getPrefName(KENAI_USERNAME_PREF)); // NOI18N
- preferences.remove(getPrefName(KENAI_PASSWORD_PREF)); // NOI18N
+ Keyring.delete(passwordPref);
}
+ preferences.remove(passwordPref);
} else {
loginPanel.putClientProperty("cancel", "true"); // NOI18N
JDialog parent = (JDialog) loginPanel.getRootPane().getParent();
@@ -205,10 +231,12 @@
Dialog d = DialogDisplayer.getDefault().createDialog(login);
String uname=preferences.get(getPrefName(KENAI_USERNAME_PREF), null); // NOI18N
- String password=preferences.get(getPrefName(KENAI_PASSWORD_PREF), null); // NOI18N
- if (uname!=null && password!=null) {
+ if (uname != null) {
loginPanel.setUsername(uname);
- loginPanel.setPassword(Scrambler.getInstance().descramble(password).toCharArray());
+ char[] password = loadPassword(preferences);
+ if (password != null) {
+ loginPanel.setPassword(password);
+ }
}
d.pack();
d.setResizable(false);
--- a/kenai.ui/test/unit/src/org/netbeans/modules/kenai/ui/spi/UIUtilsTest.java
+++ a/kenai.ui/test/unit/src/org/netbeans/modules/kenai/ui/spi/UIUtilsTest.java
@@ -1,80 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
- *
- * The contents of this file are subject to the terms of either the GNU
- * General Public License Version 2 only ("GPL") or the Common
- * Development and Distribution License("CDDL") (collectively, the
- * "License"). You may not use this file except in compliance with the
- * License. You can obtain a copy of the License at
- * http://www.netbeans.org/cddl-gplv2.html
- * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
- * specific language governing permissions and limitations under the
- * License. When distributing the software, include this License Header
- * Notice in each file and include the License file at
- * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Sun in the GPL Version 2 section of the License file that
- * accompanied this code. If applicable, add the following below the
- * License Header, with the fields enclosed by brackets [] replaced by
- * your own identifying information:
- * "Portions Copyrighted [year] [name of copyright owner]"
- *
- * If you wish your version of this file to be governed by only the CDDL
- * or only the GPL Version 2, indicate your decision by adding
- * "[Contributor] elects to include this software in this distribution
- * under the [CDDL or GPL Version 2] license." If you do not indicate a
- * single choice of license, a recipient has the option to distribute
- * your version of this file under either the CDDL, the GPL Version 2 or
- * to extend the choice of license to its licensees as provided above.
- * However, if you add GPL Version 2 code and therefore, elected the GPL
- * Version 2 license, then the option applies only if the new code is
- * made subject to such option by the copyright holder.
- *
- * Contributor(s):
- *
- * Portions Copyrighted 2009 Sun Microsystems, Inc.
- */
-
-package org.netbeans.modules.kenai.ui.spi;
-
-import java.util.prefs.Preferences;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.openide.util.NbPreferences;
-import static org.junit.Assert.*;
-
-/**
- *
- * @author Jan Becicka
- */
-public class UIUtilsTest {
-
- public UIUtilsTest() {
- }
-
- @BeforeClass
- public static void setUpClass() throws Exception {
- }
-
- @AfterClass
- public static void tearDownClass() throws Exception {
- }
-
- /**
- * Test of showLogin method, of class UIUtils.
- */
- @Test
- public void testEncodeDecode() {
- String testpass = "pswd";
- String scram = Scrambler.getInstance().scramble(testpass);
- Preferences preferences=NbPreferences.forModule(UIUtils.class);
- preferences.put("kenai.test.password", scram);
- String newp=preferences.get("kenai.test.password", null);
- String r = Scrambler.getInstance().descramble(newp);
- assertEquals(testpass, r);
- // TODO review the generated test code and remove the default call to fail.
- }
-}
--- a/keyring/build.xml
+++ a/keyring/build.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
--- a/keyring/manifest.mf
+++ a/keyring/manifest.mf
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+OpenIDE-Module: org.netbeans.modules.keyring
+OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/keyring/Bundle.properties
+OpenIDE-Module-Specification-Version: 1.0
+
--- a/keyring/nbproject/project.properties
+++ a/keyring/nbproject/project.properties
@@ -0,0 +1,4 @@
+is.autoload=true
+javac.source=1.5
+javac.compilerargs=-Xlint -Xlint:-serial
+tryme.args=-J-Dnetbeans.full.hack=true -J-Dorg.netbeans.modules.keyring.level=0
--- a/keyring/nbproject/project.xml
+++ a/keyring/nbproject/project.xml
@@ -0,0 +1,71 @@
+
+
+ org.netbeans.modules.apisupport.project
+
+
+ org.netbeans.modules.keyring
+
+
+ org.jdesktop.layout
+
+
+
+ 1
+ 1.9
+
+
+
+ org.netbeans.libs.jna
+
+
+
+ 1
+ 1.4
+
+
+
+ org.openide.awt
+
+
+
+ 7.16
+
+
+
+ org.openide.dialogs
+
+
+
+ 7.13
+
+
+
+ org.openide.util
+
+
+
+ 7.29
+
+
+
+
+
+ unit
+
+ org.netbeans.libs.junit4
+
+
+
+ org.netbeans.modules.nbjunit
+
+
+
+
+
+
+ org.netbeans.api.keyring
+ org.netbeans.spi.keyring
+
+
+
+
--- a/keyring/src/org/netbeans/api/keyring/Keyring.java
+++ a/keyring/src/org/netbeans/api/keyring/Keyring.java
@@ -0,0 +1,125 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.keyring;
+
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.spi.keyring.KeyringProvider;
+import org.openide.util.Lookup;
+import org.openide.util.Parameters;
+
+/**
+ * Client class for working with stored keys (such as passwords).
+ *
The key identifier should be unique for the whole application,
+ * so qualify it with any prefixes as needed.
+ *
Avoid calling methods on this class from the event dispatch thread,
+ * as some provider implementations may need to block while displaying a dialog
+ * (e.g. prompting for a master password to access the keyring).
+ */
+public class Keyring {
+
+ private Keyring() {}
+
+ private static KeyringProvider PROVIDER;
+ private static KeyringProvider provider() {
+ if (PROVIDER == null) {
+ for (KeyringProvider p : Lookup.getDefault().lookupAll(KeyringProvider.class)) {
+ if (p.enabled()) {
+ PROVIDER = p;
+ break;
+ }
+ }
+ if (PROVIDER == null) {
+ PROVIDER = new DummyKeyringProvider();
+ }
+ Logger.getLogger("org.netbeans.modules.keyring").log(Level.FINE, "Using provider: {0}", PROVIDER);
+ }
+ return PROVIDER;
+ }
+
+ /**
+ * Reads a key from the ring.
+ * @param key the identifier of the key
+ * @return its value if found (you may null out its elements), else null if not present
+ */
+ public static synchronized char[] read(String key) {
+ Parameters.notNull("key", key);
+ return provider().read(key);
+ }
+
+ /**
+ * Saves a key to the ring.
+ * If it could not be saved, does nothing.
+ * If the key already existed, overwrites the password.
+ * @param key a key identifier
+ * @param password the password or other sensitive information associated with the key
+ * (its contents will be nulled out by end of call)
+ * @param description a user-visible description of the key (may be null)
+ */
+ public static synchronized void save(String key, char[] password, String description) {
+ Parameters.notNull("key", key);
+ Parameters.notNull("password", password);
+ provider().save(key, password, description);
+ Arrays.fill(password, (char) 0);
+ }
+
+ /**
+ * Deletes a key from the ring.
+ * If the key was not in the ring to begin with, does nothing.
+ * @param key a key identifier
+ */
+ public static synchronized void delete(String key) {
+ Parameters.notNull("key", key);
+ provider().delete(key);
+ }
+
+ private static class DummyKeyringProvider implements KeyringProvider {
+ public boolean enabled() {
+ return true;
+ }
+ public char[] read(String key) {
+ return null;
+ }
+ public void save(String key, char[] password, String description) {}
+ public void delete(String key) {}
+ }
+
+}
--- a/keyring/src/org/netbeans/modules/keyring/Bundle.properties
+++ a/keyring/src/org/netbeans/modules/keyring/Bundle.properties
@@ -0,0 +1,1 @@
+OpenIDE-Module-Name=Keyring API
--- a/keyring/src/org/netbeans/modules/keyring/Utils.java
+++ a/keyring/src/org/netbeans/modules/keyring/Utils.java
@@ -0,0 +1,69 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.keyring;
+
+import java.util.prefs.Preferences;
+
+public class Utils {
+
+ private Utils() {}
+
+ public static byte[] chars2Bytes(char[] chars) {
+ byte[] bytes = new byte[chars.length * 2];
+ for (int i = 0; i < chars.length; i++) {
+ bytes[i * 2] = (byte) (chars[i] / 256);
+ bytes[i * 2 + 1] = (byte) (chars[i] % 256);
+ }
+ return bytes;
+ }
+
+ public static char[] bytes2Chars(byte[] bytes) {
+ char[] result = new char[bytes.length / 2];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = (char) (((int) bytes[i * 2]) * 256 + (int) bytes[i * 2 + 1]);
+ }
+ return result;
+ }
+
+ public static void goMinusR(Preferences p) {
+ // XXX try to set $userdir/config/${p.absolutePath()}.properties to -rw-------
+ }
+
+}
--- a/keyring/src/org/netbeans/modules/keyring/fallback/Bundle.properties
+++ a/keyring/src/org/netbeans/modules/keyring/fallback/Bundle.properties
@@ -0,0 +1,4 @@
+MasterPasswordPanel.masterPasswordLabel.text=Master &Password:
+MasterPasswordPanel.setNewBox.text=&Change...
+MasterPasswordPanel.newLabel1.text=&New Master Password:
+MasterPasswordPanel.newLabel2.text=&Retype:
--- a/keyring/src/org/netbeans/modules/keyring/fallback/FallbackProvider.java
+++ a/keyring/src/org/netbeans/modules/keyring/fallback/FallbackProvider.java
@@ -0,0 +1,173 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.keyring.fallback;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.Callable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.prefs.Preferences;
+import org.netbeans.modules.keyring.Utils;
+import org.netbeans.modules.keyring.spi.EncryptionProvider;
+import org.netbeans.spi.keyring.KeyringProvider;
+import org.openide.util.Lookup;
+import org.openide.util.NbPreferences;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ * Platform-independent keyring provider using a master password and the user directory.
+ */
+@ServiceProvider(service=KeyringProvider.class, position=1000)
+public class FallbackProvider implements KeyringProvider, Callable {
+
+ private static final Logger LOG = Logger.getLogger(FallbackProvider.class.getName());
+ private static final String DESCRIPTION = ".description";
+ private static final String SAMPLE_KEY = "__sample__";
+
+ private EncryptionProvider encryption;
+
+ public boolean enabled() {
+ for (EncryptionProvider p : Lookup.getDefault().lookupAll(EncryptionProvider.class)) {
+ if (p.enabled()) {
+ encryption = p;
+ LOG.log(Level.FINE, "Using provider: {0}", p);
+ Preferences prefs = prefs();
+ Utils.goMinusR(prefs);
+ p.encryptionChangingCallback(this);
+ if (!testSampleKey(prefs)) {
+ continue;
+ }
+ return true;
+ }
+ }
+ LOG.fine("No provider");
+ return false;
+ }
+
+ private boolean testSampleKey(Preferences prefs) {
+ byte[] ciphertext = prefs().getByteArray(SAMPLE_KEY, null);
+ if (ciphertext == null) {
+ save(SAMPLE_KEY, (SAMPLE_KEY + UUID.randomUUID()).toCharArray(), "Sample value ensuring that decryption is working."); // XXX I18N
+ LOG.fine("saved sample key");
+ return true;
+ } else {
+ while (true) {
+ try {
+ if (new String(encryption.decrypt(ciphertext)).startsWith(SAMPLE_KEY)) {
+ LOG.fine("succeeded in decrypting sample key");
+ return true;
+ } else {
+ LOG.fine("wrong result decrypting sample key");
+ }
+ } catch (Exception x) {
+ LOG.log(Level.FINE, "failed to decrypt sample key", x);
+ }
+ if (!encryption.decryptionFailed()) {
+ LOG.fine("sample key decryption failed and are not retrying");
+ return false;
+ }
+ LOG.fine("will retry decryption of sample key");
+ }
+ }
+ }
+
+ private Preferences prefs() {
+ return NbPreferences.forModule(FallbackProvider.class).node(encryption.id());
+ }
+
+ public char[] read(String key) {
+ byte[] ciphertext = prefs().getByteArray(key, null);
+ if (ciphertext == null) {
+ return null;
+ }
+ try {
+ return encryption.decrypt(ciphertext);
+ } catch (Exception x) {
+ LOG.log(Level.FINE, "failed to decrypt password for " + key, x);
+ }
+ return null;
+ }
+
+ public void save(String key, char[] password, String description) {
+ Preferences prefs = prefs();
+ try {
+ prefs.putByteArray(key, encryption.encrypt(password));
+ } catch (Exception x) {
+ LOG.log(Level.FINE, "failed to encrypt password for " + key, x);
+ return;
+ }
+ if (description != null) {
+ // Preferences interface gives no access to *.properties comments, so:
+ prefs.put(key + DESCRIPTION, description);
+ }
+ }
+
+ public void delete(String key) {
+ Preferences prefs = prefs();
+ prefs.remove(key);
+ prefs.remove(key + DESCRIPTION);
+ }
+
+ public Void call() throws Exception { // encryption changing
+ LOG.fine("encryption changing");
+ Map saved = new HashMap();
+ Preferences prefs = prefs();
+ for (String k : prefs.keys()) {
+ if (k.endsWith(DESCRIPTION)) {
+ continue;
+ }
+ byte[] ciphertext = prefs.getByteArray(k, null);
+ if (ciphertext == null) {
+ continue;
+ }
+ saved.put(k, encryption.decrypt(ciphertext));
+ }
+ LOG.log(Level.FINE, "reencrypting keys: {0}", saved.keySet());
+ encryption.encryptionChanged();
+ for (Map.Entry entry : saved.entrySet()) {
+ prefs.putByteArray(entry.getKey(), encryption.encrypt(entry.getValue()));
+ }
+ LOG.fine("encryption changing finished");
+ return null;
+ }
+
+}
--- a/keyring/src/org/netbeans/modules/keyring/fallback/MasterPasswordEncryption.java
+++ a/keyring/src/org/netbeans/modules/keyring/fallback/MasterPasswordEncryption.java
@@ -0,0 +1,217 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.keyring.fallback;
+
+import java.security.Key;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.KeySpec;
+import java.util.Arrays;
+import java.util.UUID;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.prefs.Preferences;
+import javax.crypto.Cipher;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.PBEParameterSpec;
+import org.netbeans.modules.keyring.Utils;
+import org.netbeans.modules.keyring.spi.EncryptionProvider;
+import org.openide.util.NbPreferences;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ * Encrypts data using a master password which the user must enter for each NetBeans session.
+ */
+@ServiceProvider(service=EncryptionProvider.class, position=1000)
+public class MasterPasswordEncryption implements EncryptionProvider {
+
+ private static final Logger LOG = Logger.getLogger(MasterPasswordEncryption.class.getName());
+ private static final String ENCRYPTION_ALGORITHM = "PBEWithSHA1AndDESede"; // NOI18N
+ private static SecretKeyFactory KEY_FACTORY;
+ private static AlgorithmParameterSpec PARAM_SPEC;
+
+ private Cipher encrypt, decrypt;
+ private boolean unlocked;
+ private Callable encryptionChanging;
+ private char[] newMasterPassword;
+
+ public boolean enabled() {
+ try {
+ KEY_FACTORY = SecretKeyFactory.getInstance(ENCRYPTION_ALGORITHM);
+ encrypt = Cipher.getInstance(ENCRYPTION_ALGORITHM);
+ decrypt = Cipher.getInstance(ENCRYPTION_ALGORITHM);
+ Preferences prefs = NbPreferences.forModule(MasterPasswordEncryption.class);
+ Utils.goMinusR(prefs);
+ String saltKey = "salt"; // NOI18N
+ byte[] salt = prefs.getByteArray(saltKey, null);
+ if (salt == null) {
+ salt = UUID.randomUUID().toString().getBytes();
+ prefs.putByteArray(saltKey, salt);
+ }
+ PARAM_SPEC = new PBEParameterSpec(salt, 20);
+ return true;
+ } catch (Exception x) {
+ LOG.log(Level.INFO, "Cannot initialize security using " + ENCRYPTION_ALGORITHM, x);
+ return false;
+ }
+ }
+
+ public String id() {
+ return "general"; // NOI18N
+ }
+
+ public byte[] encrypt(char[] cleartext) throws Exception {
+ if (!unlockIfNecessary()) {
+ throw new Exception("cannot unlock");
+ }
+ try {
+ return doEncrypt(cleartext);
+ } catch (Exception x) {
+ unlocked = false; // reset
+ throw x;
+ }
+ }
+
+ public char[] decrypt(byte[] ciphertext) throws Exception {
+ AtomicBoolean callEncryptionChanging = new AtomicBoolean();
+ if (!unlockIfNecessary(callEncryptionChanging)) {
+ throw new Exception("cannot unlock");
+ }
+ try {
+ return doDecrypt(ciphertext);
+ } catch (Exception x) {
+ unlocked = false; // reset
+ throw x;
+ } finally {
+ if (callEncryptionChanging.get()) {
+ try {
+ encryptionChanging.call();
+ } catch (Exception x) {
+ LOG.log(Level.FINE, "failed to change encryption", x);
+ }
+ }
+ }
+ }
+
+ private boolean unlockIfNecessary() {
+ AtomicBoolean callEncryptionChanging = new AtomicBoolean();
+ boolean result = unlockIfNecessary(callEncryptionChanging);
+ if (callEncryptionChanging.get()) {
+ try {
+ encryptionChanging.call();
+ } catch (Exception x) {
+ LOG.log(Level.FINE, "failed to change encryption", x);
+ }
+ }
+ return result;
+ }
+ private boolean unlockIfNecessary(AtomicBoolean callEncryptionChanging) {
+ if (unlocked) {
+ return true;
+ }
+ char[][] passwords = new MasterPasswordPanel().display();
+ if (passwords == null) {
+ LOG.fine("cancelled master password dialog");
+ return false;
+ }
+ try {
+ unlock(passwords[0]);
+ Arrays.fill(passwords[0], '\0');
+ if (passwords.length == 2) {
+ newMasterPassword = passwords[1];
+ LOG.fine("will set new master password");
+ callEncryptionChanging.set(true);
+ }
+ return true;
+ } catch (Exception x) {
+ LOG.log(Level.FINE, "failed to initialize ciphers", x);
+ return false;
+ }
+ }
+
+ void unlock(char[] masterPassword) throws Exception {
+ LOG.fine("switching to new master password");
+ KeySpec keySpec = new PBEKeySpec(masterPassword);
+ Key key = KEY_FACTORY.generateSecret(keySpec);
+ encrypt.init(Cipher.ENCRYPT_MODE, key, PARAM_SPEC);
+ decrypt.init(Cipher.DECRYPT_MODE, key, PARAM_SPEC);
+ unlocked = true;
+ }
+
+ byte[] doEncrypt(char[] cleartext) throws Exception {
+ assert unlocked;
+ byte[] cleartextB = Utils.chars2Bytes(cleartext);
+ byte[] result = encrypt.doFinal(cleartextB);
+ Arrays.fill(cleartextB, (byte) 0);
+ return result;
+ }
+
+ char[] doDecrypt(byte[] ciphertext) throws Exception {
+ assert unlocked;
+ byte[] result = decrypt.doFinal(ciphertext);
+ char[] cleartext = Utils.bytes2Chars(result);
+ Arrays.fill(result, (byte) 0);
+ return cleartext;
+ }
+
+ public boolean decryptionFailed() {
+ unlocked = false;
+ return unlockIfNecessary();
+ }
+
+ public void encryptionChangingCallback(Callable callback) {
+ encryptionChanging = callback;
+ }
+
+ public void encryptionChanged() {
+ assert newMasterPassword != null;
+ LOG.fine("encryption changed");
+ try {
+ unlock(newMasterPassword);
+ } catch (Exception x) {
+ LOG.log(Level.FINE, "failed to initialize ciphers", x);
+ }
+ Arrays.fill(newMasterPassword, '\0');
+ newMasterPassword = null;
+ }
+
+}
--- a/keyring/src/org/netbeans/modules/keyring/fallback/MasterPasswordPanel.form
+++ a/keyring/src/org/netbeans/modules/keyring/fallback/MasterPasswordPanel.form
@@ -0,0 +1,119 @@
+
+
+
--- a/keyring/src/org/netbeans/modules/keyring/fallback/MasterPasswordPanel.java
+++ a/keyring/src/org/netbeans/modules/keyring/fallback/MasterPasswordPanel.java
@@ -0,0 +1,202 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.keyring.fallback;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Arrays;
+import javax.swing.JButton;
+import javax.swing.JPanel;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import org.openide.DialogDisplayer;
+import org.openide.NotificationLineSupport;
+import org.openide.NotifyDescriptor;
+
+class MasterPasswordPanel extends JPanel {
+
+ public MasterPasswordPanel() {
+ initComponents();
+ }
+
+ /**
+ * Shows this dialog.
+ * @return master password, and if selected, new master password; or null if cancelled
+ */
+ public char[][] display() {
+ // XXX I18N
+ final JButton ok = new JButton("OK");
+ ok.setDefaultCapable(true);
+ NotifyDescriptor d = new NotifyDescriptor(this, "Enter Master Password", NotifyDescriptor.OK_CANCEL_OPTION, NotifyDescriptor.PLAIN_MESSAGE,
+ new Object[] {ok, NotifyDescriptor.CANCEL_OPTION}, ok);
+ final NotificationLineSupport notification = d.createNotificationLineSupport();
+ final Runnable update = new Runnable() {
+ public void run() {
+ if (masterPasswordField.getPassword().length == 0) {
+ notification.setInformationMessage("Enter password");
+ ok.setEnabled(false);
+ return;
+ }
+ boolean changing = setNewBox.isSelected();
+ newLabel1.setEnabled(changing);
+ newField1.setEnabled(changing);
+ newLabel2.setEnabled(changing);
+ newField2.setEnabled(changing);
+ if (changing) {
+ if (newField1.getPassword().length == 0) {
+ notification.setInformationMessage("Enter new password");
+ ok.setEnabled(false);
+ return;
+ }
+ if (!Arrays.equals(newField1.getPassword(), newField2.getPassword())) {
+ notification.setInformationMessage("New passwords do not match");
+ ok.setEnabled(false);
+ return;
+ }
+ }
+ notification.clearMessages();
+ ok.setEnabled(true);
+ }
+ };
+ DocumentListener listener = new DocumentListener() {
+ public void insertUpdate(DocumentEvent e) {
+ update.run();
+ }
+ public void removeUpdate(DocumentEvent e) {
+ update.run();
+ }
+ public void changedUpdate(DocumentEvent e) {}
+ };
+ update.run();
+ masterPasswordField.getDocument().addDocumentListener(listener);
+ newField1.getDocument().addDocumentListener(listener);
+ newField2.getDocument().addDocumentListener(listener);
+ setNewBox.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ update.run();
+ }
+ });
+ if (DialogDisplayer.getDefault().notify(d) != ok) {
+ return null;
+ }
+ char[] masterPassword = masterPasswordField.getPassword();
+ return setNewBox.isSelected() ? new char[][] {masterPassword, newField1.getPassword()} : new char[][] {masterPassword};
+ }
+
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ masterPasswordLabel = new javax.swing.JLabel();
+ masterPasswordField = new javax.swing.JPasswordField();
+ setNewBox = new javax.swing.JCheckBox();
+ newLabel1 = new javax.swing.JLabel();
+ newField1 = new javax.swing.JPasswordField();
+ newLabel2 = new javax.swing.JLabel();
+ newField2 = new javax.swing.JPasswordField();
+
+ masterPasswordLabel.setLabelFor(masterPasswordField);
+ org.openide.awt.Mnemonics.setLocalizedText(masterPasswordLabel, org.openide.util.NbBundle.getMessage(MasterPasswordPanel.class, "MasterPasswordPanel.masterPasswordLabel.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(setNewBox, org.openide.util.NbBundle.getMessage(MasterPasswordPanel.class, "MasterPasswordPanel.setNewBox.text")); // NOI18N
+
+ newLabel1.setLabelFor(newField1);
+ org.openide.awt.Mnemonics.setLocalizedText(newLabel1, org.openide.util.NbBundle.getMessage(MasterPasswordPanel.class, "MasterPasswordPanel.newLabel1.text")); // NOI18N
+ newLabel1.setEnabled(false);
+
+ newField1.setEnabled(false);
+
+ newLabel2.setLabelFor(newField2);
+ org.openide.awt.Mnemonics.setLocalizedText(newLabel2, org.openide.util.NbBundle.getMessage(MasterPasswordPanel.class, "MasterPasswordPanel.newLabel2.text")); // NOI18N
+ newLabel2.setEnabled(false);
+
+ newField2.setEnabled(false);
+
+ org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+ .add(layout.createSequentialGroup()
+ .addContainerGap()
+ .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+ .add(layout.createSequentialGroup()
+ .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+ .add(newLabel1)
+ .add(newLabel2)
+ .add(masterPasswordLabel))
+ .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+ .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+ .add(masterPasswordField, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 224, Short.MAX_VALUE)
+ .add(newField2, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 224, Short.MAX_VALUE)
+ .add(newField1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 224, Short.MAX_VALUE)))
+ .add(setNewBox))
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+ .add(layout.createSequentialGroup()
+ .addContainerGap()
+ .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
+ .add(masterPasswordLabel)
+ .add(masterPasswordField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
+ .add(18, 18, 18)
+ .add(setNewBox)
+ .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+ .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
+ .add(newLabel1)
+ .add(newField1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+ .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
+ .add(newLabel2)
+ .add(newField2, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
+ .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+ }// //GEN-END:initComponents
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JPasswordField masterPasswordField;
+ private javax.swing.JLabel masterPasswordLabel;
+ private javax.swing.JPasswordField newField1;
+ private javax.swing.JPasswordField newField2;
+ private javax.swing.JLabel newLabel1;
+ private javax.swing.JLabel newLabel2;
+ private javax.swing.JCheckBox setNewBox;
+ // End of variables declaration//GEN-END:variables
+
+}
--- a/keyring/src/org/netbeans/modules/keyring/gnome/GnomeKeyringLibrary.java
+++ a/keyring/src/org/netbeans/modules/keyring/gnome/GnomeKeyringLibrary.java
@@ -0,0 +1,80 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.keyring.gnome;
+
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+import com.sun.jna.Structure;
+
+/**
+ * @see gnome-keyring API Reference
+ */
+public interface GnomeKeyringLibrary extends Library {
+
+ GnomeKeyringLibrary LIBRARY = (GnomeKeyringLibrary) Native.loadLibrary("gnome-keyring", GnomeKeyringLibrary.class);
+
+ boolean gnome_keyring_is_available();
+
+ int gnome_keyring_store_password_sync(GnomeKeyringPasswordSchema schema,
+ String keyring,
+ String display_name,
+ String password,
+ String... attrs);
+
+ int gnome_keyring_find_password_sync(GnomeKeyringPasswordSchema schema,
+ String[] password,
+ String... attrs);
+
+ int gnome_keyring_delete_password_sync(GnomeKeyringPasswordSchema schema,
+ String... attrs);
+
+ void g_set_application_name(String name);
+
+ class GnomeKeyringPasswordSchema extends Structure {
+ public int item_type;
+ public GnomeKeyringPasswordSchemaAttribute[] attributes = new GnomeKeyringPasswordSchemaAttribute[32];
+ }
+
+ class GnomeKeyringPasswordSchemaAttribute extends Structure {
+ public String name;
+ public int type;
+ }
+
+}
--- a/keyring/src/org/netbeans/modules/keyring/gnome/GnomeProvider.java
+++ a/keyring/src/org/netbeans/modules/keyring/gnome/GnomeProvider.java
@@ -0,0 +1,131 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.keyring.gnome;
+
+import java.text.MessageFormat;
+import java.util.MissingResourceException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import static org.netbeans.modules.keyring.gnome.GnomeKeyringLibrary.*;
+import org.netbeans.spi.keyring.KeyringProvider;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+@ServiceProvider(service=KeyringProvider.class, position=100)
+public class GnomeProvider implements KeyringProvider {
+
+ private static final Logger LOG = Logger.getLogger(GnomeProvider.class.getName());
+ private static final String KEY = "key"; // NOI18N
+
+ private static GnomeKeyringPasswordSchema SCHEMA;
+
+ public boolean enabled() {
+ if (Boolean.getBoolean("netbeans.keyring.no.native")) {
+ LOG.fine("native keyring integration disabled");
+ return false;
+ }
+ if (System.getenv("GNOME_KEYRING_PID") == null) { // NOI18N
+ // XXX is this going to be set on all Gnome platforms?
+ LOG.fine("GNOME_KEYRING_PID not set");
+ return false;
+ }
+ String appName;
+ try {
+ appName = MessageFormat.format(
+ NbBundle.getBundle("org.netbeans.core.windows.view.ui.Bundle").getString("CTL_MainWindow_Title_No_Project"),
+ System.getProperty("netbeans.buildnumber"));
+ } catch (MissingResourceException x) {
+ appName = "NetBeans"; // NOI18N
+ }
+ try {
+ // Need to do this somewhere, or we get warnings on console.
+ // Also used by confirmation dialogs to give the app access to the login keyring.
+ LIBRARY.g_set_application_name(appName);
+ if (!LIBRARY.gnome_keyring_is_available()) {
+ return false;
+ }
+ SCHEMA = new GnomeKeyringPasswordSchema();
+ SCHEMA.item_type = 0; // GNOME_KEYRING_ITEM_GENERIC_SECRET
+ SCHEMA.attributes[0] = new GnomeKeyringPasswordSchemaAttribute();
+ SCHEMA.attributes[0].name = KEY;
+ SCHEMA.attributes[0].type = 0; // GNOME_KEYRING_ATTRIBUTE_TYPE_STRING
+ SCHEMA.attributes[1] = null;
+ return true;
+ } catch (Throwable t) {
+ LOG.log(Level.FINE, null, t);
+ return false;
+ }
+ }
+
+ public char[] read(String key) {
+ // XXX try to use the char[] directly; not sure how to do this with JNA
+ String[] password = {null};
+ error(GnomeKeyringLibrary.LIBRARY.gnome_keyring_find_password_sync(SCHEMA, password, KEY, key));
+ return password[0] != null ? password[0].toCharArray() : null;
+ }
+
+ public void save(String key, char[] password, String description) {
+ error(GnomeKeyringLibrary.LIBRARY.gnome_keyring_store_password_sync(
+ SCHEMA, null, description != null ? description : key, new String(password), KEY, key));
+ }
+
+ public void delete(String key) {
+ error(GnomeKeyringLibrary.LIBRARY.gnome_keyring_delete_password_sync(SCHEMA, KEY, key));
+ }
+
+ private static String[] ERRORS = {
+ "OK", // NOI18N
+ "DENIED", // NOI18N
+ "NO_KEYRING_DAEMON", // NOI18N
+ "ALREADY_UNLOCKED", // NOI18N
+ "NO_SUCH_KEYRING", // NOI18N
+ "BAD_ARGUMENTS", // NOI18N
+ "IO_ERROR", // NOI18N
+ "CANCELLED", // NOI18N
+ "KEYRING_ALREADY_EXISTS", // NOI18N
+ "NO_MATCH", // NOI18N
+ };
+ private static void error(int code) {
+ if (code != 0 && code != 9) {
+ LOG.warning("gnome-keyring error: " + ERRORS[code]);
+ }
+ }
+
+}
--- a/keyring/src/org/netbeans/modules/keyring/mac/MacProvider.java
+++ a/keyring/src/org/netbeans/modules/keyring/mac/MacProvider.java
@@ -0,0 +1,119 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.keyring.mac;
+
+import com.sun.jna.Pointer;
+import java.io.UnsupportedEncodingException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.spi.keyring.KeyringProvider;
+import org.openide.util.Utilities;
+import org.openide.util.lookup.ServiceProvider;
+
+@ServiceProvider(service=KeyringProvider.class, position=200)
+public class MacProvider implements KeyringProvider {
+
+ private static final Logger LOG = Logger.getLogger(MacProvider.class.getName());
+
+ public boolean enabled() {
+ if (Boolean.getBoolean("netbeans.keyring.no.native")) {
+ LOG.fine("native keyring integration disabled");
+ return false;
+ }
+ return Utilities.isMac();
+ }
+
+ public char[] read(String key) {
+ try {
+ byte[] serviceName = key.getBytes("UTF-8");
+ byte[] accountName = "NetBeans".getBytes("UTF-8");
+ int[] dataLength = new int[1];
+ Pointer[] data = new Pointer[1];
+ error("find", SecurityLibrary.LIBRARY.SecKeychainFindGenericPassword(null, serviceName.length, serviceName,
+ accountName.length, accountName, dataLength, data, null));
+ if (data[0] == null) {
+ return null;
+ }
+ byte[] value = data[0].getByteArray(0, dataLength[0]); // XXX ought to call SecKeychainItemFreeContent
+ return new String(value, "UTF-8").toCharArray();
+ } catch (UnsupportedEncodingException x) {
+ LOG.log(Level.WARNING, null, x);
+ return null;
+ }
+ }
+
+ public void save(String key, char[] password, String description) {
+ delete(key); // XXX supposed to use SecKeychainItemModifyContent instead, but this seems like too much work
+ try {
+ byte[] serviceName = key.getBytes("UTF-8");
+ byte[] accountName = "NetBeans".getBytes("UTF-8");
+ // Keychain Access seems to expect UTF-8, so do not use Utils.chars2Bytes:
+ byte[] data = new String(password).getBytes("UTF-8");
+ error("save", SecurityLibrary.LIBRARY.SecKeychainAddGenericPassword(null, serviceName.length, serviceName,
+ accountName.length, accountName, data.length, data, null));
+ } catch (UnsupportedEncodingException x) {
+ LOG.log(Level.WARNING, null, x);
+ }
+ // XXX use description somehow... better to use SecItemAdd with kSecAttrDescription
+ }
+
+ public void delete(String key) {
+ try {
+ byte[] serviceName = key.getBytes("UTF-8");
+ byte[] accountName = "NetBeans".getBytes("UTF-8");
+ Pointer[] itemRef = new Pointer[1];
+ error("find (for delete)", SecurityLibrary.LIBRARY.SecKeychainFindGenericPassword(null, serviceName.length, serviceName,
+ accountName.length, accountName, null, null, itemRef));
+ if (itemRef[0] != null) {
+ error("delete", SecurityLibrary.LIBRARY.SecKeychainItemDelete(itemRef[0]));
+ }
+ } catch (UnsupportedEncodingException x) {
+ LOG.log(Level.WARNING, null, x);
+ }
+ }
+
+ private static void error(String msg, int code) {
+ if (code != 0 && code != /* errSecItemNotFound, always returned from find it seems */-25300) {
+ // XXX translate, but SecCopyErrorMessageString returns weird CFStringRef
+ LOG.warning(msg + ": " + code);
+ }
+ }
+
+}
--- a/keyring/src/org/netbeans/modules/keyring/mac/SecurityLibrary.java
+++ a/keyring/src/org/netbeans/modules/keyring/mac/SecurityLibrary.java
@@ -0,0 +1,79 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.keyring.mac;
+
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+import com.sun.jna.Pointer;
+
+/**
+ * @see Security Framework Reference
+ */
+public interface SecurityLibrary extends Library {
+
+ SecurityLibrary LIBRARY = (SecurityLibrary) Native.loadLibrary("Security", SecurityLibrary.class);
+
+ int SecKeychainAddGenericPassword(
+ Pointer keychain,
+ int serviceNameLength,
+ byte[] serviceName,
+ int accountNameLength,
+ byte[] accountName,
+ int passwordLength,
+ byte[] passwordData,
+ Pointer itemRef
+ );
+
+ int SecKeychainFindGenericPassword(
+ Pointer keychainOrArray,
+ int serviceNameLength,
+ byte[] serviceName,
+ int accountNameLength,
+ byte[] accountName,
+ int[] passwordLength,
+ Pointer[] passwordData,
+ Pointer[] itemRef
+ );
+
+ int SecKeychainItemDelete(
+ Pointer itemRef
+ );
+
+}
--- a/keyring/src/org/netbeans/modules/keyring/spi/EncryptionProvider.java
+++ a/keyring/src/org/netbeans/modules/keyring/spi/EncryptionProvider.java
@@ -0,0 +1,116 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.keyring.spi;
+
+import java.util.concurrent.Callable;
+import org.netbeans.spi.keyring.KeyringProvider;
+
+/**
+ * A weaker version of {@link KeyringProvider} which can only encrypt passwords securely.
+ * Rather than managing the complete storage of the keyring, a NetBeans-specific keyring
+ * is used, but this provider can encrypt the sensitive contents.
+ * The encryption is assumed to be symmetric but the encryption key should be secured.
+ * If the NetBeans {@link KeyringProvider} is used, the first encryption provider to be found
+ * in global lookup which claims to be enabled will be used;
+ * a standard implementation exists (at position 1000) which uses a simple master password.
+ */
+public interface EncryptionProvider {
+
+ /**
+ * Check whether this provider can be used in the current JVM session.
+ * If integrating a native library, this should attempt to load it.
+ * This method will be called at most once per JVM session,
+ * prior to any other methods in this interface being called.
+ * @return true if this provider should be used, false if not
+ */
+ boolean enabled();
+
+ /**
+ * Define a unique ID for this encryption provider, so that if the same userdir
+ * is reused on machines of different architecture the encrypted passwords will not conflict.
+ * @return an arbitrary ID specific to the algorithm
+ */
+ String id();
+
+ /**
+ * Encrypt a password or other sensitive data so that only the current user can decrypt it.
+ * @param cleartext some data (may be nulled out after this call)
+ * @return encrypted data
+ * @throws Exception if anything goes wrong
+ */
+ byte[] encrypt(char[] cleartext) throws Exception;
+
+ /**
+ * Decrypt a password or other sensitive data.
+ * @param ciphertext encrypted data
+ * @return cleartext (may be nulled out after this call)
+ * @throws Exception if anything goes wrong
+ */
+ char[] decrypt(byte[] ciphertext) throws Exception;
+
+ /**
+ * Called if {@link #decrypt} produced incorrect results on a sample key.
+ * The provider can react by prompting again for a master password, for example.
+ *
Implementations which do not support dynamic changes to the encryption
+ * key or method should return false from this method.
+ * @return true if an attempt was made to correct the encryption, false if nothing has changed
+ */
+ boolean decryptionFailed();
+
+ /**
+ * Offers a callback in case the encryption needs to change.
+ * For example, this may be employed if the user asks to change a master password.
+ * During the callback, the provider will be asked to decrypt existing secrets
+ * using the old encryption key; then {@link #encryptionChanged}
+ * will be called; finally the secrets will be reencrypted using the new encryption key.
+ *
Implementations which do not support dynamic changes to the encryption
+ * key or method may ignore this method.
+ * @param callback a callback which the provider may store and later call
+ */
+ void encryptionChangingCallback(Callable callback);
+
+ /**
+ * See {@link #encryptionChangingCallback} for description.
+ *
Implementations which do not support dynamic changes to the encryption
+ * key or method may ignore this method.
+ */
+ void encryptionChanged();
+
+}
--- a/keyring/src/org/netbeans/modules/keyring/win32/Win32Protect.java
+++ a/keyring/src/org/netbeans/modules/keyring/win32/Win32Protect.java
@@ -0,0 +1,165 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.keyring.win32;
+
+import com.sun.jna.Memory;
+import com.sun.jna.Native;
+import com.sun.jna.Pointer;
+import com.sun.jna.Structure;
+import com.sun.jna.WString;
+import com.sun.jna.win32.StdCallLibrary;
+import java.util.Arrays;
+import java.util.concurrent.Callable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.modules.keyring.Utils;
+import org.netbeans.modules.keyring.spi.EncryptionProvider;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ * Data protection utility for Microsoft Windows.
+ * XXX org.tmatesoft.svn.core.internal.util.jna.SVNWinCrypt is a possibly more robust implementation
+ * (though it seems to set CRYPTPROTECT_UI_FORBIDDEN which we do not necessarily want).
+ */
+@ServiceProvider(service=EncryptionProvider.class, position=100)
+public class Win32Protect implements EncryptionProvider {
+
+ private static final Logger LOG = Logger.getLogger(Win32Protect.class.getName());
+
+ public boolean enabled() {
+ if (Boolean.getBoolean("netbeans.keyring.no.native")) {
+ LOG.fine("native keyring integration disabled");
+ return false;
+ }
+ try {
+ if (CryptLib.INSTANCE == null) {
+ LOG.fine("loadLibrary -> null");
+ return false;
+ }
+ return true;
+ } catch (Throwable t) {
+ LOG.log(Level.FINE, null, t);
+ return false;
+ }
+ }
+
+ public String id() {
+ return "win32"; // NOI18N
+ }
+
+ public byte[] encrypt(char[] cleartext) throws Exception {
+ byte[] cleartextB = Utils.chars2Bytes(cleartext);
+ CryptIntegerBlob input = new CryptIntegerBlob();
+ input.store(cleartextB);
+ Arrays.fill(cleartextB, (byte) 0);
+ CryptIntegerBlob output = new CryptIntegerBlob();
+ if (!CryptLib.INSTANCE.CryptProtectData(input, null, null, null, null, 0, output)) {
+ throw new Exception("CryptProtectData failed");
+ }
+ input.zero();
+ return output.load();
+ }
+
+ public char[] decrypt(byte[] ciphertext) throws Exception {
+ CryptIntegerBlob input = new CryptIntegerBlob();
+ input.store(ciphertext);
+ CryptIntegerBlob output = new CryptIntegerBlob();
+ if (!CryptLib.INSTANCE.CryptUnprotectData(input, null, null, null, null, 0, output)) {
+ throw new Exception("CryptUnprotectData failed");
+ }
+ byte[] result = output.load();
+ // XXX gives CCE because not a Memory: output.zero();
+ char[] cleartext = Utils.bytes2Chars(result);
+ Arrays.fill(result, (byte) 0);
+ return cleartext;
+ }
+
+ public boolean decryptionFailed() {
+ return false; // not much to do about it
+ }
+
+ public void encryptionChangingCallback(Callable callback) {}
+
+ public void encryptionChanged() {
+ assert false;
+ }
+
+ public interface CryptLib extends StdCallLibrary {
+ CryptLib INSTANCE = (CryptLib) Native.loadLibrary("Crypt32", CryptLib.class); // NOI18N
+ /** @see Reference */
+ boolean CryptProtectData(
+ CryptIntegerBlob pDataIn,
+ WString szDataDescr,
+ CryptIntegerBlob pOptionalEntropy,
+ Pointer pvReserved,
+ Pointer pPromptStruct,
+ int dwFlags,
+ CryptIntegerBlob pDataOut
+ )/* throws LastErrorException*/;
+ /** @see Reference */
+ boolean CryptUnprotectData(
+ CryptIntegerBlob pDataIn,
+ WString[] ppszDataDescr,
+ CryptIntegerBlob pOptionalEntropy,
+ Pointer pvReserved,
+ Pointer pPromptStruct,
+ int dwFlags,
+ CryptIntegerBlob pDataOut
+ )/* throws LastErrorException*/;
+ }
+
+ public static class CryptIntegerBlob extends Structure {
+ public int cbData;
+ public /*byte[]*/Pointer pbData;
+ byte[] load() {
+ return pbData.getByteArray(0, cbData);
+ // XXX how to free pbData? [Kernel32]LocalFree?
+ }
+ void store(byte[] data) {
+ cbData = data.length;
+ pbData = new Memory(data.length);
+ pbData.write(0, data, 0, cbData);
+ }
+ void zero() {
+ ((Memory) pbData).clear();
+ }
+ }
+
+}
--- a/keyring/src/org/netbeans/spi/keyring/KeyringProvider.java
+++ a/keyring/src/org/netbeans/spi/keyring/KeyringProvider.java
@@ -0,0 +1,88 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.spi.keyring;
+
+/**
+ * Provider for a keyring.
+ * Should be registered in global lookup.
+ * Providers will be searched in the order in which they are encountered
+ * until one which is {@link #enabled} is found.
+ * There is a default platform-independent implementation at position 1000
+ * which should always be enabled.
+ *
All SPI calls are made from one thread at a time, so providers need not be synchronized.
+ */
+public interface KeyringProvider {
+
+ /**
+ * Check whether this provider can be used in the current JVM session.
+ * If integrating a native keyring, this should attempt to load related
+ * libraries and check whether they can be found.
+ * This method will be called at most once per JVM session,
+ * prior to any other methods in this interface being called.
+ * @return true if this provider should be used, false if not
+ */
+ boolean enabled();
+
+ /**
+ * Read a key from the ring.
+ * @param key the identifier of the key
+ * @return its value if found (elements may be later nulled out), else null if not present
+ */
+ char[] read(String key);
+
+ /**
+ * Save a key to the ring.
+ * If it could not be saved, do nothing.
+ * If the key already existed, overwrite the password.
+ * @param key a key identifier
+ * @param password the password or other sensitive information associated with the key
+ * (elements will be later nulled out)
+ * @param description a user-visible description of the key (may be null)
+ */
+ void save(String key, char[] password, String description);
+
+ /**
+ * Delete a key from the ring.
+ * If the key was not in the ring to begin with, do nothing.
+ * @param key a key identifier
+ */
+ void delete(String key);
+
+}
--- a/keyring/test/unit/src/org/netbeans/modules/keyring/KeyringProviderTestBase.java
+++ a/keyring/test/unit/src/org/netbeans/modules/keyring/KeyringProviderTestBase.java
@@ -0,0 +1,78 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.keyring;
+
+import java.util.UUID;
+import org.netbeans.junit.NbTestCase;
+import org.netbeans.spi.keyring.KeyringProvider;
+
+public abstract class KeyringProviderTestBase extends NbTestCase {
+
+ protected KeyringProviderTestBase(String n) {
+ super(n);
+ }
+
+ protected abstract KeyringProvider createProvider();
+
+ public void testStorage() throws Exception {
+ KeyringProvider p = createProvider();
+ if (!p.enabled()) {
+ System.err.println(p + "disabled on " + System.getProperty("os.name") + ", skipping");
+ return;
+ }
+ doTestStorage(p, "something", "secret stuff " + UUID.randomUUID(), null);
+ doTestStorage(p, "more", "secret stuff", "a description here");
+ doTestStorage(p, "klíč", "hezky česky", "můj heslo");
+ doTestStorage(p, "klā′vē ər", "ॐ", "κρυπτός");
+ }
+ private void doTestStorage(KeyringProvider p, String key, String password, String description) throws Exception {
+ key = "KeyringProviderTestBase." + key; // avoid interfering with anything real
+ assertEquals(null, p.read(key));
+ try {
+ p.save(key, password.toCharArray(), description);
+ char[] loaded = p.read(key);
+ assertEquals(password, loaded != null ? new String(loaded) : null);
+ } finally {
+ p.delete(key);
+ assertEquals(null, p.read(key));
+ }
+ }
+
+}
--- a/keyring/test/unit/src/org/netbeans/modules/keyring/fallback/MasterPasswordEncryptionTest.java
+++ a/keyring/test/unit/src/org/netbeans/modules/keyring/fallback/MasterPasswordEncryptionTest.java
@@ -0,0 +1,78 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.keyring.fallback;
+
+import org.netbeans.junit.NbTestCase;
+
+public class MasterPasswordEncryptionTest extends NbTestCase {
+
+ public MasterPasswordEncryptionTest(String n) {
+ super(n);
+ }
+
+ public void testEncryption() throws Exception {
+ doTestEncryption("Top Secret!", "my password");
+ doTestEncryption("some extra secret pass phrase", "something pretty long here for whatever reason...");
+ // Only ASCII is apparently supported for master passwords:
+ doTestEncryption("muj heslo", "hezky česky");
+ doTestEncryption("muj heslo", "ॐ");
+ }
+ private void doTestEncryption(String masterPassword, String password) throws Exception {
+ MasterPasswordEncryption p = new MasterPasswordEncryption();
+ assertTrue(p.enabled());
+ p.unlock(masterPassword.toCharArray());
+ assertEquals(password, new String(p.decrypt(p.encrypt(password.toCharArray()))));
+ }
+
+ public void testWrongPassword() throws Exception {
+ MasterPasswordEncryption p = new MasterPasswordEncryption();
+ assertTrue(p.enabled());
+ p.unlock("first password".toCharArray());
+ byte[] ciphertext = p.encrypt("secret".toCharArray());
+ p.unlock("second password".toCharArray());
+ try {
+ p.decrypt(ciphertext);
+ fail("should not be able to decrypt with incorrect password");
+ } catch (Exception x) {
+ // expected: "BadPaddingException: Given final block not properly padded"
+ }
+ }
+
+}
--- a/keyring/test/unit/src/org/netbeans/modules/keyring/gnome/GnomeProviderTest.java
+++ a/keyring/test/unit/src/org/netbeans/modules/keyring/gnome/GnomeProviderTest.java
@@ -0,0 +1,55 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.keyring.gnome;
+
+import org.netbeans.modules.keyring.KeyringProviderTestBase;
+import org.netbeans.spi.keyring.KeyringProvider;
+
+public class GnomeProviderTest extends KeyringProviderTestBase {
+
+ public GnomeProviderTest(String n) {
+ super(n);
+ }
+
+ protected KeyringProvider createProvider() {
+ return new GnomeProvider();
+ }
+
+}
--- a/keyring/test/unit/src/org/netbeans/modules/keyring/mac/MacProviderTest.java
+++ a/keyring/test/unit/src/org/netbeans/modules/keyring/mac/MacProviderTest.java
@@ -0,0 +1,55 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.keyring.mac;
+
+import org.netbeans.modules.keyring.KeyringProviderTestBase;
+import org.netbeans.spi.keyring.KeyringProvider;
+
+public class MacProviderTest extends KeyringProviderTestBase {
+
+ public MacProviderTest(String n) {
+ super(n);
+ }
+
+ protected KeyringProvider createProvider() {
+ return new MacProvider();
+ }
+
+}
--- a/keyring/test/unit/src/org/netbeans/modules/keyring/win32/Win32ProtectTest.java
+++ a/keyring/test/unit/src/org/netbeans/modules/keyring/win32/Win32ProtectTest.java
@@ -0,0 +1,67 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.keyring.win32;
+
+import org.netbeans.junit.NbTestCase;
+
+public class Win32ProtectTest extends NbTestCase {
+
+ public Win32ProtectTest(String n) {
+ super(n);
+ }
+
+ public void testEncryption() throws Exception {
+ Win32Protect p = new Win32Protect();
+ if (!p.enabled()) {
+ System.err.println("Skipping Win32ProtectTest on " + System.getProperty("os.name"));
+ return;
+ }
+ doTestEncryption(p, "my password");
+ doTestEncryption(p, "something pretty long here for whatever reason...");
+ doTestEncryption(p, "hezky česky");
+ doTestEncryption(p, "ॐ");
+ }
+ private void doTestEncryption(Win32Protect p, String password) throws Exception {
+ byte[] ciphertext = p.encrypt(password.toCharArray());
+ //System.err.println(password + " -> " + Arrays.toString(ciphertext));
+ assertEquals(password, new String(p.decrypt(ciphertext)));
+ }
+
+}
--- a/libs.jna/nbproject/project.xml
+++ a/libs.jna/nbproject/project.xml
@@ -53,6 +53,7 @@
org.netbeans.core.nativeaccessorg.netbeans.modules.dlight.nativeexecutionorg.netbeans.modules.extexecution.destroy
+ org.netbeans.modules.keyringorg.netbeans.modules.maven.killerorg.netbeans.modules.python.qshellcom.sun.jna
--- a/nbbuild/cluster.properties
+++ a/nbbuild/cluster.properties
@@ -207,6 +207,7 @@
editor.mimelookup.impl,\
favorites,\
javahelp,\
+ keyring,\
libs.jna,\
libs.jsr223,\
libs.junit4,\
--- a/o.n.core/nbproject/project.xml
+++ a/o.n.core/nbproject/project.xml
@@ -74,6 +74,14 @@