/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * 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 Netscape Security Services for Java. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 2002 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ package org.mozilla.jss.tests; import org.mozilla.jss.CryptoManager; import org.mozilla.jss.ssl.*; import org.mozilla.jss.crypto.*; import org.mozilla.jss.crypto.KeyPairGenerator; import org.mozilla.jss.asn1.*; import org.mozilla.jss.pkix.primitive.*; import org.mozilla.jss.pkix.cert.*; import org.mozilla.jss.pkix.cert.Certificate; import org.mozilla.jss.util.PasswordCallback; import java.util.Calendar; import java.util.Date; import java.security.*; import java.security.PrivateKey; import java.net.InetAddress; import java.io.InputStream; import java.io.EOFException; public class SSLClientAuth implements Runnable { public static final SignatureAlgorithm sigAlg = SignatureAlgorithm.RSASignatureWithSHA1Digest; public static Certificate makeCert(String issuerName, String subjectName, int serialNumber, PrivateKey privKey, PublicKey pubKey, int rand, SEQUENCE extensions) throws Exception { AlgorithmIdentifier sigAlgID = new AlgorithmIdentifier( sigAlg.toOID()); Name issuer = new Name(); issuer.addCommonName(issuerName); issuer.addCountryName("US"); issuer.addOrganizationName("Mozilla"+rand); issuer.addOrganizationalUnitName("JSS Testing"); Name subject = new Name(); subject.addCommonName(subjectName); subject.addCountryName("US"); subject.addOrganizationName("Mozilla"+rand); subject.addOrganizationalUnitName("JSS Testing"); Calendar cal = Calendar.getInstance(); Date notBefore = cal.getTime(); cal.add(Calendar.YEAR, 1); Date notAfter = cal.getTime(); SubjectPublicKeyInfo.Template spkiTemp = new SubjectPublicKeyInfo.Template(); SubjectPublicKeyInfo spki = (SubjectPublicKeyInfo) ASN1Util.decode(spkiTemp, pubKey.getEncoded()); CertificateInfo info = new CertificateInfo( CertificateInfo.v3, new INTEGER(serialNumber), sigAlgID, issuer, notBefore, notAfter, subject, spki); if( extensions != null ) { info.setExtensions(extensions); } return new Certificate(info, privKey, sigAlg); } public static void main(String[] args) throws Exception { (new SSLClientAuth()).doIt(args); } private X509Certificate nssServerCert, nssClientCert; private String serverCertNick, clientCertNick; public void doIt(String[] args) throws Exception { CryptoManager.initialize(args[0]); CryptoManager cm = CryptoManager.getInstance(); CryptoToken tok = cm.getInternalKeyStorageToken(); PasswordCallback cb = new FilePasswordCallback(args[1]); tok.login(cb); SecureRandom rng= SecureRandom.getInstance("pkcs11prng", "Mozilla-JSS"); int rand = nextRandInt(rng); // generate CA cert KeyPairGenerator kpg = tok.getKeyPairGenerator(KeyPairAlgorithm.RSA); kpg.initialize(512); KeyPair caPair = kpg.genKeyPair(); SEQUENCE extensions = new SEQUENCE(); extensions.addElement(makeBasicConstraintsExtension()); Certificate caCert = makeCert("CACert", "CACert", 1, caPair.getPrivate(), caPair.getPublic(), rand, extensions); X509Certificate nssCaCert = cm.importUserCACertPackage( ASN1Util.encode(caCert), "cacertnick"+rand); InternalCertificate intern = (InternalCertificate)nssCaCert; intern.setSSLTrust( InternalCertificate.TRUSTED_CA | InternalCertificate.TRUSTED_CLIENT_CA | InternalCertificate.VALID_CA); // generate server cert kpg.initialize(512); KeyPair serverPair = kpg.genKeyPair(); Certificate serverCert = makeCert("CACert", "localhost", 2, caPair.getPrivate(), serverPair.getPublic(), rand, null); serverCertNick = "servercertnick"+rand; nssServerCert = cm.importCertPackage( ASN1Util.encode(serverCert), serverCertNick); // generate client auth cert kpg.initialize(512); KeyPair clientPair = kpg.genKeyPair(); Certificate clientCert = makeCert("CACert", "ClientCert", 3, caPair.getPrivate(), clientPair.getPublic(), rand, null); clientCertNick = "clientcertnick"+rand; nssClientCert = cm.importCertPackage( ASN1Util.encode(clientCert), clientCertNick); useNickname = false; testConnection(); useNickname = true; testConnection(); System.out.println("Exiting main()"); if( getSuccess() ) { System.exit(0); } else { System.exit(1); } } private boolean useNickname; private void testConnection() throws Exception { serverReady = false; // spawn server Thread server = new Thread(this); server.start(); // wait for server to open its socket synchronized(this) { while(!serverReady) { this.wait(); } } // connect to the server System.out.println("client about to connect"); SSLSocket sock = new SSLSocket("localhost", port); if( useNickname ) { sock.setClientCertNickname(clientCertNick); System.out.println("Client specified cert by nickname"); } else { sock.setClientCert(nssClientCert); System.out.println("Client specified cert directly"); } System.out.println("client connected"); sock.addHandshakeCompletedListener( new HandshakeListener("client",this)); // force the handshake sock.forceHandshake(); System.out.println("client forced handshake"); sock.close(); // wait for the server to finish server.join(); } public static class HandshakeListener implements SSLHandshakeCompletedListener { private String who; private SSLClientAuth boss; public HandshakeListener(String who, SSLClientAuth boss) { this.who = who; this.boss = boss; } public void handshakeCompleted(SSLHandshakeCompletedEvent event) { try { String mesg = who + " got a completed handshake "; SSLSecurityStatus status = event.getStatus(); if( status.isSecurityOn() ) { mesg += "(security is ON)"; } else { mesg += "(security is OFF)"; } System.out.println(mesg); } catch(Exception e) { e.printStackTrace(); boss.setFailure(); } } } public synchronized void setFailure() { success = false; } public synchronized boolean getSuccess() { return success; } private boolean success = true; public static int port = 29752; public boolean serverReady = false; /** * Server run method. */ public void run() { try { // We have to configure the server session ID cache before // creating any server sockets. SSLServerSocket.configServerSessionIDCache(10, 100, 100, null); // open the server socket and bind to the port System.out.println("Server about to create socket"); SSLServerSocket serverSock = new SSLServerSocket(port, 5, null, null, true); System.out.println("Server created socket"); serverSock.requireClientAuth(true, true); if( useNickname ) { serverSock.setServerCertNickname(serverCertNick); System.out.println("Server specified cert by nickname"); } else { serverSock.setServerCert(nssServerCert); System.out.println("Server specified cert directly"); } // tell the client we're ready synchronized(this) { serverReady = true; this.notify(); } // accept the connection System.out.println("Server about to accept"); SSLSocket sock = (SSLSocket) serverSock.accept(); System.out.println("Server accepted"); sock.addHandshakeCompletedListener( new HandshakeListener("server", this)); // try to read some bytes, to allow the handshake to go through InputStream is = sock.getInputStream(); try { System.out.println("Server about to read"); is.read(); System.out.println("Server read successful"); } catch(EOFException e) { System.out.println("Server got EOF"); } sock.close(); serverSock.close(); } catch(Exception e) { synchronized(this) { serverReady = true; setFailure(); this.notify(); } e.printStackTrace(); } System.out.println("Server exiting"); } static Extension makeBasicConstraintsExtension() throws Exception { SEQUENCE bc = new SEQUENCE(); bc.addElement( new BOOLEAN(true) ); // cA OBJECT_IDENTIFIER bcOID = new OBJECT_IDENTIFIER( new long[] {2, 5, 29, 19}); // from RFC 2459 OCTET_STRING enc = new OCTET_STRING(ASN1Util.encode(bc)); return new Extension(bcOID, true, enc); } static int nextRandInt(SecureRandom rand) throws Exception { byte[] bytes = new byte[4]; rand.nextBytes(bytes); return ((int)bytes[0])<<24 | ((int)bytes[1])<<16 | ((int)bytes[2])<<8 | ((int)bytes[3]); } }