1. 如何创建JWK
下面展示一个JWK案例
一个典型公钥JWK的案例:公钥和对应的私钥
公钥
{
"kty": "RSA",
"kid": "6ad0d632198de9ad926bf617e79cf412",
"alg": "ES256",
"n": "jy1H4f2XXsBAynu145alDhPIQ1lldTAYMLBJG2YguRd5aZ3oecZHh9Bu4qg1hw1GmNcaPG3iKUwSD-BiPw2CQfukOcdDq2oBnYYwf3EzmbwpJ5y4sk1k5aMRWh3n3atdcuNWiP_EErGLTde0_a_gkRXg39XNnHEaH5k4mY0HtBPQ7nvd1GZYsq9hbbxej3IyIUWRzjP_txuxWV7j41UkzFfjvq6xRAy-WgkZ3rHsXWU5jNRJugpYF-_P4L8C8vmHAYQUy41-YeK4-E30XIzbOL2LENAq1DnuzfdPbNX31BYn1chwQCw56mGoDqUrac-booeTjg-CYQv4r6c53urqcw",
"e": "AQAB"
}
私钥
{
"kty": "RSA",
"kid": "6ad0d632198de9ad926bf617e79cf412",
"alg": "ES256",
"n": "jy1H4f2XXsBAynu145alDhPIQ1lldTAYMLBJG2YguRd5aZ3oecZHh9Bu4qg1hw1GmNcaPG3iKUwSD-BiPw2CQfukOcdDq2oBnYYwf3EzmbwpJ5y4sk1k5aMRWh3n3atdcuNWiP_EErGLTde0_a_gkRXg39XNnHEaH5k4mY0HtBPQ7nvd1GZYsq9hbbxej3IyIUWRzjP_txuxWV7j41UkzFfjvq6xRAy-WgkZ3rHsXWU5jNRJugpYF-_P4L8C8vmHAYQUy41-YeK4-E30XIzbOL2LENAq1DnuzfdPbNX31BYn1chwQCw56mGoDqUrac-booeTjg-CYQv4r6c53urqcw",
"e": "AQAB",
"d": "AZwvv8HbTux4jg5gGEQcbREZ4nAB7RRyYTHKUK2QVqSF8AMflyb8rPFSHehoWbFzhsp8eAuATJiXZ2hNkonzWgybZy7veuEBTaUxrG04eFUeXVys0wx9wvtXeZz2e46uyVj3IylFKjSm7WQsm-G7c8Y8NigwXL8E3TAa69cjLAo8ss1bR6-JMId5zAvlAZHKceAnEzAFQjj_BODDAEYhc8Mvzxf5GTW01ABs5o4LTK0KWHmuKT5Ai7U61DIDp8BEqqaDuz7w3HVINBySuQGO3BR0VtwPsUL4m0_LxMb3Gu5RRZpYRnT323vTUFm66AXQqZsDRO4hgSahiQDTQxk_QQ",
"p": "yEZC0BBgVm5m0Lu84uZ4oxhKXXFoo1iC5j1Ilc4rwUGILFCFSULK3_zIB9xTQvLMqPkm4GEF2eQMoCjyrJg495BTZ9Ipk0qxJCklCUTQ_e5vFdAtp_K-ci2S85AsKR1-q5vuA2E-g9-HCGUUJ4834BmZE-nFeC5i_MdrrSsI7JM",
"q": "twPnvo3mcWW-5k903teXQKYgCo_SgBABnEK4Nswr6lpOplAEhR6Auf5Wo-ekh9H9SjtwwDZ8Z0qLHozH36QSCyDjbi-w_mdh-yRW055o5LXgjGqGh_qb1iVJ7pf6yCZK_zTGqx23ET1UnYRiYWDEpowrrSo6mvJaPmD8lhuQlqE",
"dp": "e2y5nkqJZJrTaE5bASbyL-k3Y1ESIKDawxP_mLsfwhEl39Gb4uNz7gh2KkoBUiAaOwSZjeydU4Q0t7ukmvORIBjlHfWqQ6jjdJqaxZSQi_4WncXXbUqvTeSCTPKMFKaluxL040ZZ5aGrMWRBwIOF9ukvqtMGLKPBw3EPDgCYlls",
"dq": "M24shNc5qCpQkEZJ8ImjXq6QmIc8P3LAERqKzBNqT-xa58_axVICGMKJtHvXN7fiNycnE0z8fsZq_AXR8V4ZF_mBECjk00lYNoxKviNpFMSruqoA7luVyYMnGJ6rAe4I61j4b4PlOzoB-lYGk5jvCmKfr4ULfRmYFKmKseBDLKE",
"qi": "kjvP5kanvQOccxpl4cK13YlRAvsrYmVVFt9coc-6odPgM2jN_lRjcCws96856PEkUVLwoo6sB6Y_qfO1orSfApRQ7nQJmYvOohu-zjyDq2GiQ6lUDn5LoDfafQyYqSEEM0U8j6_f0pWRlm-0Suzg5wrsodPzBeHktbxwi4JIYQE"
}
我们使用阿里云推荐的java代码来生成JWK密钥对,运行本工具需要java8的环境
下面讲解怎么创建一个JWK
下载生成JWK的java代码压缩包(http://doc.xdua.com/ref/jwk/jwk_generator.tar)
生成的目录结构如下,其中所有的jar文件都是用来辅助生成key和生成token,运行genkey.sh可以根据一个32位种子生成一个秘钥对
jwk_generator/ ├── com.springsource.org.apache.log4j-1.2.16.jar ├── Genkey.class ├── Genkey.java ├── genkey.sh ├── Gentoken.class ├── Gentoken.java ├── gentoken.sh ├── jose4j-0.6.3.jar ├── README.md ├── slf4j-api-1.7.25.jar └── slf4j-log4j12-1.7.25.jar
自己制作一个32位的字符串作为kid,这个kid很重要,不能让任何第三方知道,这里我们使用一个示例kid:
6ad0d632198de9ad926bf617e79cf412
把这个kid放到genkey.sh脚本中,然后运行,会启动编译和执行,生成一个公私密钥对
javac -cp .:./jose4j-0.6.3.jar Genkey.java java -cp .:./jose4j-0.6.3.jar Genkey 6ad0d632198de9ad926bf617e79cf412 #注意-cp后面要有两个符号.: ,否则就报Could not find or load main class错误了
输出结果入下图
publicKey-----------------------start {"kty":"RSA","kid":"6ad0d632198de9ad926bf617e79cf412","alg":"ES256","n":"qZG5bGi4UV_IQV1jh4btrM1NPjtQGFUNI9OvupQXFuWLkfU8QU2CflqZ9d1yvh8kpRj2JimPr5y0kv2h3XaO2VCYZInVkoHRrb0XkSZA3LcExb83nLMUCDUQxrMfE4rW5dgTaw3UMJRVGT7Bebw8NlAIdwpeTKMzJQWcLHjU7Ogle899hJHRap8LXDfX3_WWE7wIvQkbjRqwuYSzZZOzJEslZRdKkDBdESRM36yNOuG8uiPOFJOcmtd4bWmrN66LTMX1pu89e2bCuITrkgRBjzihhOXc-TqeAi-ADpWiwI3kzocubHBnXgTN7sMQsLDf3IB5_YkdbxQ_gBLv3pqREw","e":"AQAB"} publicKey-----------------------finish privateKey-----------------------start {"kty":"RSA","kid":"6ad0d632198de9ad926bf617e79cf412","alg":"ES256","n":"qZG5bGi4UV_IQV1jh4btrM1NPjtQGFUNI9OvupQXFuWLkfU8QU2CflqZ9d1yvh8kpRj2JimPr5y0kv2h3XaO2VCYZInVkoHRrb0XkSZA3LcExb83nLMUCDUQxrMfE4rW5dgTaw3UMJRVGT7Bebw8NlAIdwpeTKMzJQWcLHjU7Ogle899hJHRap8LXDfX3_WWE7wIvQkbjRqwuYSzZZOzJEslZRdKkDBdESRM36yNOuG8uiPOFJOcmtd4bWmrN66LTMX1pu89e2bCuITrkgRBjzihhOXc-TqeAi-ADpWiwI3kzocubHBnXgTN7sMQsLDf3IB5_YkdbxQ_gBLv3pqREw","e":"AQAB","d":"H3m6RoJFvG0gGeoA4JZOJeZR8TxHSMz4zG6tzt9QTeK7_pLb5W9Cyrt8mqPJebsELVUt07WhQ0K-Tg6gbiBksbgBIaBoMXLBfhN8fWfxYEbkruQoqYCgNknLXGbBXPpHm5B5QkTl30KZbT814KftMVO3zojxKh6dDsE01Rh0xaigBN57xzjPA3p3XmAh3TAobYcV_BCpxD1FKVxMavoG7Kc0ZoCYHk6eU4NrHrqEsLzqMxE3U7iEezGZhgLv-vJ5tlXjv9nxQGTafro3kTaj4II62vsapGI_iBJR1zlcT-QpXkY6afKj-dEWm9ZbKfiBcY1wITLFh0WxAxCfEawUiQ","p":"6GfgynZ_Xl2_AleC1Ax9sPAPkml3pnZb1Lnw36Zgn9olTw1glIzXja1xj8jCueylSjXyqGmd7jUOuQxArzyBinDQIYIv0IxzPHxENzrSkdiZ1Mv2Yv4XSDMXGFaN9aJGiNWMo0S_fJEhuuZ7wqOxB_-s9M0Y2W7A9f4WKKQYQF8","q":"usjAtGhxkDCukGXLIItZpFScdUABNFatJyd3lz1ZSxV-OIo9jDjIeiiXThRVJBEnXNIGYKJl548iRkz4Hs-13AlGvbHwNAthJRAmj8c08kBUcZOVT5v1Wxr2Rg7fzsMl-mHplrRZD2hA28BZ2Df6Z7fP-HRppHyradC93_27G80","dp":"o532YTOZqhr_zatEfPsqRjZMEDzWMshjEFmz3hCpOAEBgS0e0JZzbtgr-hlSFkkneR6P7sckm76Y1ehtZbGIVankraKU_RLUUkH-WI--DVXbvze1B65xP7BQUx8kpEkRtWX6tDtDQHSAta9sc_SAPuxenJ4EH5fcm5K9kPEt7as","dq":"JVpUPz_LxhwWLMZOktmLObO5_jCojQDwa64W2mXoX32S2le66znHzbYkW_bw999-Ua-mmtifLGmRXyGxVOgQ5Enunazh7maALNtH-uTJj9CRko0DBQKZVHjV2zjKRRz9kU7XWc4DKXOd1NRooza-mtNFgdg18DEGTojyD8M8Edk","qi":"FaWlmA8Gpk4NEXDSA4dy3mfCYNK6e8TbCVB5O_JXzK-z0eaQA7O8PHQ08A40HTZclqYtICT5ihrE2MIihySKi10IgNyu5iAPssCwtaV_J50IyBKl15AXPRPVhy6Em3AuQ1CJPxlvBm0OPvMPR3HXF4ZN4A6ft5RpRqmZVLfMmMI"} privateKey-----------------------finish
其实是调用Genkey.java的代码,代码参考于阿里云的API网关的示例代码
import java.security.PrivateKey; import org.jose4j.json.JsonUtil; import org.jose4j.jwk.RsaJsonWebKey; import org.jose4j.jwk.RsaJwkGenerator; import org.jose4j.jws.AlgorithmIdentifiers; import org.jose4j.jws.JsonWebSignature; import org.jose4j.jwt.JwtClaims; import org.jose4j.jwt.NumericDate; import org.jose4j.lang.JoseException; public class Genkey { public static void main(String[] args) throws Exception { try{ String keyId = args[0]; //String keyId = "b50b1effdc035e2869b6c45f33bdf5d7"; RsaJsonWebKey jwk = RsaJwkGenerator.generateJwk(2048); jwk.setKeyId(keyId); jwk.setAlgorithm(AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256); String publicKey = jwk.toJson(RsaJsonWebKey.OutputControlLevel.PUBLIC_ONLY); String privateKey = jwk.toJson(RsaJsonWebKey.OutputControlLevel.INCLUDE_PRIVATE); System.out.println("\npublicKey-----------------------start\n"); System.out.println(publicKey); System.out.println("\npublicKey-----------------------finish\n"); System.out.println("\nprivateKey-----------------------start\n"); System.out.println(privateKey); System.out.println("\nprivateKey-----------------------finish\n"); } catch(Exception e) { //将sqrt方法声明的可能抛出的Exception异常捕获 System.out.println("Got a Exception:" + e.getMessage()); e.printStackTrace(); } } }
用生成的秘钥生成一个token 文件夹下的gentoken.sh可以用刚刚的秘钥生成一个token
javac -cp .:./com.springsource.org.apache.log4j-1.2.16.jar:./slf4j-api-1.7.25.jar:./slf4j-log4j12-1.7.25.jar:./jose4j-0.6.3.jar Gentoken.java java -cp .:./com.springsource.org.apache.log4j-1.2.16.jar:./slf4j-api-1.7.25.jar:./slf4j-log4j12-1.7.25.jar:./jose4j-0.6.3.jar Gentoken
下面展示了Gentoken.java的全部
import java.security.PrivateKey; import org.jose4j.json.JsonUtil; import org.jose4j.jwk.RsaJsonWebKey; import org.jose4j.jwk.RsaJwkGenerator; import org.jose4j.jws.AlgorithmIdentifiers; import org.jose4j.jws.JsonWebSignature; import org.jose4j.jwt.JwtClaims; import org.jose4j.jwt.NumericDate; import org.jose4j.lang.JoseException; public class Gentoken { public static void main(String[] args) throws Exception { try{ String privateKeyText = "{\"kty\":\"RSA\",\"kid\":\"6ad0d632198de9ad926bf617e79cf412\",\"alg\":\"ES256\",\"n\":\"sBZDgQUcSYv-OVwjwluFBCpDm8eqmrlmWGk1fp8qdSPGt7JLII2aLqXarxeguA2l7tqHfz8c8ItpHDyUKlL2LfUQLIWFA37QG1JBpXqE24UlE01fHfdrvzwHm_bHW-5RLaNltb0W3WWfOrBIrrurRWaCBF59z2XkmA-_nw_YJybpWBsB0lhCvU_jnx8MQnlHvh19HUVSTGo9BxwhrS8No7UQe7FPxvZr9ui6XEm9lPWdgXrklNPwXtDFapV_4Qj4XnUrSgnZrcBju-B75bjfwS5KgUlTN75-kHcozrqntquyWBfEQc2Ct2_j61a1qCuTtEiK9W-C4GYSRq8Mjb7BRw\",\"e\":\"AQAB\",\"d\":\"cFr0Xam-H-uUnlL9ejFdUYgKNacUY79y32zaNl4nADGAxYudRjs0KxmmzNwdr_L9cse7d2T0-UNrIRpCTDM9Y5uf41iaw90xuo0k2AqyJUPyoTtYOs250X7jOBAhqrYI1D8TUgBCS6hhDliXN-8FXLYItfL5AdH5J1G9Kig4-tlYefkM2scZ7IiS8g7o-EZauZFhHSdCKWD7KO3XkdvqV3R2zGb06ANFlqkOzopo8eOkYfqAqTExe8dTMNUwfJHtzNR4tf2UcWqGt9sEbrdw9K8GjJZNCcXYiJFgDgKUkn-aOARwuDvjmNKmtS4HBYaT50yKwUBod7eMLiGHtse-EQ\",\"p\":\"5ZVC3bVmUvTTM9EJx6mV4Xhu8U4xsbOQDEfVb9OdDl6xd-_dnKJkHCqif2igfdr598P-cHC2DzW_O5Bwt2xSp-Ywgji_3bftfVh-BL_CTriMo8KhOIDtyZuWhv3ZL62D5Tbrqs48whd6sW-WFyA6OEqDoTBdzNdtCAP88fytBbk\",\"q\":\"xFkwafo1qLjkOUTIHpQ6PrTttsZ8P7bDWjYgfz98JARkb9YX5vDHcKq6efYCt7uFqg1la0v6NYn2EEOMZosj6zVvAizdzrBQ2fI3cepqhH7KZCi5z6vYcv8dJkX9MQPVn1os696iSvHw4MTnnug-MMl7iRy4wuvAualRsNO9fv8\",\"dp\":\"RnNyTHTRDJ3ifeEk3idVPhZl_RiguUY_6vTUM3e9l15JmWN7HwjaLaaLrQtfTJ-422ZCmyCLTcmRgGbHoBHWn31M33kor5I0h6VzLmPl7aBGUC52qM8vqRFctNoLHs1hTyJ2WTjmfi0UUoTKixgxpXqAQCOGdUiaRb5rjZqbQck\",\"dq\":\"uUpJi9kZRi2lAf5Ms77B8GchZTiODDpWxA0MQckRR5P1jCyHxeI287XJ4EouamDGVKHrsYOzjU6yLMrx9dscfFyic3UPpHty8RnJBPFor7xPFpHwN3A-BNeHFJU7yEOMFqMsfTJOCVekLxjiU21rMvMQZ2X5XcjIyOxmMO7AhtU\",\"qi\":\"odqlkxgyQJu8ypVHJRhQN12O5DdAOlIG2OwMRS4tGB35tw77wjMpQTtS5dtDTNdc714EEpZz4bD0G1kmj3u5Fw_AfCrxDVMnxKsfjp-lAFJ-25aWP_Qm8f27OOmmBjBqtrr-YfZNn56wERBu5S3EiD0IJTBKsmZ6HKpe_93VJ7s\"}"; String keyId = "6ad0d632198de9ad926bf617e79cf412"; JwtClaims claims = new JwtClaims(); claims.setGeneratedJwtId(); claims.setIssuedAtToNow(); //expire time NumericDate date = NumericDate.now(); date.addSeconds(120); claims.setExpirationTime(date); claims.setNotBeforeMinutesInThePast(1); claims.setSubject("YOUR_SUBJECT"); claims.setAudience("YOUR_AUDIENCE"); //添加自定义参数 //claims.setClaim(key, value); JsonWebSignature jws = new JsonWebSignature(); jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256); jws.setKeyIdHeaderValue(keyId); jws.setPayload(claims.toJson()); PrivateKey privateKey = new RsaJsonWebKey(JsonUtil.parseJson(privateKeyText)).getPrivateKey(); jws.setKey(privateKey); String idToken = jws.getCompactSerialization(); System.out.println("\n----------------\n"); System.out.println(idToken); } catch(Exception e) { //将sqrt方法声明的可能抛出的Exception异常捕获 System.out.println("Got a Exception:" + e.getMessage()); e.printStackTrace(); } } }