1 13 14 package se.anatom.ejbca.protocol; 15 16 import java.io.ByteArrayInputStream ; 17 import java.io.ByteArrayOutputStream ; 18 import java.io.FileInputStream ; 19 import java.io.IOException ; 20 import java.io.InputStream ; 21 import java.io.OutputStream ; 22 import java.net.HttpURLConnection ; 23 import java.net.URL ; 24 import java.net.URLConnection ; 25 import java.rmi.RemoteException ; 26 import java.security.GeneralSecurityException ; 27 import java.security.KeyPair ; 28 import java.security.KeyPairGenerator ; 29 import java.security.KeyStore ; 30 import java.security.cert.Certificate ; 31 import java.security.cert.X509Certificate ; 32 import java.security.interfaces.RSAPrivateKey ; 33 import java.util.Collection ; 34 import java.util.Enumeration ; 35 import java.util.Hashtable ; 36 import java.util.Iterator ; 37 38 import javax.ejb.DuplicateKeyException ; 39 import javax.naming.Context ; 40 import javax.naming.NamingException ; 41 import javax.net.ssl.HostnameVerifier; 42 import javax.net.ssl.HttpsURLConnection; 43 import javax.net.ssl.KeyManagerFactory; 44 import javax.net.ssl.SSLContext; 45 import javax.net.ssl.SSLSession; 46 import javax.net.ssl.SSLSocketFactory; 47 import javax.net.ssl.TrustManagerFactory; 48 49 import junit.framework.TestCase; 50 51 import org.apache.log4j.Logger; 52 import org.bouncycastle.asn1.ASN1InputStream; 53 import org.bouncycastle.asn1.ASN1OctetString; 54 import org.bouncycastle.asn1.DEROctetString; 55 import org.bouncycastle.asn1.x509.X509Extension; 56 import org.bouncycastle.asn1.x509.X509Extensions; 57 import org.bouncycastle.ocsp.BasicOCSPResp; 58 import org.bouncycastle.ocsp.CertificateID; 59 import org.bouncycastle.ocsp.OCSPException; 60 import org.bouncycastle.ocsp.OCSPReq; 61 import org.bouncycastle.ocsp.OCSPReqGenerator; 62 import org.bouncycastle.ocsp.OCSPResp; 63 import org.bouncycastle.ocsp.RevokedStatus; 64 import org.bouncycastle.ocsp.SingleResp; 65 import org.ejbca.core.ejb.ca.caadmin.ICAAdminSessionHome; 66 import org.ejbca.core.ejb.ca.caadmin.ICAAdminSessionRemote; 67 import org.ejbca.core.ejb.ca.sign.ISignSessionHome; 68 import org.ejbca.core.ejb.ca.sign.ISignSessionRemote; 69 import org.ejbca.core.ejb.ca.store.CertificateDataPK; 70 import org.ejbca.core.ejb.ca.store.ICertificateStoreSessionHome; 71 import org.ejbca.core.ejb.ca.store.ICertificateStoreSessionRemote; 72 import org.ejbca.core.ejb.ra.IUserAdminSessionHome; 73 import org.ejbca.core.ejb.ra.IUserAdminSessionRemote; 74 import org.ejbca.core.model.SecConst; 75 import org.ejbca.core.model.ca.caadmin.CAInfo; 76 import org.ejbca.core.model.ca.crl.RevokedCertInfo; 77 import org.ejbca.core.model.log.Admin; 78 import org.ejbca.core.model.ra.UserDataConstants; 79 import org.ejbca.core.protocol.ocsp.FnrFromUnidExtension; 80 import org.ejbca.util.CertTools; 81 import org.ejbca.util.KeyTools; 82 83 105 public class ProtocolLookupServerHttpTest extends TestCase { 106 private static Logger log = Logger.getLogger(ProtocolLookupServerHttpTest.class); 107 108 private String httpReqPath; 109 private final String resourceOcsp; 110 111 112 private static Context ctx; 113 private static ISignSessionHome home; 114 private static ISignSessionRemote remote; 115 protected ICertificateStoreSessionHome storehome; 116 private static IUserAdminSessionRemote usersession; 117 protected static int caid = 0; 118 protected static Admin admin; 119 private static X509Certificate cacert = null; 120 private static X509Certificate ocspTestCert = null; 121 private static KeyPair keys = null; 122 123 public ProtocolLookupServerHttpTest(String name) throws Exception { 124 this(name,"https://127.0.0.1:8443/ejbca", "publicweb/status/ocsp"); 125 } 126 127 public ProtocolLookupServerHttpTest(String name, String reqP, String res) throws Exception { 128 super(name); 129 httpReqPath = reqP; 130 resourceOcsp = res; 131 132 CertTools.installBCProvider(); 134 135 admin = new Admin(Admin.TYPE_BATCHCOMMANDLINE_USER); 136 137 ctx = getInitialContext(); 138 Object obj = ctx.lookup("CAAdminSession"); 139 ICAAdminSessionHome cahome = (ICAAdminSessionHome) javax.rmi.PortableRemoteObject.narrow(obj, ICAAdminSessionHome.class); 140 ICAAdminSessionRemote casession = cahome.create(); 141 setCAID(casession); 142 CAInfo cainfo = casession.getCAInfo(admin, caid); 143 Collection certs = cainfo.getCertificateChain(); 144 if (certs.size() > 0) { 145 Iterator certiter = certs.iterator(); 146 cacert = (X509Certificate ) certiter.next(); 147 } else { 148 log.error("NO CACERT for caid " + caid); 149 } 150 obj = ctx.lookup("RSASignSession"); 151 home = (ISignSessionHome) javax.rmi.PortableRemoteObject.narrow(obj, ISignSessionHome.class); 152 remote = home.create(); 153 Object obj2 = ctx.lookup("CertificateStoreSession"); 154 storehome = (ICertificateStoreSessionHome) javax.rmi.PortableRemoteObject.narrow(obj2, ICertificateStoreSessionHome.class); 155 obj = ctx.lookup("UserAdminSession"); 156 IUserAdminSessionHome userhome = (IUserAdminSessionHome) javax.rmi.PortableRemoteObject.narrow(obj, IUserAdminSessionHome.class); 157 usersession = userhome.create(); 158 159 keys = genKeys(); 160 161 } 162 163 protected void setCAID(ICAAdminSessionRemote casession) throws RemoteException { 164 Collection caids = casession.getAvailableCAs(admin); 165 Iterator iter = caids.iterator(); 166 if (iter.hasNext()) { 167 caid = ((Integer ) iter.next()).intValue(); 168 } else { 169 assertTrue("No active CA! Must have at least one active CA to run tests!", false); 170 } 171 } 172 protected void setUp() throws Exception { 173 log.debug(">setUp()"); 174 175 log.debug("<setUp()"); 176 } 177 178 protected void tearDown() throws Exception { 179 } 180 181 private Context getInitialContext() throws NamingException { 182 log.debug(">getInitialContext"); 183 Context ctx = new javax.naming.InitialContext (); 184 log.debug("<getInitialContext"); 185 return ctx; 186 } 187 188 195 private static KeyPair genKeys() throws Exception { 196 KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA", "BC"); 197 keygen.initialize(512); 198 log.debug("Generating keys, please wait..."); 199 KeyPair rsaKeys = keygen.generateKeyPair(); 200 log.debug("Generated " + rsaKeys.getPrivate().getAlgorithm() + " keys with length" + 201 ((RSAPrivateKey ) rsaKeys.getPrivate()).getModulus().bitLength()); 202 return rsaKeys; 203 } 205 208 public void test01OcspGoodWithFnr() throws Exception { 209 210 boolean userExists = false; 212 try { 213 usersession.addUser(admin,"unidtest","foo123","C=SE,O=AnaTom,surname=Jansson,serialNumber=123456789,CN=UNIDTest",null,"unidtest@anatom.se",false,SecConst.EMPTY_ENDENTITYPROFILE,SecConst.CERTPROFILE_FIXED_ENDUSER,SecConst.USER_ENDUSER,SecConst.TOKEN_SOFT_PEM,0,caid); 214 log.debug("created user: unidtest, foo123, C=SE, O=AnaTom,surname=Jansson,serialNumber=123456789, CN=UNIDTest"); 215 } catch (RemoteException re) { 216 if (re.detail instanceof DuplicateKeyException ) { 217 userExists = true; 218 } 219 } catch (DuplicateKeyException dke) { 220 userExists = true; 221 } 222 223 if (userExists) { 224 log.debug("User unidtest already exists."); 225 usersession.changeUser(admin, "unidtest", "foo123", "C=SE,O=AnaTom,surname=Jansson,serialNumber=123456789,CN=UNIDTest",null,"unidtest@anatom.se",false, SecConst.EMPTY_ENDENTITYPROFILE,SecConst.CERTPROFILE_FIXED_ENDUSER,SecConst.USER_ENDUSER,SecConst.TOKEN_SOFT_PEM,0,UserDataConstants.STATUS_NEW, caid); 226 log.debug("Reset status to NEW"); 227 } 228 230 ocspTestCert = (X509Certificate ) remote.createCertificate(admin, "unidtest", "foo123", keys.getPublic()); 232 assertNotNull("Misslyckades skapa cert", ocspTestCert); 233 234 OCSPReqGenerator gen = new OCSPReqGenerator(); 236 gen.addRequest(new CertificateID(CertificateID.HASH_SHA1, cacert, ocspTestCert.getSerialNumber())); 237 Hashtable exts = new Hashtable (); 238 X509Extension ext = new X509Extension(false, new DEROctetString(new FnrFromUnidExtension("123456789"))); 239 exts.put(FnrFromUnidExtension.FnrFromUnidOid, ext); 240 gen.setRequestExtensions(new X509Extensions(exts)); 241 OCSPReq req = gen.generate(); 242 243 BasicOCSPResp brep = sendOCSPPost(req.getEncoded(), true); 245 assertEquals(getFnr(brep), "654321"); 246 SingleResp[] singleResps = brep.getResponses(); 247 assertEquals("No of SingResps should be 1.", singleResps.length, 1); 248 SingleResp singleResp = singleResps[0]; 249 250 CertificateID certId = singleResp.getCertID(); 251 assertEquals("Serno in response does not match serno in request.", certId.getSerialNumber(), ocspTestCert.getSerialNumber()); 252 Object status = singleResp.getCertStatus(); 253 assertEquals("Status is not null (good)", status, null); 254 } 255 256 259 public void test02OcspBadWithFnr() throws Exception { 260 CertificateDataPK pk = new CertificateDataPK(); 261 pk.fingerprint = CertTools.getFingerprintAsString(ocspTestCert); 262 ICertificateStoreSessionRemote store = storehome.create(); 263 store.revokeCertificate(admin, ocspTestCert,null,RevokedCertInfo.REVOKATION_REASON_KEYCOMPROMISE); 264 265 OCSPReqGenerator gen = new OCSPReqGenerator(); 267 gen.addRequest(new CertificateID(CertificateID.HASH_SHA1, cacert, ocspTestCert.getSerialNumber())); 268 Hashtable exts = new Hashtable (); 269 X509Extension ext = new X509Extension(false, new DEROctetString(new FnrFromUnidExtension("123456789"))); 270 exts.put(FnrFromUnidExtension.FnrFromUnidOid, ext); 271 gen.setRequestExtensions(new X509Extensions(exts)); 272 OCSPReq req = gen.generate(); 273 274 BasicOCSPResp brep = sendOCSPPost(req.getEncoded(), true); 276 assertEquals(getFnr(brep), null); 278 SingleResp[] singleResps = brep.getResponses(); 279 assertEquals("No of SingResps should be 1.", singleResps.length, 1); 280 SingleResp singleResp = singleResps[0]; 281 282 CertificateID certId = singleResp.getCertID(); 283 assertEquals("Serno in response does not match serno in request.", certId.getSerialNumber(), ocspTestCert.getSerialNumber()); 284 Object status = singleResp.getCertStatus(); 285 assertTrue("Status is not RevokedStatus", status instanceof RevokedStatus); 286 RevokedStatus rev = (RevokedStatus) status; 287 assertTrue("Status does not have reason", rev.hasRevocationReason()); 288 int reason = rev.getRevocationReason(); 289 assertEquals("Wrong revocation reason", reason, RevokedCertInfo.REVOKATION_REASON_KEYCOMPROMISE); 290 } 291 292 295 public void test03OcspGoodWithNoFnr() throws Exception { 296 usersession.changeUser(admin, "unidtest", "foo123", "C=SE,O=AnaTom,surname=Jansson,serialNumber=12345678,CN=UNIDTest",null,"unidtest@anatom.se",false, SecConst.EMPTY_ENDENTITYPROFILE,SecConst.CERTPROFILE_FIXED_ENDUSER,SecConst.USER_ENDUSER,SecConst.TOKEN_SOFT_PEM,0,UserDataConstants.STATUS_NEW, caid); 298 log.debug("Reset status to NEW"); 299 ocspTestCert = (X509Certificate ) remote.createCertificate(admin, "unidtest", "foo123", keys.getPublic()); 301 assertNotNull("Misslyckades skapa cert", ocspTestCert); 302 303 OCSPReqGenerator gen = new OCSPReqGenerator(); 305 gen.addRequest(new CertificateID(CertificateID.HASH_SHA1, cacert, ocspTestCert.getSerialNumber())); 306 Hashtable exts = new Hashtable (); 307 X509Extension ext = new X509Extension(false, new DEROctetString(new FnrFromUnidExtension("123456789"))); 308 exts.put(FnrFromUnidExtension.FnrFromUnidOid, ext); 309 gen.setRequestExtensions(new X509Extensions(exts)); 310 OCSPReq req = gen.generate(); 311 312 BasicOCSPResp brep = sendOCSPPost(req.getEncoded(), true); 314 assertEquals(getFnr(brep), null); 315 SingleResp[] singleResps = brep.getResponses(); 316 assertEquals("No of SingResps should be 1.", singleResps.length, 1); 317 SingleResp singleResp = singleResps[0]; 318 319 CertificateID certId = singleResp.getCertID(); 320 assertEquals("Serno in response does not match serno in request.", certId.getSerialNumber(), ocspTestCert.getSerialNumber()); 321 Object status = singleResp.getCertStatus(); 322 assertEquals("Status is not null (good)", status, null); 323 } 324 325 328 public void test04OcspGoodNoSerialNo() throws Exception { 329 usersession.changeUser(admin, "unidtest", "foo123", "C=SE,O=AnaTom,surname=Jansson,CN=UNIDTest",null,"unidtest@anatom.se",false, SecConst.EMPTY_ENDENTITYPROFILE,SecConst.CERTPROFILE_FIXED_ENDUSER,SecConst.USER_ENDUSER,SecConst.TOKEN_SOFT_PEM,0,UserDataConstants.STATUS_NEW, caid); 331 log.debug("Reset status to NEW"); 332 ocspTestCert = (X509Certificate ) remote.createCertificate(admin, "unidtest", "foo123", keys.getPublic()); 334 assertNotNull("Misslyckades skapa cert", ocspTestCert); 335 336 OCSPReqGenerator gen = new OCSPReqGenerator(); 338 gen.addRequest(new CertificateID(CertificateID.HASH_SHA1, cacert, ocspTestCert.getSerialNumber())); 339 Hashtable exts = new Hashtable (); 340 X509Extension ext = new X509Extension(false, new DEROctetString(new FnrFromUnidExtension("123456789"))); 341 exts.put(FnrFromUnidExtension.FnrFromUnidOid, ext); 342 gen.setRequestExtensions(new X509Extensions(exts)); 343 OCSPReq req = gen.generate(); 344 345 BasicOCSPResp brep = sendOCSPPost(req.getEncoded(), true); 347 assertEquals(getFnr(brep), null); 348 SingleResp[] singleResps = brep.getResponses(); 349 assertEquals("No of SingResps should be 1.", singleResps.length, 1); 350 SingleResp singleResp = singleResps[0]; 351 352 CertificateID certId = singleResp.getCertID(); 353 assertEquals("Serno in response does not match serno in request.", certId.getSerialNumber(), ocspTestCert.getSerialNumber()); 354 Object status = singleResp.getCertStatus(); 355 assertEquals("Status is not null (good)", status, null); 356 } 357 358 362 public void test05HttpsNotAuthorized() throws Exception { 363 usersession.changeUser(admin, "unidtest", "foo123", "C=SE,O=AnaTom,surname=Jansson,serialNumber=123456789,CN=UNIDTest",null,"unidtest@anatom.se",false, SecConst.EMPTY_ENDENTITYPROFILE,SecConst.CERTPROFILE_FIXED_ENDUSER,SecConst.USER_ENDUSER,SecConst.TOKEN_SOFT_PEM,0,UserDataConstants.STATUS_NEW, caid); 365 log.debug("Reset status to NEW"); 366 ocspTestCert = (X509Certificate ) remote.createCertificate(admin, "unidtest", "foo123", keys.getPublic()); 368 assertNotNull("Misslyckades skapa cert", ocspTestCert); 369 370 OCSPReqGenerator gen = new OCSPReqGenerator(); 372 gen.addRequest(new CertificateID(CertificateID.HASH_SHA1, cacert, ocspTestCert.getSerialNumber())); 373 Hashtable exts = new Hashtable (); 374 X509Extension ext = new X509Extension(false, new DEROctetString(new FnrFromUnidExtension("123456789"))); 375 exts.put(FnrFromUnidExtension.FnrFromUnidOid, ext); 376 gen.setRequestExtensions(new X509Extensions(exts)); 377 OCSPReq req = gen.generate(); 378 379 BasicOCSPResp brep = sendOCSPPost(req.getEncoded(), false); 381 assertEquals(getFnr(brep), null); 382 SingleResp[] singleResps = brep.getResponses(); 383 assertEquals("No of SingResps should be 1.", singleResps.length, 1); 384 SingleResp singleResp = singleResps[0]; 385 386 CertificateID certId = singleResp.getCertID(); 387 assertEquals("Serno in response does not match serno in request.", certId.getSerialNumber(), ocspTestCert.getSerialNumber()); 388 Object status = singleResp.getCertStatus(); 389 assertEquals("Status is not null (good)", status, null); 390 } 391 392 396 public void test06HttpNotAuthorized() throws Exception { 397 httpReqPath = "http://127.0.0.1:8080/ejbca"; 400 usersession.changeUser(admin, "unidtest", "foo123", "C=SE,O=AnaTom,surname=Jansson,serialNumber=123456789,CN=UNIDTest",null,"unidtest@anatom.se",false, SecConst.EMPTY_ENDENTITYPROFILE,SecConst.CERTPROFILE_FIXED_ENDUSER,SecConst.USER_ENDUSER,SecConst.TOKEN_SOFT_PEM,0,UserDataConstants.STATUS_NEW, caid); 402 log.debug("Reset status to NEW"); 403 ocspTestCert = (X509Certificate ) remote.createCertificate(admin, "unidtest", "foo123", keys.getPublic()); 405 assertNotNull("Misslyckades skapa cert", ocspTestCert); 406 407 OCSPReqGenerator gen = new OCSPReqGenerator(); 409 gen.addRequest(new CertificateID(CertificateID.HASH_SHA1, cacert, ocspTestCert.getSerialNumber())); 410 Hashtable exts = new Hashtable (); 411 X509Extension ext = new X509Extension(false, new DEROctetString(new FnrFromUnidExtension("123456789"))); 412 exts.put(FnrFromUnidExtension.FnrFromUnidOid, ext); 413 gen.setRequestExtensions(new X509Extensions(exts)); 414 OCSPReq req = gen.generate(); 415 416 BasicOCSPResp brep = sendOCSPPost(req.getEncoded(), true); 418 assertEquals(getFnr(brep), null); 419 SingleResp[] singleResps = brep.getResponses(); 420 assertEquals("No of SingResps should be 1.", singleResps.length, 1); 421 SingleResp singleResp = singleResps[0]; 422 423 CertificateID certId = singleResp.getCertID(); 424 assertEquals("Serno in response does not match serno in request.", certId.getSerialNumber(), ocspTestCert.getSerialNumber()); 425 Object status = singleResp.getCertStatus(); 426 assertEquals("Status is not null (good)", status, null); 427 } 428 429 433 private BasicOCSPResp sendOCSPPost(byte[] ocspPackage, boolean trust) throws IOException , OCSPException, GeneralSecurityException { 434 URL url = new URL (httpReqPath + '/' + resourceOcsp); 436 HttpURLConnection con = (HttpURLConnection )getUrlConnection(url, trust); 438 con.setDoOutput(true); 440 con.setRequestMethod("POST"); 441 442 con.setRequestProperty("Content-Type", "application/ocsp-request"); 444 OutputStream os = con.getOutputStream(); 445 os.write(ocspPackage); 446 os.close(); 447 assertEquals("Response code", 200, con.getResponseCode()); 448 assertEquals("Content-Type", "application/ocsp-response", con.getContentType()); 449 ByteArrayOutputStream baos = new ByteArrayOutputStream (); 450 InputStream in = con.getInputStream(); 452 int b = in.read(); 453 while (b != -1) { 454 baos.write(b); 455 b = in.read(); 456 } 457 baos.flush(); 458 in.close(); 459 byte[] respBytes = baos.toByteArray(); 460 OCSPResp response = new OCSPResp(new ByteArrayInputStream (respBytes)); 461 assertEquals("Response status not zero.", response.getStatus(), 0); 462 BasicOCSPResp brep = (BasicOCSPResp) response.getResponseObject(); 463 X509Certificate [] chain = brep.getCerts("BC"); 464 boolean verify = brep.verify(chain[0].getPublicKey(), "BC"); 465 assertTrue("Response failed to verify.", verify); 466 return brep; 467 } 468 469 private String getFnr(BasicOCSPResp brep) throws IOException { 470 byte[] fnrrep = brep.getExtensionValue(FnrFromUnidExtension.FnrFromUnidOid.getId()); 471 if (fnrrep == null) { 472 return null; 473 } 474 assertNotNull(fnrrep); 475 ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream (fnrrep)); 476 ASN1OctetString octs = (ASN1OctetString) aIn.readObject(); 477 aIn = new ASN1InputStream(new ByteArrayInputStream (octs.getOctets())); 478 FnrFromUnidExtension fnrobj = FnrFromUnidExtension.getInstance(aIn.readObject()); 479 return fnrobj.getFnr(); 480 } 481 482 private SSLSocketFactory getSSLFactory(boolean trust) throws GeneralSecurityException , IOException { 483 log.debug(">getSSLFactory()"); 484 485 String trustp12 = "/lookup-kstrust.p12"; 486 if (!trust) trustp12 = "/lookup-ksnotrust.p12"; 487 char[] passphrase = "lookup".toCharArray(); 488 489 SSLContext ctx = SSLContext.getInstance("TLS"); 490 KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); 491 492 KeyStore ks = KeyStore.getInstance("PKCS12", "BC"); 494 ks.load(new FileInputStream (trustp12), passphrase); 495 kmf.init(ks, passphrase); 496 497 KeyStore trustks = KeyStore.getInstance("jks"); 499 trustks.load(null, "foo123".toCharArray()); 500 Enumeration en = ks.aliases(); 502 String alias = (String )en.nextElement(); 503 Certificate [] certs = KeyTools.getCertChain(ks, alias); 504 trustks.setCertificateEntry("trusted", certs[certs.length-1]); 505 TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); 506 tmf.init(trustks); 507 508 ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 509 510 log.debug("<getSSLFactory()"); 511 return ctx.getSocketFactory(); 512 } 513 514 522 private URLConnection getUrlConnection(URL url, boolean trust) throws IOException , GeneralSecurityException { 523 log.debug(">getUrlConnection( URL url )"); 524 log.debug(" - url=" + url); 525 URLConnection orgcon = url.openConnection(); 526 log.debug(orgcon.getClass()); 527 if (orgcon instanceof HttpsURLConnection) { 528 HttpsURLConnection con = (HttpsURLConnection) orgcon; 529 con.setHostnameVerifier(new SimpleVerifier()); 530 con.setSSLSocketFactory(getSSLFactory(trust)); 531 } else 532 log.debug("getUrlConnection(): Ingen HttpsUrlConnection!"); 533 log.debug("<getUrlConnection() --> " + orgcon); 534 return orgcon; 535 } 536 537 class SimpleVerifier implements HostnameVerifier { 538 public boolean verify(String hostname, SSLSession session) { 539 return true; 540 } 541 } 542 543 } 544 | Popular Tags |