All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.api.client.json.webtoken.JsonWebSignature Maven / Gradle / Ivy

Go to download

Google HTTP Client Library for Java. Functionality that works on all supported Java platforms, including Java 5 (or higher) desktop (SE) and web (EE), Android, and Google App Engine.

The newest version!
/*
 * Copyright 2012 Google LLC.
 *
 * Licensed 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 com.google.api.client.json.webtoken;

import com.google.api.client.json.JsonFactory;
import com.google.api.client.util.Base64;
import com.google.api.client.util.Beta;
import com.google.api.client.util.Key;
import com.google.api.client.util.Preconditions;
import com.google.api.client.util.SecurityUtils;
import com.google.api.client.util.StringUtils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

/**
 * JSON Web Signature(JWS).
 *
 * 

Sample usage: * *

 * public static void printPayload(JsonFactory jsonFactory, String tokenString) throws IOException {
 *   JsonWebSignature jws = JsonWebSignature.parse(jsonFactory, tokenString);
 *   System.out.println(jws.getPayload());
 * }
 * 
* *

Implementation is not thread-safe. * * @since 1.14 (since 1.7 as com.google.api.client.auth.jsontoken.JsonWebSignature) * @author Yaniv Inbar */ public class JsonWebSignature extends JsonWebToken { /** Bytes of the signature. */ private final byte[] signatureBytes; /** Bytes of the signature content. */ private final byte[] signedContentBytes; /** * @param header header * @param payload payload * @param signatureBytes bytes of the signature * @param signedContentBytes bytes of the signed content */ public JsonWebSignature( Header header, Payload payload, byte[] signatureBytes, byte[] signedContentBytes) { super(header, payload); this.signatureBytes = Preconditions.checkNotNull(signatureBytes); this.signedContentBytes = Preconditions.checkNotNull(signedContentBytes); } /** * Header as specified in Reserved * Header Parameter Names. */ public static class Header extends JsonWebToken.Header { /** * Algorithm header parameter that identifies the cryptographic algorithm used to secure the JWS * or {@code null} for none. */ @Key("alg") private String algorithm; /** * JSON Web Key URL header parameter that is an absolute URL that refers to a resource for a set * of JSON-encoded public keys, one of which corresponds to the key that was used to digitally * sign the JWS or {@code null} for none. */ @Key("jku") private String jwkUrl; /** * JSON Web Key header parameter that is a public key that corresponds to the key used to * digitally sign the JWS or {@code null} for none. */ @Key("jwk") private String jwk; /** * Key ID header parameter that is a hint indicating which specific key owned by the signer * should be used to validate the digital signature or {@code null} for none. */ @Key("kid") private String keyId; /** * X.509 URL header parameter that is an absolute URL that refers to a resource for the X.509 * public key certificate or certificate chain corresponding to the key used to digitally sign * the JWS or {@code null} for none. */ @Key("x5u") private String x509Url; /** * X.509 certificate thumbprint header parameter that provides a base64url encoded SHA-1 * thumbprint (a.k.a. digest) of the DER encoding of an X.509 certificate that can be used to * match the certificate or {@code null} for none. */ @Key("x5t") private String x509Thumbprint; /** * X.509 certificate chain header parameter contains the X.509 public key certificate or * certificate chain corresponding to the key used to digitally sign the JWS or {@code null} for * none. * * @since 1.19.1. */ @Key("x5c") private ArrayList x509Certificates; /** * Array listing the header parameter names that define extensions that are used in the JWS * header that MUST be understood and processed or {@code null} for none. */ @Key("crit") private List critical; @Override public Header setType(String type) { super.setType(type); return this; } /** * Returns the algorithm header parameter that identifies the cryptographic algorithm used to * secure the JWS or {@code null} for none. */ public final String getAlgorithm() { return algorithm; } /** * Sets the algorithm header parameter that identifies the cryptographic algorithm used to * secure the JWS or {@code null} for none. * *

Overriding is only supported for the purpose of calling the super implementation and * changing the return type, but nothing else. */ public Header setAlgorithm(String algorithm) { this.algorithm = algorithm; return this; } /** * Returns the JSON Web Key URL header parameter that is an absolute URL that refers to a * resource for a set of JSON-encoded public keys, one of which corresponds to the key that was * used to digitally sign the JWS or {@code null} for none. */ public final String getJwkUrl() { return jwkUrl; } /** * Sets the JSON Web Key URL header parameter that is an absolute URL that refers to a resource * for a set of JSON-encoded public keys, one of which corresponds to the key that was used to * digitally sign the JWS or {@code null} for none. * *

Overriding is only supported for the purpose of calling the super implementation and * changing the return type, but nothing else. */ public Header setJwkUrl(String jwkUrl) { this.jwkUrl = jwkUrl; return this; } /** * Returns the JSON Web Key header parameter that is a public key that corresponds to the key * used to digitally sign the JWS or {@code null} for none. */ public final String getJwk() { return jwk; } /** * Sets the JSON Web Key header parameter that is a public key that corresponds to the key used * to digitally sign the JWS or {@code null} for none. * *

Overriding is only supported for the purpose of calling the super implementation and * changing the return type, but nothing else. */ public Header setJwk(String jwk) { this.jwk = jwk; return this; } /** * Returns the key ID header parameter that is a hint indicating which specific key owned by the * signer should be used to validate the digital signature or {@code null} for none. */ public final String getKeyId() { return keyId; } /** * Sets the key ID header parameter that is a hint indicating which specific key owned by the * signer should be used to validate the digital signature or {@code null} for none. * *

Overriding is only supported for the purpose of calling the super implementation and * changing the return type, but nothing else. */ public Header setKeyId(String keyId) { this.keyId = keyId; return this; } /** * Returns the X.509 URL header parameter that is an absolute URL that refers to a resource for * the X.509 public key certificate or certificate chain corresponding to the key used to * digitally sign the JWS or {@code null} for none. */ public final String getX509Url() { return x509Url; } /** * Sets the X.509 URL header parameter that is an absolute URL that refers to a resource for the * X.509 public key certificate or certificate chain corresponding to the key used to digitally * sign the JWS or {@code null} for none. * *

Overriding is only supported for the purpose of calling the super implementation and * changing the return type, but nothing else. */ public Header setX509Url(String x509Url) { this.x509Url = x509Url; return this; } /** * Returns the X.509 certificate thumbprint header parameter that provides a base64url encoded * SHA-1 thumbprint (a.k.a. digest) of the DER encoding of an X.509 certificate that can be used * to match the certificate or {@code null} for none. */ public final String getX509Thumbprint() { return x509Thumbprint; } /** * Sets the X.509 certificate thumbprint header parameter that provides a base64url encoded * SHA-1 thumbprint (a.k.a. digest) of the DER encoding of an X.509 certificate that can be used * to match the certificate or {@code null} for none. * *

Overriding is only supported for the purpose of calling the super implementation and * changing the return type, but nothing else. */ public Header setX509Thumbprint(String x509Thumbprint) { this.x509Thumbprint = x509Thumbprint; return this; } /** * Returns the X.509 certificate chain header parameter contains the X.509 public key * certificate or certificate chain corresponding to the key used to digitally sign the JWS or * {@code null} for none. * * @since 1.19.1. */ public final List getX509Certificates() { return new ArrayList<>(x509Certificates); } /** * Sets the X.509 certificate chain header parameter contains the X.509 public key certificate * or certificate chain corresponding to the key used to digitally sign the JWS or {@code null} * for none. * *

Overriding is only supported for the purpose of calling the super implementation and * changing the return type, but nothing else. * * @since 1.19.1. */ public Header setX509Certificates(List x509Certificates) { this.x509Certificates = new ArrayList<>(x509Certificates); return this; } /** * Returns an array listing the header parameter names that define extensions used in the JWS * header that MUST be understood and processed or {@code null} for none. * * @since 1.16 */ public final List getCritical() { if (critical == null || critical.isEmpty()) { return null; } return new ArrayList<>(critical); } /** * Sets the header parameter names that define extensions used in the JWS header that MUST be * understood and processed or {@code null} for none. * *

Overriding is only supported for the purpose of calling the super implementation and * changing the return type, but nothing else. * * @since 1.16 */ public Header setCritical(List critical) { this.critical = new ArrayList<>(critical); return this; } @Override public Header set(String fieldName, Object value) { return (Header) super.set(fieldName, value); } @Override public Header clone() { return (Header) super.clone(); } } @Override public Header getHeader() { return (Header) super.getHeader(); } /** * Verifies the signature of the content. * *

Currently only {@code "RS256"} and {@code "ES256"} algorithms are verified, but others may * be added in the future. For any other algorithm it returns {@code false}. * * @param publicKey public key * @return whether the algorithm is recognized and it is verified * @throws GeneralSecurityException */ public final boolean verifySignature(PublicKey publicKey) throws GeneralSecurityException { String algorithm = getHeader().getAlgorithm(); if ("RS256".equals(algorithm)) { return SecurityUtils.verify( SecurityUtils.getSha256WithRsaSignatureAlgorithm(), publicKey, signatureBytes, signedContentBytes); } else if ("ES256".equals(algorithm)) { return SecurityUtils.verify( SecurityUtils.getEs256SignatureAlgorithm(), publicKey, DerEncoder.encode(signatureBytes), signedContentBytes); } else { return false; } } /** * {@link Beta}
* Verifies the signature of the content using the certificate chain embedded in the signature. * *

Currently only {@code "RS256"} and {@code "ES256"} algorithms are verified, but others may * be added in the future. For any other algorithm it returns {@code null}. * *

The leaf certificate of the certificate chain must be an SSL server certificate. * * @param trustManager trust manager used to verify the X509 certificate chain embedded in this * message * @return the signature certificate if the signature could be verified, null otherwise * @throws GeneralSecurityException * @since 1.19.1 */ @Beta public final X509Certificate verifySignature(X509TrustManager trustManager) throws GeneralSecurityException { List x509Certificates = getHeader().getX509Certificates(); if (x509Certificates == null || x509Certificates.isEmpty()) { return null; } String algorithm = getHeader().getAlgorithm(); if ("RS256".equals(algorithm)) { return SecurityUtils.verify( SecurityUtils.getSha256WithRsaSignatureAlgorithm(), trustManager, x509Certificates, signatureBytes, signedContentBytes); } else if ("ES256".equals(algorithm)) { return SecurityUtils.verify( SecurityUtils.getEs256SignatureAlgorithm(), trustManager, x509Certificates, DerEncoder.encode(signatureBytes), signedContentBytes); } else { return null; } } /** * {@link Beta}
* Verifies the signature of the content using the certificate chain embedded in the signature. * *

Currently only {@code "RS256"} algorithm is verified, but others may be added in the future. * For any other algorithm it returns {@code null}. * *

The certificate chain is verified using the system default trust manager. * *

The leaf certificate of the certificate chain must be an SSL server certificate. * * @return the signature certificate if the signature could be verified, null otherwise * @throws GeneralSecurityException * @since 1.19.1. */ @Beta public final X509Certificate verifySignature() throws GeneralSecurityException { X509TrustManager trustManager = getDefaultX509TrustManager(); if (trustManager == null) { return null; } return verifySignature(trustManager); } private static X509TrustManager getDefaultX509TrustManager() { try { TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); factory.init((KeyStore) null); for (TrustManager manager : factory.getTrustManagers()) { if (manager instanceof X509TrustManager) { return (X509TrustManager) manager; } } return null; } catch (NoSuchAlgorithmException e) { return null; } catch (KeyStoreException e) { return null; } } /** Returns the bytes of the signature. */ public final byte[] getSignatureBytes() { return Arrays.copyOf(signatureBytes, signatureBytes.length); } /** Returns the bytes of the signature content. */ public final byte[] getSignedContentBytes() { return Arrays.copyOf(signedContentBytes, signedContentBytes.length); } /** * Parses the given JWS token string and returns the parsed {@link JsonWebSignature}. * * @param jsonFactory JSON factory * @param tokenString JWS token string * @return parsed JWS */ public static JsonWebSignature parse(JsonFactory jsonFactory, String tokenString) throws IOException { return parser(jsonFactory).parse(tokenString); } /** Returns a new instance of a JWS parser. */ public static Parser parser(JsonFactory jsonFactory) { return new Parser(jsonFactory); } /** * JWS parser. * *

Implementation is not thread-safe. */ public static final class Parser { /** JSON factory. */ private final JsonFactory jsonFactory; /** Header class to use for parsing. */ private Class headerClass = Header.class; /** Payload class to use for parsing. */ private Class payloadClass = Payload.class; /** @param jsonFactory JSON factory */ public Parser(JsonFactory jsonFactory) { this.jsonFactory = Preconditions.checkNotNull(jsonFactory); } /** Returns the header class to use for parsing. */ public Class getHeaderClass() { return headerClass; } /** Sets the header class to use for parsing. */ public Parser setHeaderClass(Class headerClass) { this.headerClass = headerClass; return this; } /** Returns the payload class to use for parsing. */ public Class getPayloadClass() { return payloadClass; } /** Sets the payload class to use for parsing. */ public Parser setPayloadClass(Class payloadClass) { this.payloadClass = payloadClass; return this; } /** Returns the JSON factory. */ public JsonFactory getJsonFactory() { return jsonFactory; } /** * Parses a JWS token into a parsed {@link JsonWebSignature}. * * @param tokenString JWS token string * @return parsed {@link JsonWebSignature} */ public JsonWebSignature parse(String tokenString) throws IOException { // split on the dots int firstDot = tokenString.indexOf('.'); Preconditions.checkArgument(firstDot != -1); byte[] headerBytes = Base64.decodeBase64(tokenString.substring(0, firstDot)); int secondDot = tokenString.indexOf('.', firstDot + 1); Preconditions.checkArgument(secondDot != -1); Preconditions.checkArgument(tokenString.indexOf('.', secondDot + 1) == -1); // decode the bytes byte[] payloadBytes = Base64.decodeBase64(tokenString.substring(firstDot + 1, secondDot)); byte[] signatureBytes = Base64.decodeBase64(tokenString.substring(secondDot + 1)); byte[] signedContentBytes = StringUtils.getBytesUtf8(tokenString.substring(0, secondDot)); // parse the header and payload Header header = jsonFactory.fromInputStream(new ByteArrayInputStream(headerBytes), headerClass); Preconditions.checkArgument(header.getAlgorithm() != null); Payload payload = jsonFactory.fromInputStream(new ByteArrayInputStream(payloadBytes), payloadClass); return new JsonWebSignature(header, payload, signatureBytes, signedContentBytes); } } /** * Signs a given JWS header and payload based on the given private key using RSA and SHA-256 as * described in JWS using * RSA SHA-256. * * @param privateKey private key * @param jsonFactory JSON factory * @param header JWS header * @param payload JWS payload * @return signed JWS string * @since 1.14 (since 1.7 as com.google.api.client.auth.jsontoken.RsaSHA256Signer) */ public static String signUsingRsaSha256( PrivateKey privateKey, JsonFactory jsonFactory, JsonWebSignature.Header header, JsonWebToken.Payload payload) throws GeneralSecurityException, IOException { String content = Base64.encodeBase64URLSafeString(jsonFactory.toByteArray(header)) + "." + Base64.encodeBase64URLSafeString(jsonFactory.toByteArray(payload)); byte[] contentBytes = StringUtils.getBytesUtf8(content); byte[] signature = SecurityUtils.sign( SecurityUtils.getSha256WithRsaSignatureAlgorithm(), privateKey, contentBytes); return content + "." + Base64.encodeBase64URLSafeString(signature); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy