From 3546cafb4942247edf298996186fcdfa32eb9954 Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Tue, 17 Dec 2013 08:33:18 +0100 Subject: First version for testing -> Exthex OAuth implementation --- .../gv/egovernment/moa/id/auth/oauth/CertTest.java | 86 ++++++++++ .../moa/id/auth/oauth/OAuth20ErrorsTests.java | 190 +++++++++++++++++++++ .../id/auth/oauth/OAuth20GoogleClientTestCase.java | 136 +++++++++++++++ .../moa/id/auth/oauth/OAuth20UtilTest.java | 48 ++++++ 4 files changed, 460 insertions(+) create mode 100644 id/server/idserverlib/src/test/java/test/at/gv/egovernment/moa/id/auth/oauth/CertTest.java create mode 100644 id/server/idserverlib/src/test/java/test/at/gv/egovernment/moa/id/auth/oauth/OAuth20ErrorsTests.java create mode 100644 id/server/idserverlib/src/test/java/test/at/gv/egovernment/moa/id/auth/oauth/OAuth20GoogleClientTestCase.java create mode 100644 id/server/idserverlib/src/test/java/test/at/gv/egovernment/moa/id/auth/oauth/OAuth20UtilTest.java (limited to 'id/server/idserverlib/src/test') diff --git a/id/server/idserverlib/src/test/java/test/at/gv/egovernment/moa/id/auth/oauth/CertTest.java b/id/server/idserverlib/src/test/java/test/at/gv/egovernment/moa/id/auth/oauth/CertTest.java new file mode 100644 index 000000000..6452d5ae6 --- /dev/null +++ b/id/server/idserverlib/src/test/java/test/at/gv/egovernment/moa/id/auth/oauth/CertTest.java @@ -0,0 +1,86 @@ +package test.at.gv.egovernment.moa.id.auth.oauth; + +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPrivateKey; + +import net.oauth.jsontoken.crypto.RsaSHA256Signer; +import net.oauth.jsontoken.crypto.RsaSHA256Verifier; + +import org.opensaml.xml.security.x509.BasicX509Credential; +import org.testng.annotations.Test; + +import at.gv.egovernment.moa.util.KeyStoreUtils; +import at.gv.egovernment.moa.util.StringUtils; +import eu.stork.vidp.messages.exception.SAMLException; + +public class CertTest { + + /** KeyStore Path */ + private String keyStorePath = "file:/D:/dev/work/exthex/workspace/OAuthTesting/resources/keys/test_keystore.jks"; + + /** KeyStore Password */ + private String keyStorePassword = "test12"; + + /** Specific Key Name as Credential */ + private String keyName = "1"; + + /** Key password */ + private String keyPassword = "test12"; + + + + + @Test(enabled = false) + public void loadCert() throws Exception { + + if (StringUtils.isEmpty(this.keyStorePath)) throw new SAMLException("No keyStorePath specified"); + + // KeyStorePassword optional + // if (StringUtils.isEmpty(this.keyStorePassword)) + // throw new SAMLException("No keyStorePassword specified"); + + if (StringUtils.isEmpty(this.keyName)) throw new SAMLException("No keyName specified"); + + // KeyStorePassword optional + // if (StringUtils.isEmpty(this.keyPassword)) + // throw new SAMLException("No keyPassword specified"); + + KeyStore ks = null; + try { + ks = KeyStoreUtils.loadKeyStore(this.keyStorePath, this.keyStorePassword); + } + catch (Exception e) { + e.printStackTrace(); + } + + // return new KeyStoreX509CredentialAdapter(ks, keyName, keyPwd.toCharArray()); + BasicX509Credential credential = null; + try { + java.security.cert.X509Certificate certificate = (X509Certificate) ks.getCertificate(this.keyName); + + PrivateKey privateKey = (PrivateKey) ks.getKey(this.keyName, this.keyPassword.toCharArray()); + credential = new BasicX509Credential(); + credential.setEntityCertificate(certificate); + credential.setPrivateKey(privateKey); + + System.out.println(privateKey); + + } + catch (Exception e) { + e.printStackTrace(); + + } + System.out.println(credential); + + String data = "someData"; + + RsaSHA256Signer signer = new RsaSHA256Signer("signer1", keyName, (RSAPrivateKey) credential.getPrivateKey()); + + byte[] signedData = signer.sign(data.getBytes()); + + RsaSHA256Verifier verifier = new RsaSHA256Verifier(credential.getPublicKey()); + verifier.verifySignature(data.getBytes(), signedData); + } +} diff --git a/id/server/idserverlib/src/test/java/test/at/gv/egovernment/moa/id/auth/oauth/OAuth20ErrorsTests.java b/id/server/idserverlib/src/test/java/test/at/gv/egovernment/moa/id/auth/oauth/OAuth20ErrorsTests.java new file mode 100644 index 000000000..64179d75a --- /dev/null +++ b/id/server/idserverlib/src/test/java/test/at/gv/egovernment/moa/id/auth/oauth/OAuth20ErrorsTests.java @@ -0,0 +1,190 @@ +package test.at.gv.egovernment.moa.id.auth.oauth; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import at.gv.egovernment.moa.id.protocols.oauth20.OAuth20Constants; +import at.gv.egovernment.moa.id.protocols.oauth20.OAuth20Util; + +import com.google.api.client.extensions.java6.auth.oauth2.VerificationCodeReceiver; +import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.jackson2.JacksonFactory; + +public class OAuth20ErrorsTests { + + final static Logger log = LoggerFactory.getLogger(OAuth20ErrorsTests.class); + + private static VerificationCodeReceiver receiver; + + // base uri + private static String OAUTH2_BASE_URI = "http://localhost:8080/moa-id-auth/dispatcher"; + // auth action + private static String OAUTH2_AUTH_URI = OAUTH2_BASE_URI + "?mod=id_oauth20&action=AUTH"; + // token action + private static String OAUTH2_TOKEN_URI = OAUTH2_BASE_URI + "?mod=id_oauth20&action=TOKEN"; + + // client id + private static String CLIENT_ID = "http://test"; + // client secret + private static String CLIENT_SECRET = "d435cf0a-3933-48f7-b142-339710c8f070"; + // OAuth 2.0 scopes + private static List SCOPES = Arrays.asList("testScope1", "testScope2"); + // state + private static String STATE = "testState"; + // code + private static String CODE = "code"; + // redirect uri + private static String REDIRECT_URI = "http://localhost:59542/Callback"; + + @BeforeMethod + public void beforeTest() throws Exception { + receiver = new LocalServerReceiver.Builder().setPort(59542).build(); + // REDIRECT_URI = receiver.getRedirectUri(); + // start + receiver.getRedirectUri(); + } + + @AfterMethod + public void afterTest() { + try { + receiver.stop(); + } + catch (IOException e) { + } + } + + private void checkParam(final String paramString, final String paramName) { + String[] help = paramString.split("="); + Assert.assertEquals(help[0], paramName); + Assert.assertTrue(StringUtils.isNotEmpty(help[1])); + } + + private void checkParams(final String queryString) { + // System.out.println("QueryString: " + queryString); + + System.out.println("Result url: " + queryString); + + String[] params = queryString.split("&"); + + this.checkParam(params[0], OAuth20Constants.PARAM_ERROR); + this.checkParam(params[1], OAuth20Constants.PARAM_ERROR_DESCRIPTION); + // this.checkParam(params[2], OAuth20Constants.PARAM_ERROR_URI); + // this.checkParam(params[3], OAuth20Constants.PARAM_STATE); + this.checkParam(params[2], OAuth20Constants.PARAM_STATE); + } + + class OAuthRequestParameters { + String redirectUri; + String clientId; + String responseType; + String scope; + String state; + String error; + + public OAuthRequestParameters(String redirectUri, String clientId, String responseType, String scope, String state, + String error) { + this.redirectUri = redirectUri; + this.clientId = clientId; + this.responseType = responseType; + this.scope = scope; + this.state = state; + this.error = error; + } + } + + @DataProvider(name = "parameter") + public Object[][] parameterProvider() { + // parameter is missing + // OAuthRequestParameters p0 = new OAuthRequestParameters(null, OA_URL, CLIENT_ID, CODE, + // "testScope1", null, + // "User authorization failed (invalid_request)"); + // OAuthRequestParameters p1 = new OAuthRequestParameters(REDIRECT_URI, CLIENT_ID, CODE, + // "testScope1", STATE, + // "User authorization failed (invalid_request)"); + OAuthRequestParameters p2 = new OAuthRequestParameters(REDIRECT_URI, null, CODE, "testScope1", STATE, + "User authorization failed (invalid_request)"); + OAuthRequestParameters p3 = new OAuthRequestParameters(REDIRECT_URI, CLIENT_ID, null, "testScope1", STATE, + "User authorization failed (invalid_request)"); + OAuthRequestParameters p4 = new OAuthRequestParameters(REDIRECT_URI, CLIENT_ID, CODE, null, STATE, null); + OAuthRequestParameters p5 = new OAuthRequestParameters(REDIRECT_URI, CLIENT_ID, CODE, "testScope1", null, + "User authorization failed (invalid_request)"); + + // wrong response type + OAuthRequestParameters p6 = new OAuthRequestParameters(REDIRECT_URI, CLIENT_ID, "WRONG_CODE", "testScope1", STATE, + "User authorization failed (unsupported_response_type)"); + // wrong client id + OAuthRequestParameters p7 = new OAuthRequestParameters(REDIRECT_URI, "wrongClient", CODE, "testScope1", STATE, + "User authorization failed (invalid_request)"); + // wrong redirect uri + // OAuthRequestParameters p9 = new OAuthRequestParameters("wrongURI", OA_URL, "wrongClient", + // CODE, "testScope1", STATE, + // "User authorization failed (access_denied)"); + + return new Object[][] { { p2 }, { p3 }, { p4 }, { p5 }, { p6 }, { p7 } }; + } + + @Test(dataProvider = "parameter", enabled = false) + public void testMissingParams(OAuthRequestParameters p) throws Exception { + StringBuilder url = new StringBuilder(); + url.append(OAUTH2_AUTH_URI); + + if (StringUtils.isNotEmpty(p.redirectUri)) OAuth20Util.addParameterToURL(url, "redirect_uri", p.redirectUri); + if (StringUtils.isNotEmpty(p.clientId)) OAuth20Util.addParameterToURL(url, "client_id", p.clientId); + if (StringUtils.isNotEmpty(p.responseType)) OAuth20Util.addParameterToURL(url, "response_type", p.responseType); + if (StringUtils.isNotEmpty(p.scope)) OAuth20Util.addParameterToURL(url, "scope", p.scope); + if (StringUtils.isNotEmpty(p.state)) OAuth20Util.addParameterToURL(url, "state", p.state); + + String finalUrl = url.toString(); + System.out.println("Calling: " + finalUrl); + + HttpClient client = new HttpClient(); + GetMethod get = new GetMethod(finalUrl); + int res = client.executeMethod(get); + Assert.assertEquals(res, HttpServletResponse.SC_OK); + + // assert + + if (p.error == null) { + Assert.assertFalse(get.getQueryString().contains("error")); + // receiver.waitForCode(); + } else { + // check if all error params are returned + this.checkParams(get.getQueryString()); + try { + receiver.waitForCode(); + Assert.assertTrue(false); + } + catch (Exception e) { + Assert.assertEquals(e.getMessage(), p.error); + } + } + } + + @Test(enabled = false) + public void testTokenErrorResponse() throws Exception { + HttpClient client = new HttpClient(); + GetMethod get = new GetMethod(OAUTH2_TOKEN_URI + "&client_id=" + CLIENT_ID + "&client_secret=" + CLIENT_SECRET + + "&code=test&grant_type=authorization_code"); + int res = client.executeMethod(get); + + System.out.println(res); + System.out.println(get.getResponseBodyAsString()); + } +} diff --git a/id/server/idserverlib/src/test/java/test/at/gv/egovernment/moa/id/auth/oauth/OAuth20GoogleClientTestCase.java b/id/server/idserverlib/src/test/java/test/at/gv/egovernment/moa/id/auth/oauth/OAuth20GoogleClientTestCase.java new file mode 100644 index 000000000..7cf2ac82b --- /dev/null +++ b/id/server/idserverlib/src/test/java/test/at/gv/egovernment/moa/id/auth/oauth/OAuth20GoogleClientTestCase.java @@ -0,0 +1,136 @@ +package test.at.gv.egovernment.moa.id.auth.oauth; + +import java.awt.Desktop; +import java.awt.Desktop.Action; +import java.io.IOException; +import java.math.BigInteger; +import java.net.URI; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.Test; + +import com.google.api.client.auth.oauth2.AuthorizationCodeFlow; +import com.google.api.client.auth.oauth2.AuthorizationCodeRequestUrl; +import com.google.api.client.auth.oauth2.BearerToken; +import com.google.api.client.auth.oauth2.ClientParametersAuthentication; +import com.google.api.client.auth.oauth2.TokenResponse; +import com.google.api.client.auth.openidconnect.IdToken; +import com.google.api.client.extensions.java6.auth.oauth2.VerificationCodeReceiver; +import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver; +import com.google.api.client.http.GenericUrl; +import com.google.api.client.http.HttpExecuteInterceptor; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.jackson2.JacksonFactory; + +public class OAuth20GoogleClientTestCase { + + final static Logger log = LoggerFactory.getLogger(OAuth20GoogleClientTestCase.class); + + // private static FileDataStoreFactory DATA_STORE_FACTORY; + + // Global instance of the HTTP transport. + private static HttpTransport HTTP_TRANSPORT = new NetHttpTransport(); + // Global instance of the JSON factory. + private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance(); + + private static String ISS = "https://localhost/moa-id-auth/"; + + // base uri + //private static String OAUTH2_BASE_URI = ISS + "dispatcher"; + // auth action + //private static String OAUTH2_AUTH_URI = OAUTH2_BASE_URI + "?mod=id_oauth20&action=AUTH"; + private static String OAUTH2_AUTH_URI = ISS + "oauth2/auth"; + + // token action + //private static String OAUTH2_TOKEN_URI = OAUTH2_BASE_URI + "?mod=id_oauth20&action=TOKEN"; + private static String OAUTH2_TOKEN_URI = ISS + "oauth2/token"; + + // client id + private static String CLIENT_ID = "http://test"; + // client secret + private static String CLIENT_SECRET = "d435cf0a-3933-48f7-b142-339710c8f070"; + // OAuth 2.0 scopes + private static final List SCOPES = Arrays.asList("profile", "eID", "eID_gov", "mandate"); + + // open browser for bku login + private void openURL(String url) { + Assert.assertNotNull(url); + System.out.println(url); + if (Desktop.isDesktopSupported()) { + Desktop desktop = Desktop.getDesktop(); + if (desktop.isSupported(Action.BROWSE)) { + try { + desktop.browse(URI.create(url)); + return; + } + catch (IOException e) { + // handled below + } + } + } + // Finally just ask user to open in their browser using copy-paste + + log.info("Please open the following URL in your browser:"); + log.info(url); + } + + private TokenResponse authorize() throws Exception { + // set up a receiver for the callback + VerificationCodeReceiver receiver = new LocalServerReceiver.Builder().setPort(59542).build(); + + // create AuthorizationCodeFlow + GenericUrl token_uri = new GenericUrl(OAUTH2_TOKEN_URI); + HttpExecuteInterceptor credentials = new ClientParametersAuthentication(CLIENT_ID, CLIENT_SECRET); + AuthorizationCodeFlow flow = new AuthorizationCodeFlow.Builder(BearerToken.queryParameterAccessMethod(), HTTP_TRANSPORT, + JSON_FACTORY, token_uri, credentials, CLIENT_ID, OAUTH2_AUTH_URI).setScopes(SCOPES).build(); + // .setDataStoreFactory(DATA_STORE_FACTORY) + + // create AuthorizationCodeRequestUrl + try { + String redirectUri = receiver.getRedirectUri(); + String state = new BigInteger(130, new SecureRandom()).toString(32); + AuthorizationCodeRequestUrl authorizationUrl = flow.newAuthorizationUrl().setRedirectUri(redirectUri).setState(state); + + // open in browser + this.openURL(authorizationUrl.build()); + + // receive authorization code and exchange it for an access token + String code = receiver.waitForCode(); + System.out.println(code); + TokenResponse response = flow.newTokenRequest(code).setRedirectUri(redirectUri).execute(); + return response; + } + finally { + // if anything fails, stop the receiver + receiver.stop(); + } + + } + + // eyJhbGciOiJSUzI1NiIsImtpZCI6IjEifQ.eyJpc3MiOiJodHRwczovL2xvY2FsaG9zdC9tb2EtaWQtYXV0aC8iLCJleHAiOi02MzE5MDMsInN1YiI6IncveThQY2pNTHBFTGZmUHRTSDNtbmd6M24rRVx1MDAzZCIsImJpcnRoZGF0ZSI6IjE5ODUtMDItMDEiLCJmYW1pbHlfbmFtZSI6IkhpZXNzIiwiZ2l2ZW5fbmFtZSI6Ik1pY2hhZWwiLCJpYXQiOi02MzIyMDN9.Z_jveITHlTtktPOOV3n_sMbg50YQ4YcOEcSUs_RJ-4FGedj1sVxk9gmlUQcBPfQaBrPgC6RoiPLTy8CKu2PBClEyv9c9HdzIGqBjWzaTSNASx_QL5bfG4EQ8VZmSEI9d0whzlaBgkUFNfhx-Q2ZVh-g8SJ-0JO0zFR18OSRNTxPTJ4PPl0APqn2H-98sU331_zQKiZxNOvl_6OG26VoIYwEuW5m_N5tsf4lLAlqYcdHR3iNTeu8AkAOvlEwv7Z3BeeOiP4u-OWuc6VusWBPxaI2NwmDIoorpyIxY-wEFb4CWICuyk61Wlq1SCNdl-f-ODwJBK3rlj0IMlYbAjKSB0g + private void verifyIdToken(TokenResponse response) throws Exception { + String id_token = (String) response.getUnknownKeys().get("id_token"); + log.info("going to parse id token: {}", id_token); + + IdToken idToken = IdToken.parse(JSON_FACTORY, id_token); + Assert.assertTrue(idToken.verifyIssuer(ISS)); + + log.info(idToken.getPayload().toPrettyString()); + } + + @Test(enabled = false) + public void testServerFlow() throws Exception { + TokenResponse response = this.authorize(); + log.info(response.toPrettyString()); + + this.verifyIdToken(response); + } + +} diff --git a/id/server/idserverlib/src/test/java/test/at/gv/egovernment/moa/id/auth/oauth/OAuth20UtilTest.java b/id/server/idserverlib/src/test/java/test/at/gv/egovernment/moa/id/auth/oauth/OAuth20UtilTest.java new file mode 100644 index 000000000..83b92dbad --- /dev/null +++ b/id/server/idserverlib/src/test/java/test/at/gv/egovernment/moa/id/auth/oauth/OAuth20UtilTest.java @@ -0,0 +1,48 @@ +package test.at.gv.egovernment.moa.id.auth.oauth; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import at.gv.egovernment.moa.id.protocols.oauth20.OAuth20Util; + +public class OAuth20UtilTest { + + @Test + public void validateURL() { + Assert.assertTrue(OAuth20Util.isUrl("file:/D:/dev/work/exthex/workspace/OAuthTesting/resources/keys/test_keystore.jks")); + Assert.assertTrue(OAuth20Util.isUrl("https://www.google.at/")); + Assert.assertTrue(OAuth20Util.isUrl("http://test")); + Assert.assertTrue(OAuth20Util.isUrl("http://localhost:59542/Callback")); + + + Assert.assertFalse(OAuth20Util.isUrl("http://")); + Assert.assertFalse(OAuth20Util.isUrl("123http://test")); + Assert.assertFalse(OAuth20Util.isUrl("test")); + } + + @Test + public void validateState() { + // check state for invalid characters (like < > & ; ... javascript ... to prevent xss) + + Assert.assertFalse(OAuth20Util.isValidStateValue("javascript")); + Assert.assertFalse(OAuth20Util.isValidStateValue("")); + Assert.assertFalse(OAuth20Util.isValidStateValue("Tasst")); + Assert.assertFalse(OAuth20Util.isValidStateValue("Tes&t")); + Assert.assertFalse(OAuth20Util.isValidStateValue("Tes;t")); + Assert.assertTrue(OAuth20Util.isValidStateValue("secure_state")); + } + + + @Test + public void testExp() { + Pattern urlPattern = Pattern.compile("/oauth2/auth\\?(.*)$", Pattern.CASE_INSENSITIVE); + Matcher matcher = urlPattern.matcher("https://localhost/moa-id-auth/oauth2/auth?client_id=http://test&redirect_uri=http://localhost:59542/Callback&response_type=code&scope=profile%20eID%20eID_gov%20mandate&state=7gfnabf112ogg9segnnrfpi83q"); + System.out.println(matcher.find()); + } + +} -- cgit v1.2.3