diff --git a/dpop/DPoP-client-app/.gitignore b/dpop/DPoP-client-app/.gitignore new file mode 100644 index 000000000..633a3d2e2 --- /dev/null +++ b/dpop/DPoP-client-app/.gitignore @@ -0,0 +1,5 @@ +target +*.iml +.idea +dpop.key +dpop.pub diff --git a/dpop/DPoP-client-app/README.md b/dpop/DPoP-client-app/README.md new file mode 100644 index 000000000..99c3d5f8d --- /dev/null +++ b/dpop/DPoP-client-app/README.md @@ -0,0 +1,73 @@ +# DPoP-client-app + This client application was originally developed by Darsa Mahendrarajah as Gradle application. This repository contains the Maven conversion of the same. + + ## How to setup : + 1. Clone /dpop/DPoP-client-app + 2. Build the project using ```mvn clean install``` + 3. Follow the below steps + +### Option 01: +1. Generate per request basis DPOPProofGenerator.java (This will generate per request +private and public keypair ) + - Navigate to /samples-is/dpop/DPoP-client-app folder. + - Run the below command. + + ```java -cp target/client-app-1.0-jar-with-dependencies.jar org.wso2.dpop.client.DPOPProofGenerator``` + - Enter the user inputs for below questions. + + ``` + Enter the public and private key pair type(EC or RSA): + EC + Enter the HTTP Method: + POST + Enter the HTTP Url: + https://localhost:9443/oauth2/token + + ``` +- Extract the dpop proof from the output. + + ``` + [Signed JWT] : eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiQ + y03b1ZVQ2Z2OUlRQmlWN0JlLVdNVVEtdF9ibmo1TzBlV0tuZkhnUUJaSSIsInkiOiJla2Rsci12dFJFMHFMdUpNYU5FUDR + vdXhKNVVHTEREdWphZUN6ejhJTkdjIn19.eyJodG0iOiJQT1NUIiwic3ViIjoic3ViIiwibmJmIjoxNjM1MTc2MDg2LCJp + c3MiOiJpc3N1ZXIiLCJodHUiOiJodHRwczpcL1wvbG9jYWxob3N0Ojk0NDNcL29hdXRoMlwvdG9rZW4iLCJpYXQiOjE2Mz + UxNzYwODYsImp0aSI6ImUzMTEzYzdmLTE0YTQtNGU2ZC1hY2VmLTZjZDQ5NWQ1ZWZjNCJ9.lgEjdP1Fv3OOAaFfM4CEREg + 46RGdbusLzb4409eCMabCwbN0dS6NdXsc6qxfR_TVhkKzIUKLSDmM4qzEIuw4LQ + ``` + + ### Option 02: + 1. Generate dpop proof by passing public key and private key file location. + - Generate public key pair and store it in a file.(GeneratePublicKeyPair.java) + 1. Navigate to /samples-is/dpop/DPoP-client-app folder and run the below command.Private key file (dpop.key) and Public key file will be generated(dpop.pub). + + ```java -cp target/client-app-1.0-jar-with-dependencies.jar org.wso2.dpop.client.GeneratePublicKeyPair``` + - Use the already created key pairs and load and generate the DPoP Proof(DPOPProofGeneratorFromPP.java) . + 1. Navigate to /samples-is/dpop/DPoP-client-app folder. + 2. Run the below command. + + ``` java -cp target/client-app-1.0-jar-with-dependencies.jar org.wso2.dpop.client.DPOPProofGeneratorFromPP``` + + 3. Enter the user inputs for the below questions. + + ``` + File path to private key: + dpop.key + File path to public key: + dpop.pub + Enter the public and private key pair type(EC or RSA): + EC + Enter the HTTP Method: + POST + Enter the HTTP Url: + https://localhost:9443/oauth2/token + ``` + 4. Extract the dpop proof from the output. + + ``` + [Signed JWT] : eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiQ + y03b1ZVQ2Z2OUlRQmlWN0JlLVdNVVEtdF9ibmo1TzBlV0tuZkhnUUJaSSIsInkiOiJla2Rsci12dFJFMHFMdUpNYU5FUDR + vdXhKNVVHTEREdWphZUN6ejhJTkdjIn19.eyJodG0iOiJQT1NUIiwic3ViIjoic3ViIiwibmJmIjoxNjM1MTc2MDg2LCJp + c3MiOiJpc3N1ZXIiLCJodHUiOiJodHRwczpcL1wvbG9jYWxob3N0Ojk0NDNcL29hdXRoMlwvdG9rZW4iLCJpYXQiOjE2Mz + UxNzYwODYsImp0aSI6ImUzMTEzYzdmLTE0YTQtNGU2ZC1hY2VmLTZjZDQ5NWQ1ZWZjNCJ9.lgEjdP1Fv3OOAaFfM4CEREg + 46RGdbusLzb4409eCMabCwbN0dS6NdXsc6qxfR_TVhkKzIUKLSDmM4qzEIuw4LQ``` + diff --git a/dpop/DPoP-client-app/pom.xml b/dpop/DPoP-client-app/pom.xml new file mode 100644 index 000000000..73262fe37 --- /dev/null +++ b/dpop/DPoP-client-app/pom.xml @@ -0,0 +1,151 @@ + + + 4.0.0 + + org.wso2.dpop.client + client-app + 1.0 + + + 8 + 8 + 7.3.0.wso2v1 + 1.3 + 1.2 + + + + + ${project.basedir} + + lib/*.jar + + + + + + org.apache.maven.plugins + maven-eclipse-plugin + 2.9 + + true + false + + + + org.apache.maven.plugins + maven-dependency-plugin + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.8 + 1.8 + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + true + lib/ + com.etl_aggregation.app.App + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.4.1 + + + + jar-with-dependencies + + + + + com.etl_aggregation.app.App + + + + + + + make-assembly + + package + + single + + + + + + + + + org.wso2.orbit.com.nimbusds + nimbus-jose-jwt + ${nimbusds.version} + + + net.minidev + json-smart + ${json-smart.version} + + + commons-logging + commons-logging + ${commons.logging.version} + + + + + + wso2-nexus + WSO2 internal Repository + https://maven.wso2.org/nexus/content/groups/wso2-public/ + + true + daily + ignore + + + + wso2.releases + WSO2 internal Repository + https://maven.wso2.org/nexus/content/repositories/releases/ + + true + daily + ignore + + + + + wso2.snapshots + WSO2 Snapshot Repository + https://maven.wso2.org/nexus/content/repositories/snapshots/ + + true + daily + + + false + + + + diff --git a/dpop/DPoP-client-app/src/main/java/org/wso2/dpop/client/DPOPProofGenerator.java b/dpop/DPoP-client-app/src/main/java/org/wso2/dpop/client/DPOPProofGenerator.java new file mode 100644 index 000000000..e22cc1fe4 --- /dev/null +++ b/dpop/DPoP-client-app/src/main/java/org/wso2/dpop/client/DPOPProofGenerator.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2022, WSO2 LLC (http://www.wso2.org) All Rights Reserved. + * + * WSO2 LLC licenses this file to you under the Apache license, + * Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.wso2.dpop.client; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JOSEObjectType; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.crypto.ECDSASigner; +import com.nimbusds.jose.crypto.RSASSASigner; +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.ECKey; +import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.RSAKey; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPublicKey; +import java.text.ParseException; +import java.util.Date; +import java.util.Scanner; +import java.util.UUID; + +import static com.nimbusds.jose.JWSAlgorithm.ES256; +import static com.nimbusds.jose.JWSAlgorithm.RS384; + +/** + * DPOPProofGenerator will generate per request private and public keypair. + */ +public class DPOPProofGenerator { + + private static final Log log = LogFactory.getLog(DPOPProofGenerator.class); + + public static void main(String args[]) throws NoSuchAlgorithmException, JOSEException, ParseException, + InvalidAlgorithmParameterException { + + Scanner scan = new Scanner(System.in); + System.out.println("Enter the public and private key pair type(EC or RSA): "); + String keyPairType = scan.nextLine(); + System.out.println("Enter the HTTP Method: "); + String httpMethod = scan.nextLine(); + System.out.println("Enter the HTTP Url: "); + String httpUrl = scan.nextLine(); + + KeyPairGenerator gen = KeyPairGenerator.getInstance(keyPairType); + KeyPair keyPair; + JWK jwk = null; + if ("EC".equals(keyPairType)) { + gen.initialize(Curve.P_256.toECParameterSpec()); + keyPair = gen.generateKeyPair(); + jwk = new ECKey.Builder(Curve.P_256, (ECPublicKey) keyPair.getPublic()) + .build(); + } else { + gen = KeyPairGenerator.getInstance("RSA"); + gen.initialize(2048); + keyPair = gen.generateKeyPair(); + jwk = new RSAKey.Builder((RSAPublicKey) keyPair.getPublic()).build(); + } + + log.info("jwk: " + jwk); + log.info("publicKey: " + keyPair.getPublic()); + log.info("private: " + keyPair.getPrivate()); + + JWTClaimsSet.Builder jwtClaimsSetBuilder = new JWTClaimsSet.Builder(); + jwtClaimsSetBuilder.issuer("issuer"); + jwtClaimsSetBuilder.subject("sub"); + jwtClaimsSetBuilder.issueTime(new Date(System.currentTimeMillis())); + jwtClaimsSetBuilder.jwtID(UUID.randomUUID().toString()); + jwtClaimsSetBuilder.notBeforeTime(new Date(System.currentTimeMillis())); + jwtClaimsSetBuilder.claim("htm", httpMethod); + jwtClaimsSetBuilder.claim("htu", httpUrl); + + JWSHeader.Builder headerBuilder; + if ("EC".equals(keyPairType)) { + headerBuilder = new JWSHeader.Builder(ES256); + } else { + headerBuilder = new JWSHeader.Builder(RS384); + } + headerBuilder.type(new JOSEObjectType("dpop+jwt")); + headerBuilder.jwk(jwk); + SignedJWT signedJWT = new SignedJWT(headerBuilder.build(), jwtClaimsSetBuilder.build()); + + if ("EC".equals(keyPairType)) { + ECDSASigner ecdsaSigner = new ECDSASigner(keyPair.getPrivate(), Curve.P_256); + signedJWT.sign(ecdsaSigner); + } else { + RSASSASigner rsassaSigner = new RSASSASigner(keyPair.getPrivate()); + signedJWT.sign(rsassaSigner); + } + + log.info("[Signed JWT] : " + signedJWT.serialize()); + JWK parseJwk = JWK.parse(String.valueOf(jwk)); + if ("EC".equals(keyPairType)) { + ECKey ecKey = (ECKey) parseJwk; + log.info("[ThumbPrint] : " + ecKey.computeThumbprint().toString()); + } else { + RSAKey rsaKey = (RSAKey) parseJwk; + log.info("[ThumbPrint] : " + rsaKey.computeThumbprint().toString()); + } + } +} diff --git a/dpop/DPoP-client-app/src/main/java/org/wso2/dpop/client/DPOPProofGeneratorFromPP.java b/dpop/DPoP-client-app/src/main/java/org/wso2/dpop/client/DPOPProofGeneratorFromPP.java new file mode 100644 index 000000000..cbbfc8721 --- /dev/null +++ b/dpop/DPoP-client-app/src/main/java/org/wso2/dpop/client/DPOPProofGeneratorFromPP.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2022, WSO2 LLC (http://www.wso2.org) All Rights Reserved. + * + * WSO2 LLC licenses this file to you under the Apache license, + * Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.wso2.dpop.client; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JOSEObjectType; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.crypto.ECDSASigner; +import com.nimbusds.jose.crypto.RSASSASigner; +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.ECKey; +import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.RSAKey; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.text.ParseException; +import java.util.Date; +import java.util.Scanner; +import java.util.UUID; + +import static com.nimbusds.jose.JWSAlgorithm.ES256; +import static com.nimbusds.jose.JWSAlgorithm.RS384; + +/** + * DPOPProofGeneratorFromPP will use the already generated key pairs and load and generate the DPoP Proof. + */ +public class DPOPProofGeneratorFromPP { + + private static final Log log = LogFactory.getLog(DPOPProofGeneratorFromPP.class); + + public static void main(String args[]) throws NoSuchAlgorithmException, JOSEException, ParseException, + IOException, InvalidKeySpecException { + + Scanner scan = new Scanner(System.in); + System.out.println("File path to private key: "); + String privateKeyPath = scan.nextLine(); + System.out.println("File path to public key: "); + String publicKeyPath = scan.nextLine(); + System.out.println("Enter the public and private key pair type(EC or RSA): "); + String keyPairType = scan.nextLine(); + System.out.println("Enter the HTTP Method: "); + String httpMethod = scan.nextLine(); + System.out.println("Enter the HTTP Url: "); + String httpUrl = scan.nextLine(); + + /* Read all bytes from the private key file */ + Path path = Paths.get(privateKeyPath); + byte[] bytes = Files.readAllBytes(path); + + /* Generate private key. */ + PKCS8EncodedKeySpec privateKs = new PKCS8EncodedKeySpec(bytes); + KeyFactory privateKf = KeyFactory.getInstance(keyPairType); + PrivateKey privateKey = privateKf.generatePrivate(privateKs); + + /* Read all the public key bytes */ + Path pubPath = Paths.get(publicKeyPath); + byte[] pubBytes = Files.readAllBytes(pubPath); + + /* Generate public key. */ + X509EncodedKeySpec ks = new X509EncodedKeySpec(pubBytes); + KeyFactory kf = KeyFactory.getInstance(keyPairType); + PublicKey publicCert = kf.generatePublic(ks); + + JWK jwk = null; + if ("EC".equals(keyPairType)) { + jwk = new ECKey.Builder(Curve.P_256, (ECPublicKey) publicCert) + .build(); + } else { + jwk = new RSAKey.Builder((RSAPublicKey) publicCert).build(); + } + + log.info("jwk: " + jwk); + log.info("publicKey: " + publicCert); + log.info("private: " + privateKey); + + JWTClaimsSet.Builder jwtClaimsSetBuilder = new JWTClaimsSet.Builder(); + jwtClaimsSetBuilder.issuer("issuer"); + jwtClaimsSetBuilder.subject("sub"); + jwtClaimsSetBuilder.issueTime(new Date(System.currentTimeMillis())); + jwtClaimsSetBuilder.jwtID(UUID.randomUUID().toString()); + jwtClaimsSetBuilder.notBeforeTime(new Date(System.currentTimeMillis())); + jwtClaimsSetBuilder.claim("htm", httpMethod); + jwtClaimsSetBuilder.claim("htu", httpUrl); + + JWSHeader.Builder headerBuilder; + if ("EC".equals(keyPairType)) { + headerBuilder = new JWSHeader.Builder(ES256); + } else { + headerBuilder = new JWSHeader.Builder(RS384); + } + headerBuilder.type(new JOSEObjectType("dpop+jwt")); + headerBuilder.jwk(jwk); + SignedJWT signedJWT = new SignedJWT(headerBuilder.build(), jwtClaimsSetBuilder.build()); + + if ("EC".equals(keyPairType)) { + ECDSASigner ecdsaSigner = new ECDSASigner(privateKey, Curve.P_256); + signedJWT.sign(ecdsaSigner); + } else { + RSASSASigner rsassaSigner = new RSASSASigner(privateKey); + signedJWT.sign(rsassaSigner); + } + + log.info("[Signed JWT from File] : " + signedJWT.serialize()); + JWK parseJwk = JWK.parse(String.valueOf(jwk)); + if ("EC".equals(keyPairType)) { + ECKey ecKey = (ECKey) parseJwk; + log.info("[ThumbPrint] : " + ecKey.computeThumbprint().toString()); + } else { + RSAKey rsaKey = (RSAKey) parseJwk; + log.info("[ThumbPrint] : " + rsaKey.computeThumbprint().toString()); + } + } +} diff --git a/dpop/DPoP-client-app/src/main/java/org/wso2/dpop/client/GeneratePublicKeyPair.java b/dpop/DPoP-client-app/src/main/java/org/wso2/dpop/client/GeneratePublicKeyPair.java new file mode 100644 index 000000000..486b48c75 --- /dev/null +++ b/dpop/DPoP-client-app/src/main/java/org/wso2/dpop/client/GeneratePublicKeyPair.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2022, WSO2 LLC (http://www.wso2.org) All Rights Reserved. + * + * WSO2 LLC licenses this file to you under the Apache license, + * Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.wso2.dpop.client; + +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.ECKey; +import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.RSAKey; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.Key; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPublicKey; +import java.util.Scanner; + +/** + * GeneratePublicKeyPair will create a public private key pair and store it in a file. + */ +public class GeneratePublicKeyPair { + + private static final Log log = LogFactory.getLog(GeneratePublicKeyPair.class); + + public static void main(String args[]) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, + IOException { + + Scanner scan = new Scanner(System.in); + System.out.println("Enter the public and private key pair type(EC or RSA): "); + + String keyPairType = scan.nextLine(); + KeyPairGenerator gen = KeyPairGenerator.getInstance(keyPairType); + KeyPair keyPair; + JWK jwk; + if ("EC".equals(keyPairType)) { + gen.initialize(Curve.P_256.toECParameterSpec()); + keyPair = gen.generateKeyPair(); + jwk = new ECKey.Builder(Curve.P_256, (ECPublicKey) keyPair.getPublic()) + .build(); + } else { + gen = KeyPairGenerator.getInstance("RSA"); + gen.initialize(2048); + keyPair = gen.generateKeyPair(); + jwk = new RSAKey.Builder((RSAPublicKey) keyPair.getPublic()).build(); + } + + log.info("jwk: " + jwk); + log.info("publicKey: " + keyPair.getPublic()); + log.info("private: " + keyPair.getPrivate()); + + Key pub = keyPair.getPublic(); + Key pvt = keyPair.getPrivate(); + + log.info("Private key format: " + pvt.getFormat()); + log.info("Public key format: " + pub.getFormat()); + + String outFile = "dpop"; + FileOutputStream privout = new FileOutputStream(outFile + ".key"); + privout.write(pvt.getEncoded()); + privout.close(); + + FileOutputStream pubout = new FileOutputStream(outFile + ".pub"); + pubout.write(pub.getEncoded()); + pubout.close(); + } +} diff --git a/pom.xml b/pom.xml index daa8afe7a..a2aa403fc 100644 --- a/pom.xml +++ b/pom.xml @@ -65,6 +65,7 @@ identity-mgt authenticators passive-sts + dpop/DPoP-client-app