зеркало из https://github.com/mozilla/gecko-dev.git
496 строки
13 KiB
Java
496 строки
13 KiB
Java
/*
|
|
* The contents of this file are subject to the Mozilla Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is the Netscape Security Services for Java.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998-2000 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the
|
|
* terms of the GNU General Public License Version 2 or later (the
|
|
* "GPL"), in which case the provisions of the GPL are applicable
|
|
* instead of those above. If you wish to allow use of your
|
|
* version of this file only under the terms of the GPL and not to
|
|
* allow others to use your version of this file under the MPL,
|
|
* indicate your decision by deleting the provisions above and
|
|
* replace them with the notice and other provisions required by
|
|
* the GPL. If you do not delete the provisions above, a recipient
|
|
* may use your version of this file under either the MPL or the
|
|
* GPL.
|
|
*/
|
|
|
|
// This demonstrates the SSL client-side support
|
|
//
|
|
|
|
import org.mozilla.jss.ssl.*;
|
|
|
|
import java.io.*;
|
|
import java.util.*;
|
|
import java.net.*;
|
|
import org.mozilla.jss.*;
|
|
import org.mozilla.jss.crypto.AlreadyInitializedException;
|
|
|
|
/**
|
|
* Parameters supported by this socket test:
|
|
* SSLClient <host=hostname> <port=portnum> <cert=clientcertnickname>
|
|
*
|
|
*
|
|
*/
|
|
|
|
public class SSLClient implements
|
|
SSLCertificateApprovalCallback, SSLClientCertificateSelectionCallback
|
|
{
|
|
boolean handshakeEventHappened = false;
|
|
boolean doClientAuth = false;
|
|
PrintStream results;
|
|
|
|
Hashtable args; // command line arguments are store here
|
|
|
|
public static Hashtable initArgs() {
|
|
Hashtable args = new Hashtable();
|
|
|
|
args.put("hostname","localhost");
|
|
args.put("port","10443");
|
|
return args;
|
|
}
|
|
|
|
public String getArgument(String key) {
|
|
return (String)args.get(key);
|
|
}
|
|
|
|
|
|
|
|
public void run()
|
|
{
|
|
try {
|
|
SSLHandshakeCompletedListener listener = null;
|
|
|
|
SSLSocket s;
|
|
String hostname;
|
|
int port;
|
|
|
|
String msg = "GET /index.html";
|
|
|
|
String portstr = getArgument("port");
|
|
if (portstr == null) {
|
|
port = 443;
|
|
}
|
|
else {
|
|
port = Integer.valueOf(portstr).intValue();
|
|
}
|
|
|
|
/* the argument 'cert' specifies the nickname of
|
|
a client-auth-cert to use if asked by the server */
|
|
|
|
String nickname = getArgument("cert");
|
|
|
|
hostname = getArgument("hostname");
|
|
|
|
results.println("Connecting to " + hostname +
|
|
" on port " + port );
|
|
|
|
s = new SSLSocket(InetAddress.getByName(hostname),
|
|
port,
|
|
null, /* local addr */
|
|
0, /* local port */
|
|
true, /* stream */
|
|
this, /* approvalCallback */
|
|
this /* certSelectionCallback */
|
|
);
|
|
|
|
results.println("Connected.");
|
|
|
|
// If you pass null for the certSelectionCallback argument
|
|
// above, you can provide the name of the client-auth-
|
|
// cert ahead of time, by using the setClientCertNickname
|
|
// API
|
|
// select the cert for client auth
|
|
|
|
// s.setClientCertNickname("cert");
|
|
|
|
|
|
// Setup a handshake callback. This listener will get invoked
|
|
// When the SSL handshake is completed on this socket.
|
|
|
|
listener = new ClientHandshakeCB(this);
|
|
s.addHandshakeCompletedListener(listener);
|
|
|
|
OutputStream o = s.getOutputStream();
|
|
PrintOutputStreamWriter out = new PrintOutputStreamWriter(o);
|
|
|
|
results.println("Sending: " + msg + " to " + hostname +
|
|
", " + port );
|
|
|
|
// send HTTP GET message across SSL connection
|
|
out.println(msg + "\r");
|
|
|
|
InputStream in = s.getInputStream();
|
|
byte[] bytes = new byte[4096];
|
|
int totalBytes = 0;
|
|
int numReads = 0;
|
|
String lastBytes = null;
|
|
|
|
|
|
// now try to read data back from the SSL connection
|
|
try {
|
|
for(;;) {
|
|
results.println("Calling Read.");
|
|
int n = in.read(bytes, 0, bytes.length);
|
|
if(n == -1) {
|
|
results.println("EOF found.");
|
|
break;
|
|
}
|
|
|
|
if (n == 0) {
|
|
results.println("Zero bytes read?");
|
|
break;
|
|
}
|
|
numReads++;
|
|
|
|
if(totalBytes == 0) {
|
|
// don't print forever...
|
|
String data = new String(bytes, 0, 30, "8859_1");
|
|
results.println("Read " + n + " bytes of data");
|
|
results.println("First 30 bytes: " + escapeHTML(data));
|
|
}
|
|
|
|
totalBytes += n;
|
|
lastBytes = new String(bytes, n-31, 30, "8859_1");
|
|
}
|
|
} catch (IOException e) {
|
|
results.println("IOException while reading from pipe? "+
|
|
"Actually got "+ totalBytes + " bytes total");
|
|
e.printStackTrace(results);
|
|
results.println("");
|
|
throw e;
|
|
} finally {
|
|
results.println("Last 30 bytes: " + lastBytes);
|
|
results.println("Number of read() calls: " + numReads );
|
|
results.println("\nDiagnostics");
|
|
|
|
|
|
results.println("Total bytes read: " + totalBytes );
|
|
results.println("Security status of session:");
|
|
SSLSecurityStatus status = s.getStatus();
|
|
results.println(status.toString());
|
|
|
|
}
|
|
|
|
// Got here, so no exception thrown above.
|
|
// Try to clean up.
|
|
o.close();
|
|
o = null;
|
|
in.close();
|
|
in = null;
|
|
if (listener != null) {
|
|
s.removeHandshakeCompletedListener(listener);
|
|
listener = null;
|
|
}
|
|
s.close();
|
|
s = null;
|
|
|
|
} catch(Exception e) {
|
|
results.println("***** TEST FAILED *****");
|
|
e.printStackTrace(results);
|
|
}
|
|
|
|
results.println("END OF TEST");
|
|
}
|
|
|
|
/**
|
|
* given an input string, convert less-than, greater-than, and ampersand
|
|
* from raw characters to escaped characters
|
|
* (< becomes `&lt;', etc.)
|
|
*/
|
|
private String escapeHTML(String s)
|
|
{
|
|
StringBuffer result = new StringBuffer();
|
|
|
|
for(int i=0; i<s.length(); i++) {
|
|
char c = s.charAt(i);
|
|
switch(c) {
|
|
case '<': result.append("<"); break;
|
|
case '>': result.append(">"); break;
|
|
case '&': result.append("&"); break;
|
|
default: result.append(c); break;
|
|
}
|
|
}
|
|
|
|
return result.toString();
|
|
}
|
|
|
|
|
|
/* CONSTRUCTOR */
|
|
|
|
public SSLClient( PrintStream ps, String verStr, Hashtable myargs)
|
|
throws Exception
|
|
{
|
|
|
|
this.args = myargs;
|
|
this.results = ps;
|
|
int i;
|
|
|
|
String ocsp = (String)myargs.get("ocsp_on");
|
|
boolean ocsp_on = false;
|
|
if (ocsp != null) {
|
|
if (ocsp.equals("on") ) {
|
|
ocsp_on = true;
|
|
}
|
|
else if (ocsp.equals("off")) {
|
|
ocsp_on = false;
|
|
}
|
|
else {
|
|
throw new Exception("Error: invalid argument '"+
|
|
ocsp+ "' must be 'on' or 'off'");
|
|
}
|
|
}
|
|
|
|
|
|
try {
|
|
CryptoManager.InitializationValues vals =
|
|
new CryptoManager.InitializationValues
|
|
("secmod.db", "key3.db", "cert7.db");
|
|
vals.ocspCheckingEnabled = ocsp_on;
|
|
vals.ocspResponderURL = (String)myargs.get("ocsp_url");
|
|
vals.ocspResponderCertNickname = (String)myargs.get(
|
|
"ocsp_nickname");
|
|
CryptoManager.initialize(vals);
|
|
}
|
|
catch (KeyDatabaseException kdbe) {
|
|
System.out.println("Couldn't open the key database");
|
|
return;
|
|
}
|
|
catch (CertDatabaseException cdbe) {
|
|
System.out.println("Couldn't open the certificate database");
|
|
return;
|
|
}
|
|
catch (AlreadyInitializedException aie) {
|
|
System.out.println("CryptoManager already initialized???");
|
|
return;
|
|
}
|
|
|
|
/* enable all the SSL2 cipher suites */
|
|
for (i = SSLSocket.SSL2_RC4_128_WITH_MD5;
|
|
i <= SSLSocket.SSL2_DES_192_EDE3_CBC_WITH_MD5; ++i) {
|
|
SSLSocket.setCipherPreference( i, true);
|
|
}
|
|
|
|
/* enable all the SSL3 cipher suites */
|
|
for (i = 0; cipherSuites[i] != 0; ++i) {
|
|
SSLSocket.setCipherPreference( cipherSuites[i], true);
|
|
}
|
|
}
|
|
|
|
static final int cipherSuites[] = {
|
|
SSLSocket.SSL3_RSA_WITH_RC4_128_MD5,
|
|
SSLSocket.SSL3_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
SSLSocket.SSL3_RSA_WITH_DES_CBC_SHA,
|
|
SSLSocket.SSL3_RSA_EXPORT_WITH_RC4_40_MD5,
|
|
SSLSocket.SSL3_RSA_EXPORT_WITH_RC2_CBC_40_MD5,
|
|
SSLSocket.SSL3_RSA_WITH_NULL_MD5,
|
|
0
|
|
};
|
|
|
|
public static void main(String argv[])
|
|
{
|
|
|
|
Hashtable myargs = initArgs();
|
|
int i;
|
|
|
|
for (i=0;i<argv.length;i++) {
|
|
int index = argv[i].indexOf("=");
|
|
if (index == -1) {
|
|
exitError("Invalid Argument: "+argv[i]);
|
|
}
|
|
String key = argv[i].substring(0,index);
|
|
String value = argv[i].substring(index+1);
|
|
if (value== null) {
|
|
exitError("Invalid value for argument: "+argv[i]);
|
|
}
|
|
myargs.put(key,value);
|
|
}
|
|
|
|
try {
|
|
SSLClient x = new SSLClient(System.out, "Stand alone Ver 0.1",myargs);
|
|
x.run();
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public static void exitError(String x) {
|
|
System.err.println("Error: "+x);
|
|
System.exit(1);
|
|
}
|
|
|
|
|
|
/**
|
|
* SSLCertificateApprovalCallback.approve()
|
|
*
|
|
* This is a test implementation of the certificate approval callback which
|
|
* gets invoked when the server presents a certificate which is not
|
|
* trusted by the client
|
|
*/
|
|
|
|
public boolean approve(
|
|
org.mozilla.jss.crypto.X509Certificate servercert,
|
|
SSLCertificateApprovalCallback.ValidityStatus status) {
|
|
|
|
SSLCertificateApprovalCallback.ValidityItem item;
|
|
|
|
System.out.println("in TestCertApprovalCallback.approve()");
|
|
|
|
/* dump out server cert details */
|
|
|
|
System.out.println("Peer cert details: "+
|
|
"\n subject: "+servercert.getSubjectDN().toString()+
|
|
"\n issuer: "+servercert.getIssuerDN().toString()+
|
|
"\n serial: "+servercert.getSerialNumber().toString()
|
|
);
|
|
|
|
/* iterate through all the problems */
|
|
|
|
boolean trust_the_server_cert=false;
|
|
|
|
Enumeration errors = status.getReasons();
|
|
int i=0;
|
|
while (errors.hasMoreElements()) {
|
|
i++;
|
|
item = (SSLCertificateApprovalCallback.ValidityItem) errors.nextElement();
|
|
System.out.println("item "+i+
|
|
" reason="+item.getReason()+
|
|
" depth="+item.getDepth());
|
|
org.mozilla.jss.crypto.X509Certificate cert = item.getCert();
|
|
if (item.getReason() ==
|
|
SSLCertificateApprovalCallback.ValidityStatus.UNTRUSTED_ISSUER) {
|
|
trust_the_server_cert = true;
|
|
}
|
|
|
|
System.out.println(" cert details: "+
|
|
"\n subject: "+cert.getSubjectDN().toString()+
|
|
"\n issuer: "+cert.getIssuerDN().toString()+
|
|
"\n serial: "+cert.getSerialNumber().toString()
|
|
);
|
|
}
|
|
|
|
|
|
/* - importCertToPerm is in JSS 2.1 only
|
|
if (trust_the_server_cert) {
|
|
System.out.println("importing certificate.");
|
|
try {
|
|
InternalCertificate newcert =
|
|
org.mozilla.jss.CryptoManager.getInstance().
|
|
importCertToPerm(servercert,"testnick");
|
|
newcert.setSSLTrust(InternalCertificate.TRUSTED_PEER |
|
|
InternalCertificate.VALID_PEER);
|
|
} catch (Exception e) {
|
|
System.out.println("thrown exception: "+e);
|
|
}
|
|
}
|
|
*/
|
|
|
|
|
|
/* allow the connection to continue.
|
|
returning false here would abort the connection */
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
* This interface is what you should implement if you want to
|
|
* be able to decide whether or not you want to approve the peer's cert,
|
|
* instead of having NSS do that.
|
|
*/
|
|
|
|
/**
|
|
* this method will be called form the native callback code
|
|
* when a certificate is requested. You must return a String
|
|
* which is the nickname of the certificate you wish to present.
|
|
*
|
|
* @param nicknames A Vector of Strings. These strings are an
|
|
* aid to the user to select the correct nickname. This list is
|
|
* made from the list of all certs which are valid, match the
|
|
* CA's trusted by the server, and which you have the private
|
|
* key of. If nicknames.length is 0, you should present an
|
|
* error to the user saying 'you do not have any unexpired
|
|
* certificates'.
|
|
* @return You must return the nickname of the certificate you
|
|
* wish to use. You can return null if you do not wish to send
|
|
* a certificate.
|
|
*/
|
|
public String select(Vector nicknames) {
|
|
Enumeration e = nicknames.elements();
|
|
String s="",first=null;
|
|
|
|
System.out.println("in TestClientCertificateSelectionCallback.select() "+s);
|
|
while (e.hasMoreElements()) {
|
|
s = (String)e.nextElement();
|
|
if (first == null) {
|
|
first = s;
|
|
}
|
|
System.out.println(" "+s);
|
|
}
|
|
return first;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
class ClientHandshakeCB implements SSLHandshakeCompletedListener {
|
|
SSLClient sc;
|
|
|
|
ClientHandshakeCB(SSLClient sc) {
|
|
this.sc = sc;
|
|
}
|
|
|
|
public void handshakeCompleted(SSLHandshakeCompletedEvent event) {
|
|
sc.handshakeEventHappened = true;
|
|
sc.results.println("handshake happened");
|
|
}
|
|
}
|
|
|
|
class PrintOutputStreamWriter
|
|
extends java.io.OutputStreamWriter
|
|
{
|
|
|
|
public PrintOutputStreamWriter(OutputStream out)
|
|
{
|
|
super(out);
|
|
}
|
|
|
|
public void print(String x)
|
|
throws java.io.IOException
|
|
{
|
|
write(x, 0, x.length());
|
|
}
|
|
|
|
public void println(String x)
|
|
throws java.io.IOException
|
|
{
|
|
String line = x + "\n";
|
|
write(line, 0, line.length());
|
|
flush();
|
|
}
|
|
}
|
|
|
|
|