-
Notifications
You must be signed in to change notification settings - Fork 3
X509 #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
X509 #7
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,6 +37,12 @@ dependencies { | |
| compile 'com.fasterxml.jackson.core:jackson-databind:2.9.2' | ||
| compile 'commons-codec:commons-codec:1.11' | ||
| compile 'com.google.code.gson:gson:2.8.2' | ||
| compile 'com.auth0:jwks-rsa:0.3.0' | ||
| compile 'com.nimbusds:nimbus-jose-jwt:2.19.1' | ||
| compile 'org.apache.httpcomponents:httpcore:4.4.1' | ||
| compile 'org.apache.httpcomponents:httpclient:4.5' | ||
| compile group: 'org.slf4j', name:'slf4j-api', version: '1.7.2' | ||
| compile group: 'com.googlecode.json-simple', name: 'json-simple', version: '1.1.1' | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yet another JSON library?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. probably was an unused import. removed. |
||
| testCompile 'org.bouncycastle:bcprov-jdk15on:1.58' | ||
| testCompile 'junit:junit:4.12' | ||
| testCompile 'net.jodah:concurrentunit:0.4.3' | ||
|
|
@@ -61,4 +67,4 @@ test { | |
| task clean(type: Delete) { | ||
| delete rootProject.buildDir | ||
| delete 'CHANGELOG.md.release' | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| {"keys":[{"alg":"RS256","kty":"RSA","use":"sig","x5c":["MIIDDTCCAfWgAwIBAgIJAJVkuSv2H8mDMA0GCSqGSIb3DQEBBQUAMB0xGzAZBgNVBAMMEnNhbmRyaW5vLmF1dGgwLmNvbTAeFw0xNDA1MTQyMTIyMjZaFw0yODAxMjEyMTIyMjZaMB0xGzAZBgNVBAMMEnNhbmRyaW5vLmF1dGgwLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL6jWASkHhXz5Ug6t5BsYBrXDIgrWu05f3oq2fE+5J5REKJiY0Ddc+Kda34ZwOptnUoef3JwKPDAckTJQDugweNNZPwOmFMRKj4xqEpxEkIX8C+zHs41Q6x54ZZy0xU+WvTGcdjzyZTZ/h0iOYisswFQT/s6750tZG0BOBtZ5qS/80tmWH7xFitgewdWteJaASE/eO1qMtdNsp9fxOtN5U/pZDUyFm3YRfOcODzVqp3wOz+dcKb7cdZN11EYGZOkjEekpcedzHCo9H4aOmdKCpytqL/9FXoihcBMg39s1OW3cfwfgf5/kvOJdcqR4PoATQTfsDVoeMWVB4XLGR6SC5kCAwEAAaNQME4wHQYDVR0OBBYEFHDYn9BQdup1CoeoFi0Rmf5xn/W9MB8GA1UdIwQYMBaAFHDYn9BQdup1CoeoFi0Rmf5xn/W9MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAGLpQZdd2ICVnGjc6CYfT3VNoujKYWk7E0shGaCXFXptrZ8yaryfo6WAizTfgOpQNJH+Jz+QsCjvkRt6PBSYX/hb5OUDU2zNJN48/VOw57nzWdjI70H2Ar4oJLck36xkIRs/+QX+mSNCjZboRwh0LxanXeALHSbCgJkbzWbjVnfJEQUP9P/7NGf0MkO5I95C/Pz9g91y8gU+R3imGppLy9Zx+OwADFwKAEJak4JrNgcjHBQenakAXnXP6HG4hHH4MzO8LnLiKv8ZkKVL67da/80PcpO0miMNPaqBBMd2Cy6GzQYE0ag6k0nk+DMIFn7K+o21gjUuOEJqIbAvhbf2KcM="],"n":"vqNYBKQeFfPlSDq3kGxgGtcMiCta7Tl_eirZ8T7knlEQomJjQN1z4p1rfhnA6m2dSh5_cnAo8MByRMlAO6DB401k_A6YUxEqPjGoSnESQhfwL7MezjVDrHnhlnLTFT5a9MZx2PPJlNn-HSI5iKyzAVBP-zrvnS1kbQE4G1nmpL_zS2ZYfvEWK2B7B1a14loBIT947Woy102yn1_E603lT-lkNTIWbdhF85w4PNWqnfA7P51wpvtx1k3XURgZk6SMR6Slx53McKj0fho6Z0oKnK2ov_0VeiKFwEyDf2zU5bdx_B-B_n-S84l1ypHg-gBNBN-wNWh4xZUHhcsZHpILmQ","e":"AQAB","kid":"8RGoVdVjD8fItyR3FFo0hVNaZYtPGwoP6xKi9e_V7bI","x5t":"RkI5MjI5OUY5ODc1N0Q4QzM0OUYzNkVGMTJDOUEzQkFCOTU3NjE2Rg"}]} | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is this for and why is it here?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. its for the tests to test x509 functionality |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,23 +20,26 @@ | |
| package com.auth0.jwt; | ||
|
|
||
| import com.auth0.jwt.creators.EncodeType; | ||
| import com.auth0.jwt.creators.JWTCreator; | ||
| import com.auth0.jwt.exceptions.JWTDecodeException; | ||
| import com.auth0.jwt.creators.FbJwtCreator; | ||
| import com.auth0.jwt.creators.GoogleJwtCreator; | ||
| import com.auth0.jwt.creators.GoogleOrFbJwtCreator; | ||
| import com.auth0.jwt.impl.Claims; | ||
| import com.auth0.jwt.impl.JWTParser; | ||
| import com.auth0.jwt.interfaces.Claim; | ||
| import com.auth0.jwt.interfaces.DecodedJWT; | ||
| import com.auth0.jwt.interfaces.Header; | ||
| import com.auth0.jwt.interfaces.Payload; | ||
| import org.apache.commons.codec.binary.Base32; | ||
| import org.apache.commons.codec.binary.Base64; | ||
| import org.apache.commons.codec.binary.Hex; | ||
| import org.apache.commons.codec.binary.StringUtils; | ||
|
|
||
| import com.auth0.jwt.utils.TokenUtils; | ||
| import com.google.common.base.Strings; | ||
| import java.net.URLDecoder; | ||
| import java.net.URLEncoder; | ||
| import java.nio.charset.StandardCharsets; | ||
| import java.util.Date; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import org.apache.commons.codec.binary.Base32; | ||
| import org.apache.commons.codec.binary.Base64; | ||
| import org.apache.commons.codec.binary.Hex; | ||
| import org.apache.commons.codec.binary.StringUtils; | ||
|
|
||
| /** | ||
| * The JWTDecoder class holds the decode method to parse a given JWT token into it's JWT representation. | ||
|
|
@@ -47,6 +50,8 @@ public final class JWTDecoder implements DecodedJWT { | |
| private final String[] parts; | ||
| private final Header header; | ||
| private final Payload payload; | ||
| private static final String ISSUER_FACEBOOK = "facebook"; | ||
| private static final String ISSUER_GOOGLE = "google"; | ||
|
|
||
| public JWTDecoder(String jwt, EncodeType encodeType) throws Exception { | ||
| parts = TokenUtils.splitToken(jwt); | ||
|
|
@@ -55,13 +60,13 @@ public JWTDecoder(String jwt, EncodeType encodeType) throws Exception { | |
| String payloadJson = null; | ||
| switch (encodeType) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if the encode type doesn't match any value defined in the switch?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i provide the values myself, so theres no way it cant be one of the 3 |
||
| case Base16: | ||
| headerJson = URLDecoder.decode(new String(Hex.decodeHex(parts[0])), "UTF-8"); | ||
| payloadJson = URLDecoder.decode(new String(Hex.decodeHex(parts[1])), "UTF-8"); | ||
| headerJson = URLDecoder.decode(new String(Hex.decodeHex(parts[0])), StandardCharsets.UTF_8.name()); | ||
| payloadJson = URLDecoder.decode(new String(Hex.decodeHex(parts[1])), StandardCharsets.UTF_8.name()); | ||
| break; | ||
| case Base32: | ||
| Base32 base32 = new Base32(); | ||
| headerJson = URLDecoder.decode(new String(base32.decode(parts[0]), "UTF-8")); | ||
| payloadJson = URLDecoder.decode(new String(base32.decode(parts[1]), "UTF-8")); | ||
| headerJson = URLDecoder.decode(new String(base32.decode(parts[0]), StandardCharsets.UTF_8.name())); | ||
| payloadJson = URLDecoder.decode(new String(base32.decode(parts[1]), StandardCharsets.UTF_8.name())); | ||
| break; | ||
| case Base64: | ||
| headerJson = StringUtils.newStringUtf8(Base64.decodeBase64(parts[0])); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good point. changed. |
||
|
|
@@ -161,4 +166,39 @@ public String getSignature() { | |
| public String getToken() { | ||
| return String.format("%s.%s.%s", parts[0], parts[1], parts[2]); | ||
| } | ||
|
|
||
| public GoogleOrFbJwtCreator decodeJWT(DecodedJWT jwt) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This class itself is a JWT decoder. It receives the token upon construction, and decodes the values storing the result on final fields. It's intended to use and throw away. What's this method for? By the way: How is that a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it goes back to our discussion during one of our scrum meetings. @lccodes might have more context. |
||
| Map<String, Claim> claims = jwt.getClaims(); | ||
| Claim issuerClaim = claims.get(Claims.ISSUER); | ||
| if(issuerClaim == null) { | ||
| throw new IllegalArgumentException("null issuer claim"); | ||
| } | ||
| String issuer = issuerClaim.asString(); | ||
| GoogleOrFbJwtCreator googleOrFbJwtCreator = null; | ||
| if(Strings.isNullOrEmpty(issuer)) { | ||
| throw new IllegalArgumentException("null or empty issuer"); | ||
| } | ||
| if(ISSUER_FACEBOOK.contains(issuer)) { | ||
| googleOrFbJwtCreator = FbJwtCreator.build() | ||
| .withExp(claims.get(Claims.EXPIRES_AT).asDate()) | ||
| .withIat(claims.get(Claims.ISSUED_AT).asDate()) | ||
| .withAppId(claims.get(Claims.APP_ID).asString()) | ||
| .withUserId(claims.get(Claims.USER_ID).asString()); | ||
| } else if(ISSUER_GOOGLE.contains(issuer)) { | ||
| googleOrFbJwtCreator = GoogleJwtCreator.build() | ||
| .withPicture(claims.get(Claims.PICTURE).asString()) | ||
| .withEmail(claims.get(Claims.EMAIL).asString()) | ||
| .withIssuer(claims.get(Claims.ISSUER).asString()) | ||
| .withSubject(claims.get(Claims.SUBJECT).asString()) | ||
| .withAudience(claims.get(Claims.AUDIENCE).asString()) | ||
| .withExp(claims.get(Claims.EXPIRES_AT).asDate()) | ||
| .withIat(claims.get(Claims.ISSUED_AT).asDate()) | ||
| .withName(claims.get(Claims.NAME).asString()); | ||
| } else { | ||
| throw new IllegalArgumentException("Not from a Facebook or Google issuer"); | ||
| } | ||
|
|
||
| return googleOrFbJwtCreator; | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,9 +25,14 @@ | |
| import com.auth0.jwt.interfaces.DecodedJWT; | ||
| import com.auth0.jwt.interfaces.ECDSAKeyProvider; | ||
| import com.auth0.jwt.interfaces.RSAKeyProvider; | ||
|
|
||
| import java.io.UnsupportedEncodingException; | ||
| import java.security.interfaces.*; | ||
| import java.security.interfaces.ECKey; | ||
| import java.security.interfaces.ECPrivateKey; | ||
| import java.security.interfaces.ECPublicKey; | ||
| import java.security.interfaces.RSAKey; | ||
| import java.security.interfaces.RSAPrivateKey; | ||
| import java.security.interfaces.RSAPublicKey; | ||
|
|
||
|
|
||
| /** | ||
| * The Algorithm class represents an algorithm to be used in the Signing or Verification process of a Token. | ||
|
|
@@ -385,6 +390,15 @@ public String toString() { | |
| */ | ||
| public abstract void verify(DecodedJWT jwt, EncodeType encodeType) throws Exception; | ||
|
|
||
| /** | ||
| * Verify the given token including x509 functionality | ||
| * @param jwt the already decoded JWT that it's going to be verified. | ||
| * @param jwksFile | ||
| * @param pemFile | ||
| * @throws Exception | ||
| */ | ||
| public abstract void verifyWithX509(DecodedJWT jwt, String jwksFile, String pemFile) throws Exception; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think the Also:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think i know what you're getting at, but the issue also comes with JWT.decodeWithX509() which is used primarily for the RSA algorithm and just uses the algorithm attribute. should we have a separate rsaAlgorithm but then how would we know we are trying to sign with rsaAlgorithm? i would appreciate it if we could have a google hangouts meeting to discuss this design. |
||
|
|
||
| /** | ||
| * Sign the given content using this Algorithm instance. | ||
| * | ||
|
|
@@ -406,4 +420,4 @@ public boolean equals(Object algorithmParam) { | |
| Algorithm algorithm = (Algorithm) algorithmParam; | ||
| return this.description.equals(algorithm.description) && this.name.equals(algorithm.name); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,18 +24,16 @@ | |
| import com.auth0.jwt.exceptions.SignatureVerificationException; | ||
| import com.auth0.jwt.interfaces.DecodedJWT; | ||
| import com.auth0.jwt.interfaces.ECDSAKeyProvider; | ||
| import org.apache.commons.codec.binary.Base32; | ||
| import org.apache.commons.codec.binary.Base64; | ||
| import org.apache.commons.codec.binary.Hex; | ||
| import org.apache.commons.codec.binary.StringUtils; | ||
|
|
||
| import java.net.URLDecoder; | ||
| import java.nio.charset.StandardCharsets; | ||
| import java.security.InvalidKeyException; | ||
| import java.security.NoSuchAlgorithmException; | ||
| import java.security.SignatureException; | ||
| import java.security.interfaces.ECPrivateKey; | ||
| import java.security.interfaces.ECPublicKey; | ||
| import org.apache.commons.codec.binary.Base32; | ||
| import org.apache.commons.codec.binary.Base64; | ||
| import org.apache.commons.codec.binary.Hex; | ||
|
|
||
| class ECDSAAlgorithm extends Algorithm { | ||
|
|
||
|
|
@@ -66,12 +64,12 @@ public void verify(DecodedJWT jwt, EncodeType encodeType) throws Exception { | |
| String urlDecoded = null; | ||
| switch (encodeType) { | ||
| case Base16: | ||
| urlDecoded = URLDecoder.decode(signature, "UTF-8"); | ||
| urlDecoded = URLDecoder.decode(signature, StandardCharsets.UTF_8.name()); | ||
| signatureBytes = Hex.decodeHex(urlDecoded); | ||
| break; | ||
| case Base32: | ||
| Base32 base32 = new Base32(); | ||
| urlDecoded = URLDecoder.decode(signature, "UTF-8"); | ||
| urlDecoded = URLDecoder.decode(signature, StandardCharsets.UTF_8.name()); | ||
| signatureBytes = base32.decode(urlDecoded); | ||
| break; | ||
| case Base64: | ||
|
|
@@ -94,6 +92,11 @@ public void verify(DecodedJWT jwt, EncodeType encodeType) throws Exception { | |
| } | ||
| } | ||
|
|
||
| @Override | ||
| public void verifyWithX509(DecodedJWT jwt, String jwksFile, String pemFile) throws Exception { | ||
| throw new UnsupportedOperationException("X509 is not supported for ECDSA algorithm"); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Things like this point you that this class shouldn't have this knowledge.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. comes back to the fact that they all extend Algorithm and the design issue up above. lets discuss. |
||
| } | ||
|
|
||
| @Override | ||
| public byte[] sign(byte[] contentBytes) throws SignatureGenerationException { | ||
| try { | ||
|
|
@@ -241,6 +244,7 @@ static ECDSAKeyProvider providerForKeys(final ECPublicKey publicKey, final ECPri | |
| if (publicKey == null && privateKey == null) { | ||
| throw new IllegalArgumentException("Both provided Keys cannot be null."); | ||
| } | ||
|
|
||
| return new ECDSAKeyProvider() { | ||
| @Override | ||
| public ECPublicKey getPublicKeyById(String keyId) { | ||
|
|
@@ -258,4 +262,4 @@ public String getPrivateKeyId() { | |
| } | ||
| }; | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels wrong that a jwt library is using another jwt library. Where is this used for and can it be coded in this same library rather than depending on them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
its for the PemReader object used in these 2 lines: