├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.txt ├── NOTICE ├── README.md ├── RELEASING.md ├── apache-http-client ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── joyent │ │ └── http │ │ └── signature │ │ └── apache │ │ └── httpclient │ │ ├── HttpSignatureAuthScheme.java │ │ ├── HttpSignatureAuthSchemeProvider.java │ │ ├── HttpSignatureAuthSchemeProviderLookup.java │ │ ├── HttpSignatureAuthenticationStrategy.java │ │ ├── HttpSignatureCache.java │ │ ├── HttpSignatureConfigurator.java │ │ ├── HttpSignatureRequestInterceptor.java │ │ └── package-info.java │ └── test │ ├── java │ └── com │ │ └── joyent │ │ └── http │ │ └── signature │ │ └── apache │ │ └── httpclient │ │ ├── HttpSignatureAuthIT.java │ │ └── HttpSignatureRequestInterceptorTest.java │ └── resources │ ├── logback-test.xml │ └── testng.xml ├── checkstyle.xml ├── common ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── joyent │ │ └── http │ │ └── signature │ │ ├── CryptoException.java │ │ ├── HttpSignatureException.java │ │ ├── KeyFingerprinter.java │ │ ├── KeyLoadException.java │ │ ├── KeyPairLoader.java │ │ ├── Signer.java │ │ ├── ThreadLocalClearException.java │ │ ├── ThreadLocalSigner.java │ │ ├── crypto │ │ ├── JceDigest.java │ │ ├── MantaNativeRSACoreEngine.java │ │ ├── NativeRSABlindedEngine.java │ │ ├── NativeRSAProvider.java │ │ ├── NativeRSAWithSHA.java │ │ ├── NssBridgeKeyConverter.java │ │ └── package-info.java │ │ └── package-info.java │ └── test │ ├── java │ └── com │ │ └── joyent │ │ └── http │ │ └── signature │ │ ├── KeyFingerprinterIntegrationCycle.java │ │ ├── KeyFingerprinterTest.java │ │ ├── KeyPairLoaderTest.java │ │ ├── SignerDSATest.java │ │ ├── SignerECDSATest.java │ │ ├── SignerLegacyConstructorTest.java │ │ ├── SignerRSATest.java │ │ ├── SignerTest.java │ │ └── SignerTestUtil.java │ └── resources │ ├── keys │ ├── dsa │ │ ├── id_dsa_1024 │ │ └── id_dsa_1024.pub │ ├── ecdsa │ │ ├── ecdsa_256 │ │ ├── ecdsa_256.pub │ │ ├── ecdsa_384 │ │ ├── ecdsa_384.pub │ │ ├── ecdsa_521 │ │ └── ecdsa_521.pub │ └── rsa │ │ ├── id_rsa_1024 │ │ ├── id_rsa_1024.pub │ │ ├── id_rsa_2048 │ │ ├── id_rsa_2048.pub │ │ ├── id_rsa_3072 │ │ ├── id_rsa_3072.pub │ │ ├── id_rsa_4096 │ │ └── id_rsa_4096.pub │ └── testng.xml ├── google-http-client ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── joyent │ │ └── http │ │ └── signature │ │ └── google │ │ └── httpclient │ │ ├── RequestHttpSigner.java │ │ └── package-info.java │ └── test │ ├── java │ └── com │ │ └── joyent │ │ └── http │ │ └── signature │ │ └── google │ │ └── httpclient │ │ └── RequestHttpSignerTest.java │ └── resources │ └── testng.xml ├── jaxrs-client ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── joyent │ │ └── http │ │ └── signature │ │ └── jaxrs │ │ └── client │ │ ├── SignedRequestClientRequestFilter.java │ │ └── package-info.java │ └── test │ ├── java │ └── com │ │ └── joyent │ │ └── http │ │ └── signature │ │ └── jaxrs │ │ └── client │ │ ├── SignedRequestClientRequestFilterIT.java │ │ └── testapp │ │ ├── TestApplication.java │ │ └── TestResource.java │ └── resources │ ├── arquillian.xml │ ├── logback-test.xml │ └── testng-it.xml ├── license-history.txt ├── microbench ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── joyent │ └── http │ └── signature │ ├── BenchmarkDSASigner.java │ ├── BenchmarkECDSASigner.java │ ├── BenchmarkKeyFingerprinter.java │ ├── BenchmarkRSASigner.java │ ├── BenchmarkSigner.java │ ├── DefaultSignDateAsStringBenchmark.java │ └── package-info.java ├── pom.xml └── suppressions.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### Intellij ### 4 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 5 | 6 | *.iml 7 | 8 | ## Directory-based project format: 9 | .idea/ 10 | # if you remove the above rule, at least ignore the following: 11 | 12 | # User-specific stuff: 13 | # .idea/workspace.xml 14 | # .idea/tasks.xml 15 | # .idea/dictionaries 16 | 17 | # Sensitive or high-churn files: 18 | # .idea/dataSources.ids 19 | # .idea/dataSources.xml 20 | # .idea/sqlDataSources.xml 21 | # .idea/dynamic.xml 22 | # .idea/uiDesigner.xml 23 | 24 | # Gradle: 25 | # .idea/gradle.xml 26 | # .idea/libraries 27 | 28 | # Mongo Explorer plugin: 29 | # .idea/mongoSettings.xml 30 | 31 | ## File-based project format: 32 | *.ipr 33 | *.iws 34 | 35 | ## Plugin-specific files: 36 | 37 | # IntelliJ 38 | out/ 39 | 40 | # mpeltonen/sbt-idea plugin 41 | .idea_modules/ 42 | 43 | # JIRA plugin 44 | atlassian-ide-plugin.xml 45 | 46 | # Crashlytics plugin (for Android Studio and IntelliJ) 47 | com_crashlytics_export_strings.xml 48 | crashlytics.properties 49 | crashlytics-build.properties 50 | 51 | 52 | ### Eclipse ### 53 | *.pydevproject 54 | .metadata 55 | .gradle 56 | bin/ 57 | tmp/ 58 | *.tmp 59 | *.bak 60 | *.swp 61 | *~.nib 62 | local.properties 63 | .settings/ 64 | .loadpath 65 | 66 | # External tool builders 67 | .externalToolBuilders/ 68 | 69 | # Locally stored "Eclipse launch configurations" 70 | *.launch 71 | 72 | # CDT-specific 73 | .cproject 74 | 75 | # PDT-specific 76 | .buildpath 77 | 78 | # sbteclipse plugin 79 | .target 80 | 81 | # TeXlipse plugin 82 | .texlipse 83 | 84 | # OS X 85 | .DS_Store 86 | 87 | ### Maven ### 88 | target/ 89 | pom.xml.tag 90 | pom.xml.releaseBackup 91 | pom.xml.versionsBackup 92 | pom.xml.next 93 | release.properties 94 | dependency-reduced-pom.xml 95 | 96 | common/test-output 97 | google-http-client/test-output 98 | jaxrs-client/test-output 99 | jaxrs-client/derby.log 100 | jaxrs-client/test-output 101 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | language: java 3 | # We now cache the Maven dependencies directory for faster builds 4 | cache: 5 | directories: 6 | - $HOME/.m2 7 | sudo: false 8 | 9 | before_install: 10 | - echo 'MAVEN_OPTS="-Dorg.slf4j.simpleLogger.defaultLogLevel=warn"' >~/.mavenrc 11 | 12 | matrix: 13 | fast_finish: true 14 | include: 15 | # checkstyle 16 | - jdk: oraclejdk8 17 | env: 18 | - DESC="checkstyle" 19 | - CMD="mvn clean checkstyle:check" 20 | # unit tests (oraclejdk8) 21 | - jdk: oraclejdk8 22 | env: 23 | - DESC="oraclejdk8 unit tests" 24 | - CMD="mvn clean test -Dcheckstyle.skip=true" 25 | # unit tests (openjdk8) 26 | - jdk: openjdk8 27 | env: 28 | - DESC="openjdk8 unit tests" 29 | - CMD="mvn clean test -Dcheckstyle.skip=true" 30 | - LANG=en_US.utf8 31 | 32 | script: echo ${CMD}; ${CMD} 33 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We love pull requests from everyone. 4 | 5 | Fork, then clone the repo: 6 | 7 | git clone git@github.com:your-username/joyent/java-http-signature.git 8 | 9 | Make sure all tests including the integration tests pass: 10 | 11 | mvn verify 12 | 13 | Make your change. Add tests for your change. Make sure that all tests and style 14 | checks pass: 15 | 16 | mvn checkstyle:checkstyle -Dcheckstyle.skip=false verify 17 | 18 | Add your changes to the CHANGELOG.md and commit. 19 | 20 | Push to your fork and [submit a pull request][pr]. 21 | 22 | [pr]: https://github.com/joyent/java-http-signature/compare/ 23 | 24 | At this point you're waiting on us. We like to at least comment on pull requests 25 | within three business days (and, typically, one business day). We may suggest 26 | some changes or improvements or alternatives. 27 | 28 | Some things that will increase the chance that your pull request is accepted: 29 | 30 | * Filing a github issue describing the improvement or the bug before you start work. 31 | * Write tests. 32 | * Follow the style defined in (checkstyle.xml)[checkstyle.xml]. 33 | * Write a good commit message. 34 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | The product includes software from the Legion of the Bouncy Castle project 2 | under the MIT License (see: com.joyent.http.signature.crypto.MantaNativeRSACoreEngine). 3 | 4 | The product includes software from the JNA GMP project 5 | under the Apache License 2.0 (see: com.joyent.http.signature.crypto.NativeRSABlindedEngine). 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/joyent/java-http-signature.svg?branch=master)](https://travis-ci.org/joyent/java-http-signature) 2 | 3 | # Java HTTP Signature Utilities 4 | 5 | [java-http-signature](https://github.com/joyent/java-http-signature) is a 6 | community maintained set of utilities for making HTTP Signature requests against 7 | the [Joyent Public Cloud](http://www.joyent.com). 8 | 9 | This project is a fork of the code that once existed as part of the 10 | [Java Manta SDK](http://joyent.github.com/java-manta). Currently, this project 11 | interacts directly with [Bouncy Castle](https://www.bouncycastle.org/) to create 12 | HTTP Signatures. In the future, we may use a project like 13 | [httpsig-java](https://github.com/adamcin/httpsig-java) 14 | or [http-signatures-java](https://github.com/tomitribe/http-signatures-java) to 15 | do the signing. 16 | 17 | ## Installation 18 | 19 | ### Requirements 20 | * [Java 1.8](http://www.oracle.com/technetwork/java/javase/downloads/index.html) or higher. 21 | * [Maven 3.3](https://maven.apache.org/) 22 | 23 | ## Using Maven 24 | Add the latest dependency to your Maven `pom.xml`. 25 | 26 | For Apache HTTP Client AuthScheme support: 27 | ```xml 28 | 29 | com.joyent.http-signature 30 | apache-http-client-signature 31 | LATEST 32 | 33 | ``` 34 | 35 | For Google HTTP Client support: 36 | ```xml 37 | 38 | com.joyent.http-signature 39 | google-http-client-signature 40 | LATEST 41 | 42 | ``` 43 | 44 | For JAX-RS Client support: 45 | ```xml 46 | 47 | com.joyent.http-signature 48 | jaxrs-client-signature 49 | LATEST 50 | 51 | ``` 52 | 53 | ### From Source 54 | If you prefer to build from source, you'll also need 55 | [Maven](https://maven.apache.org/), and then invoke: 56 | 57 | ``` bash 58 | # mvn package 59 | ``` 60 | 61 | ## Usage 62 | 63 | ### Thread Safety Warning 64 | 65 | The Java Cryptographic Extensions Signature class is not thread safe, 66 | but it is entirely likely that you will want to use multiple threads 67 | to generate HTTP signatures. You can solve this problem by using the 68 | included `ThreadLocalSigner` class. However, this class has the limitation 69 | of storing one Signer class per invoking thread. Be very careful that 70 | you properly shut down your threads and do not accidentally create a 71 | memory leak. To nuke all of the thread references, you can call the 72 | `clearAll()` method on `ThreadLocalSigner`. 73 | 74 | The `ThreadLocal` approach is used by default in the `jaxrs-client`, 75 | the `google-http-client` and the `apache-http-client` modules. 76 | 77 | ### Google HTTP Client Integration 78 | 79 | You will need to create a HttpSigner object and then use that object as part 80 | of an Interceptor to sign the request object. For example: 81 | 82 | ```java 83 | public static HttpRequestFactory buildRequestFactory() { 84 | String keyPath = "/path/to/my/rsa/key"; 85 | String login = "account_name"; 86 | String fingerprint = "b2:b2:b2:b2:b2:b2:b2:b2:f7:f7:f7:f7:f7:f7:f7:f7"; 87 | HttpSignerUtils.getKeyPair(new File(keyPath).toPath()); 88 | HttpSigner signer = new HttpSigner(keyPair, login, fingerprint); 89 | 90 | HttpExecuteInterceptor signingInterceptor = new HttpExecuteInterceptor() { 91 | @Override 92 | public void intercept(final HttpRequest request) throws IOException { 93 | httpSigner.signRequest(request); 94 | } 95 | }; 96 | 97 | HttpRequestInitializer initializer = new HttpRequestInitializer() { 98 | @Override 99 | public void initialize(final HttpRequest request) throws IOException { 100 | request.setInterceptor(signingInterceptor); 101 | request.setParser(new JsonObjectParser(JSON_FACTORY)); 102 | } 103 | }; 104 | 105 | HttpTransport transport = new NetHttpTransport(); 106 | 107 | return transport.createRequestFactory(initializer); 108 | } 109 | ``` 110 | 111 | ### JAX-RS Client Integration 112 | 113 | To use the JAX-RS Client integration, instantiate a `SignedRequestClientRequestFilter` with 114 | the proper credentials, then register this instance with the JAX-RS Client. 115 | For example: 116 | 117 | ```java 118 | String keyPath = "/path/to/my/rsa/key"; 119 | String login = "account_name"; 120 | String fingerprint = "b2:b2:b2:b2:b2:b2:b2:b2:f7:f7:f7:f7:f7:f7:f7:f7"; 121 | final SignedRequestClientRequestFilter signedRequestClientRequestFilter = new SignedRequestClientRequestFilter( 122 | login, 123 | fingerprint, 124 | keyPath 125 | ); 126 | 127 | Response response = ClientBuilder.newClient() 128 | .register(signedRequestClientRequestFilter) 129 | .target(endpointBaseUrl.toURI()) 130 | .request(MediaType.APPLICATION_JSON_TYPE) 131 | .get(); 132 | ``` 133 | 134 | ## Contributions 135 | 136 | Contributions welcome! Please read the [CONTRIBUTING.md](CONTRIBUTING.md) document for details 137 | on getting started. 138 | 139 | ### Releasing 140 | 141 | Please refer to the [release documentation](RELEASING.md). 142 | 143 | ### Bugs 144 | 145 | See . 146 | 147 | ## License 148 | Java HTTP Signatures is licensed under the MPLv2. Please see the 149 | [LICENSE.txt](LICENSE.txt) file for more details. 150 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | ### Releasing 2 | 3 | In order to release to [Maven central](https://search.maven.org/), you will need [an account] (https://issues.sonatype.org) with [Sonatype OSSRH](http://central.sonatype.org/pages/ossrh-guide.html). 4 | If you do not already have an account, you can click the signup link from the login screen 5 | to begin the process of registering for an account. After signing up, you will need to add 6 | your sonatype credentials to your your maven settings file. By default this settings file is 7 | located at `$HOME/.m2/settings.xml`. In addition to sonatype credentials, you will 8 | also need to add a [gpg signing](https://maven.apache.org/plugins/maven-gpg-plugin/sign-mojo.html) key configuration. 9 | 10 | For the security conscious, a [guide to encrypting credentials in maven settings files](https://maven.apache.org/guides/mini/guide-encryption.html) exists to 11 | illustrate how credentials can be protected. 12 | 13 | The following is an example settings.xml file: 14 | 15 | ```xml 16 | 20 | 21 | 22 | gpg 23 | 24 | 25 | gpg 26 | keyname 27 | passphrase 28 | ${env.HOME}/.gnupg/secring.gpg 29 | 30 | 31 | 32 | 33 | 34 | ossrh 35 | username 36 | password 37 | 38 | 39 | 40 | ``` 41 | 42 | To perform a release: 43 | 44 | 1. Make sure the source builds, test suites pass, and the source and java artifacts can 45 | be generated and signed: 46 | `mvn clean verify -Prelease` 47 | 2. Start from a clean working directory and make sure you have no modified 48 | files in your workspace: 49 | `mvn clean && git status` 50 | 3. Prepare the release: 51 | 4. `mvn release:clean release:prepare` 52 | 4. Enter the version to be associated with this release. 53 | You should be prompted for this version number, and the default assumed version 54 | will be shown and should correspond to the version that was in the pom.xml 55 | file but WITHOUT the `-SNAPSHOT` suffix. 56 | 5. Enter the SCM tag to be used to mark this commit in the SCM. 57 | You should be prompted for this tag name, and the default will be 58 | `{projectName}-{releaseVersion}` 59 | 6. Enter the new development version. 60 | You should be prompted for this version number, and the default for this will 61 | be an incremented version number of the release followed by a `-SNAPSHOT` 62 | suffix. 63 | 64 | At this point 65 | * The release plugin will continue and build/package the artifacts. 66 | * The pom.xml file will be updated to reflect the version change to the release 67 | version. 68 | * The new pom.xml will be committed. 69 | * The new commit will be tagged. 70 | * The pom.xml file will be updated again with the new development version. 71 | * Then this new pom.xml will be committed. 72 | 73 | If the process fails for some reason during any of these points, you can invoke 74 | `mvn release:rollback` to go back to the preparation point and try again, but 75 | you will also have to revert any SCM commits that were done 76 | (`git reset --hard HEAD^1` command works well for this) as well as remove any 77 | tags that were created (`git tag -l && git tag -d ` commands help 78 | with this). 79 | 80 | 7. Push tags to github: 81 | `git push --follow-tags` 82 | In order for the `release:perform` goal to complete successfully, you will need to 83 | push the tags created by the maven release plugin to the remote git server. 84 | 85 | 8. Perform the actual release: 86 | `mvn release:perform` 87 | A build will be performed and packaged and artifacts deployed to the sonatype 88 | staging repository. 89 | 90 | 9. Log into the [Sonatype OSSHR Next](https://oss.sonatype.org) web interface 91 | to [verify and promote](http://central.sonatype.org/pages/releasing-the-deployment.html) 92 | the build. 93 | 94 | **NOTE**: By default, these instructions assumes the release is being done from a 95 | branch that can be merged into a primary branch upon successful completion, 96 | and that the SCM operations that are carried out by maven plugins will NOT 97 | access the repo, but rather, work on a local copy instead. The release plugin 98 | as configured in the maven repo sets values for this assumption 99 | (`localCheckout=true` and `pushChanges=false`). 100 | 101 | **NOTE**: If the release is being done in a separate fork of the primary 102 | github repo, doing a merge via pull request will not also copy the tags that 103 | were created during the release process. The tags will have to be created in 104 | the primary repo separately, but this may be preferred anyway. 105 | -------------------------------------------------------------------------------- /apache-http-client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 4.0.0 11 | 12 | 13 | java-http-signature 14 | com.joyent.http-signature 15 | 4.1.3-SNAPSHOT 16 | 17 | 18 | apache-http-client-signature 19 | jar 20 | 21 | 22 | 23 | 4.1.3-SNAPSHOT 24 | 25 | 26 | 27 | 28 | com.joyent.http-signature 29 | http-signature-common 30 | ${dependency.http-signature-common.version} 31 | 32 | 33 | com.joyent.http-signature 34 | http-signature-common 35 | ${dependency.http-signature-common.version} 36 | test-jar 37 | test 38 | 39 | 40 | org.apache.httpcomponents 41 | httpclient 42 | ${dependency.apache-httpclient.version} 43 | 44 | 45 | 46 | 47 | org.slf4j 48 | jcl-over-slf4j 49 | ${dependency.slfj.version} 50 | 51 | 52 | org.slf4j 53 | slf4j-api 54 | 55 | 56 | 57 | 58 | 59 | 60 | ch.qos.logback 61 | logback-classic 62 | ${dependency.logback.version} 63 | test 64 | 65 | 66 | org.slf4j 67 | slf4j-api 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-surefire-plugin 78 | 79 | ${maven.test.skip} 80 | 81 | src/test/resources/testng.xml 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /apache-http-client/src/main/java/com/joyent/http/signature/apache/httpclient/HttpSignatureAuthScheme.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature.apache.httpclient; 9 | 10 | import com.joyent.http.signature.Signer; 11 | import com.joyent.http.signature.ThreadLocalSigner; 12 | import org.apache.commons.logging.Log; 13 | import org.apache.commons.logging.LogFactory; 14 | import org.apache.http.Header; 15 | import org.apache.http.HttpHeaders; 16 | import org.apache.http.HttpRequest; 17 | import org.apache.http.auth.AuthenticationException; 18 | import org.apache.http.auth.ContextAwareAuthScheme; 19 | import org.apache.http.auth.Credentials; 20 | import org.apache.http.auth.MalformedChallengeException; 21 | import org.apache.http.message.BasicHeader; 22 | import org.apache.http.protocol.HttpContext; 23 | 24 | import java.security.KeyPair; 25 | import java.util.Locale; 26 | import java.util.concurrent.ConcurrentHashMap; 27 | import java.util.concurrent.ConcurrentMap; 28 | import java.util.function.Function; 29 | 30 | /** 31 | * Apache HTTP Client plugin that allows for HTTP Signature based authentication. 32 | * 33 | * @author Elijah Zupancic 34 | * @since 1.0.0 35 | */ 36 | @SuppressWarnings({"deprecation", "checkstyle:javadocmethod", "checkstyle:javadoctype", 37 | "checkstyle:javadocvariable"}) 38 | public class HttpSignatureAuthScheme implements ContextAwareAuthScheme { 39 | /** 40 | * Name of authentication scheme. 41 | */ 42 | public static final String SCHEME_NAME = "Signatures"; 43 | 44 | /** 45 | * The static logger instance. 46 | */ 47 | private static final Log LOG = LogFactory.getLog(HttpSignatureAuthScheme.class); 48 | 49 | /** 50 | * Anonymous function class that creates new cache instances based 51 | * on the passed credential. 52 | */ 53 | private static final Function 54 | NEW_CACHE_FUNCTION = HttpSignatureCache::new; 55 | 56 | /** 57 | * Keypair used to sign requests. 58 | */ 59 | private final KeyPair keyPair; 60 | 61 | /** 62 | * Thread local instance of {@link Signer}. 63 | */ 64 | private final ThreadLocalSigner signer; 65 | 66 | /** 67 | * Map of credentials to cache object used for looking up cached signatures. 68 | */ 69 | private ConcurrentMap signatureCacheMap = 70 | new ConcurrentHashMap<>(); 71 | 72 | 73 | /** 74 | * Creates a new instance allowing for HTTP signing with default 75 | * settings. An internal {@link 76 | * com.joyent.http.signature.ThreadLocalSigner} instance will be 77 | * created. 78 | * 79 | * @param keyPair Public/private keypair object used to sign HTTP requests. 80 | */ 81 | public HttpSignatureAuthScheme(final KeyPair keyPair) { 82 | this(keyPair, true); 83 | } 84 | 85 | 86 | /** 87 | * Creates a new instance allowing for HTTP signing with default 88 | * settings. An internal {@link 89 | * com.joyent.http.signature.ThreadLocalSigner} instance will be 90 | * created. 91 | * 92 | * @param keyPair Public/private keypair object used to sign HTTP requests. 93 | * @param useNativeCodeToSign true to enable native code acceleration of cryptographic singing 94 | * 95 | * @deprecated Prefer #HttpSignatureAuthScheme(final KeyPair 96 | * keyPair, final Signer) if configuration of Signer algorithm, 97 | * hashes, or providers is required. 98 | */ 99 | @Deprecated 100 | public HttpSignatureAuthScheme(final KeyPair keyPair, final boolean useNativeCodeToSign) { 101 | this(keyPair, new ThreadLocalSigner()); 102 | } 103 | 104 | 105 | /** 106 | * Creates a new instance allowing for HTTP signing. 107 | * 108 | * @param keyPair Public/private keypair object used to sign HTTP requests. 109 | * @param signer {@link 110 | * com.joyent.http.signature.ThreadLocalSigner} to use for all 111 | * signed requests. 112 | */ 113 | public HttpSignatureAuthScheme(final KeyPair keyPair, final ThreadLocalSigner signer) { 114 | if (keyPair == null) { 115 | throw new IllegalArgumentException("KeyPair must be present"); 116 | } 117 | 118 | this.keyPair = keyPair; 119 | this.signer = signer; 120 | } 121 | 122 | 123 | @Override 124 | public void processChallenge(final Header header) throws MalformedChallengeException { 125 | /* We error here because HTTP signature based authentication doesn't 126 | * work on a challenge response model. Even if we get passed a header there 127 | * is no response header available for us to process. */ 128 | 129 | throw new IllegalStateException("No challenge should ever occur"); 130 | } 131 | 132 | @Override 133 | public String getSchemeName() { 134 | return SCHEME_NAME; 135 | } 136 | 137 | @Override 138 | public String getParameter(final String name) { 139 | return null; 140 | } 141 | 142 | @Override 143 | public String getRealm() { 144 | return null; 145 | } 146 | 147 | @Override 148 | public boolean isConnectionBased() { 149 | return false; 150 | } 151 | 152 | @Override 153 | public boolean isComplete() { 154 | return true; 155 | } 156 | 157 | @Override 158 | public Header authenticate(final Credentials credentials, 159 | final HttpRequest request, 160 | final HttpContext context) 161 | throws AuthenticationException { 162 | return signRequestHeader(credentials, request); 163 | } 164 | 165 | @Override 166 | public Header authenticate(final Credentials credentials, 167 | final HttpRequest request) 168 | throws AuthenticationException { 169 | return authenticate(credentials, request, null); 170 | } 171 | 172 | @Override 173 | public String toString() { 174 | return getSchemeName().toUpperCase(Locale.ROOT); 175 | } 176 | 177 | /** 178 | * Signs an {@link HttpRequest} and returns a header with the signed 179 | * authorization value. 180 | * 181 | * @param credentials Credentials containing a username 182 | * @param request The {@link HttpRequest} to sign. 183 | * @return header with signed authorization value 184 | * @throws AuthenticationException If unable to sign the request. 185 | */ 186 | protected Header signRequestHeader(final Credentials credentials, 187 | final HttpRequest request) 188 | throws AuthenticationException { 189 | if (LOG.isDebugEnabled()) { 190 | LOG.debug(String.format("Signing request: %s", request)); 191 | } 192 | 193 | final Header date = request.getFirstHeader(HttpHeaders.DATE); 194 | final String stringDate; 195 | 196 | if (date != null) { 197 | stringDate = date.getValue(); 198 | } else { 199 | stringDate = signer.get().defaultSignDateAsString(); 200 | request.setHeader(HttpHeaders.DATE, stringDate); 201 | } 202 | 203 | // Assure that a cache object is always present for each credential 204 | signatureCacheMap.computeIfAbsent(credentials, NEW_CACHE_FUNCTION); 205 | 206 | final String authz = signatureCacheMap.get(credentials) 207 | .updateAndGetSignature(stringDate, signer.get(), keyPair); 208 | return new BasicHeader(HttpHeaders.AUTHORIZATION, authz); 209 | } 210 | 211 | public ThreadLocalSigner getSigner() { 212 | return signer; 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /apache-http-client/src/main/java/com/joyent/http/signature/apache/httpclient/HttpSignatureAuthSchemeProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature.apache.httpclient; 9 | 10 | import com.joyent.http.signature.ThreadLocalSigner; 11 | import org.apache.http.auth.AuthScheme; 12 | import org.apache.http.auth.AuthSchemeProvider; 13 | import org.apache.http.protocol.HttpContext; 14 | 15 | import java.security.KeyPair; 16 | 17 | /** 18 | * Provides an new instance of {@link HttpSignatureAuthScheme}. 19 | * 20 | * @author Elijah Zupancic 21 | * @since 2.0.3 22 | */ 23 | public class HttpSignatureAuthSchemeProvider implements AuthSchemeProvider { 24 | /** 25 | * Public/private keypair object used to sign HTTP requests. 26 | */ 27 | private final KeyPair keyPair; 28 | 29 | /** 30 | * Deprecated flag that enables native code acceleration of cryptographic singing. 31 | */ 32 | private final boolean useNativeCodeToSign; 33 | 34 | /** 35 | * Fully configured {@code Signer} to pass to instantiated {@code 36 | * AuthScheme}s. 37 | */ 38 | private final ThreadLocalSigner signer; 39 | 40 | /** 41 | * Create a new instance of the provider. 42 | * @param keyPair Public/private keypair object used to sign HTTP requests. 43 | * @param useNativeCodeToSign true to enable native code acceleration of cryptographic singing 44 | * 45 | * @deprecated Prefer {@link #HttpSignatureAuthSchemeProvider(KeyPair, ThreadLocalSigner)} 46 | */ 47 | @Deprecated 48 | public HttpSignatureAuthSchemeProvider(final KeyPair keyPair, 49 | final boolean useNativeCodeToSign) { 50 | this.keyPair = keyPair; 51 | this.useNativeCodeToSign = useNativeCodeToSign; 52 | this.signer = null; 53 | } 54 | 55 | /** 56 | * Create a new instance of the provider. 57 | * @param keyPair Public/private keypair object used to sign HTTP requests. 58 | * @param signer Configured Signer instance 59 | */ 60 | public HttpSignatureAuthSchemeProvider(final KeyPair keyPair, 61 | final ThreadLocalSigner signer) { 62 | this.keyPair = keyPair; 63 | this.useNativeCodeToSign = false; 64 | this.signer = signer; 65 | } 66 | 67 | /** 68 | * Creates an instance of {@link AuthScheme}. 69 | * 70 | * @param context parameter not used 71 | * @return new instance of {@link AuthScheme} 72 | */ 73 | @Override 74 | @SuppressWarnings("deprecation") 75 | public AuthScheme create(final HttpContext context) { 76 | if (signer != null) { 77 | return new HttpSignatureAuthScheme(keyPair, signer); 78 | } else { 79 | return new HttpSignatureAuthScheme(keyPair, useNativeCodeToSign); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /apache-http-client/src/main/java/com/joyent/http/signature/apache/httpclient/HttpSignatureAuthSchemeProviderLookup.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature.apache.httpclient; 9 | 10 | import com.joyent.http.signature.ThreadLocalSigner; 11 | import org.apache.http.auth.AuthSchemeProvider; 12 | import org.apache.http.config.Lookup; 13 | 14 | import java.security.KeyPair; 15 | 16 | /** 17 | * {@link org.apache.http.config.Lookup} implementation that provides a 18 | * default mapping to an HTTP signatures 19 | * {@link org.apache.http.auth.AuthSchemeProvider}. 20 | * 21 | * @author Elijah Zupancic 22 | * @since 2.0.3 23 | */ 24 | public class HttpSignatureAuthSchemeProviderLookup implements Lookup { 25 | /** 26 | * Backing instance of provider used for all lookups. 27 | */ 28 | private final HttpSignatureAuthSchemeProvider authSchemeProvider; 29 | 30 | /** 31 | * Create a new instance of the lookup with the passed provider. 32 | * @param authSchemeProvider provider to use to back lookup calls 33 | */ 34 | public HttpSignatureAuthSchemeProviderLookup( 35 | final HttpSignatureAuthSchemeProvider authSchemeProvider) { 36 | this.authSchemeProvider = authSchemeProvider; 37 | } 38 | 39 | /** 40 | * Create a new instance of the lookup with a new provider setup with the 41 | * passed key. 42 | * @param keyPair Public/private keypair object used to sign HTTP requests. 43 | * @param useNativeCodeToSign true to enable native code acceleration of cryptographic singing 44 | * 45 | * @deprecated Prefer {@link #HttpSignatureAuthSchemeProviderLookup(KeyPair, ThreadLocalSigner)} 46 | */ 47 | @Deprecated 48 | public HttpSignatureAuthSchemeProviderLookup( 49 | final KeyPair keyPair, final boolean useNativeCodeToSign) { 50 | this.authSchemeProvider = new HttpSignatureAuthSchemeProvider( 51 | keyPair, useNativeCodeToSign); 52 | } 53 | 54 | /** 55 | * Create a new instance of the lookup with a new provider setup with the 56 | * passed key. 57 | * 58 | * @param keyPair Public/private keypair object used to sign HTTP requests. 59 | * @param signer Configured Signer instance 60 | */ 61 | public HttpSignatureAuthSchemeProviderLookup( 62 | final KeyPair keyPair, final ThreadLocalSigner signer) { 63 | this.authSchemeProvider = new HttpSignatureAuthSchemeProvider( 64 | keyPair, signer); 65 | } 66 | 67 | @Override 68 | public AuthSchemeProvider lookup(final String name) { 69 | if (name.equalsIgnoreCase(HttpSignatureAuthScheme.SCHEME_NAME)) { 70 | return authSchemeProvider; 71 | } else { 72 | return null; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /apache-http-client/src/main/java/com/joyent/http/signature/apache/httpclient/HttpSignatureAuthenticationStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature.apache.httpclient; 9 | 10 | import org.apache.commons.logging.Log; 11 | import org.apache.commons.logging.LogFactory; 12 | import org.apache.http.Header; 13 | import org.apache.http.HttpHost; 14 | import org.apache.http.HttpResponse; 15 | import org.apache.http.HttpStatus; 16 | import org.apache.http.StatusLine; 17 | import org.apache.http.auth.AuthOption; 18 | import org.apache.http.auth.AuthProtocolState; 19 | import org.apache.http.auth.AuthScheme; 20 | import org.apache.http.auth.AuthSchemeProvider; 21 | import org.apache.http.auth.AuthState; 22 | import org.apache.http.auth.Credentials; 23 | import org.apache.http.auth.MalformedChallengeException; 24 | import org.apache.http.client.AuthCache; 25 | import org.apache.http.client.AuthenticationStrategy; 26 | import org.apache.http.client.protocol.HttpClientContext; 27 | import org.apache.http.config.Lookup; 28 | import org.apache.http.impl.client.BasicAuthCache; 29 | import org.apache.http.protocol.HttpContext; 30 | 31 | import java.util.ArrayDeque; 32 | import java.util.Collections; 33 | import java.util.Map; 34 | import java.util.Objects; 35 | import java.util.Queue; 36 | 37 | /** 38 | * {@link AuthenticationStrategy} implementation that allows the Apache HTTP 39 | * Client to authenticate via the HTTP Signature scheme. 40 | * 41 | * @author Elijah Zupancic 42 | * @since 2.0.3 43 | */ 44 | public class HttpSignatureAuthenticationStrategy implements AuthenticationStrategy { 45 | /** 46 | * The static logger instance. 47 | */ 48 | private static final Log LOG = LogFactory.getLog(HttpSignatureAuthScheme.class); 49 | 50 | /** 51 | * Immutable list of challenges with a single dummy value because for our purposes 52 | * all that matters is that this map is not empty. 53 | */ 54 | private final Map challenges = Collections.singletonMap(null, null); 55 | 56 | /** 57 | * AuthOption that always returns {@link HttpSignatureAuthScheme}. 58 | */ 59 | private final AuthOption authOption; 60 | 61 | /** 62 | * Create a new instance using a provider found via a {@link Lookup}. 63 | * 64 | * @param authSchemeProviderLookup Lookup that will return an {@link AuthScheme} 65 | * when asked for "Signatures" 66 | * @param credentials credentials containing the HTTP Signature username 67 | */ 68 | public HttpSignatureAuthenticationStrategy( 69 | final Lookup authSchemeProviderLookup, 70 | final Credentials credentials) { 71 | this(authSchemeProviderLookup.lookup("Signatures").create(null), credentials); 72 | } 73 | 74 | /** 75 | * Creates a new instance using the passed authentication scheme. 76 | * 77 | * @param authScheme authentication scheme to use to authenticate 78 | * requests (expecting {@link HttpSignatureAuthScheme}) 79 | * @param credentials credentials containing the HTTP Signature username 80 | */ 81 | public HttpSignatureAuthenticationStrategy(final AuthScheme authScheme, 82 | final Credentials credentials) { 83 | this.authOption = new AuthOption(authScheme, credentials); 84 | } 85 | 86 | /** 87 | * Determines if the given HTTP response response represents 88 | * an authentication challenge that was sent back as a result 89 | * of authentication failure. 90 | * 91 | * @param authHost authentication host. 92 | * @param response HTTP response. 93 | * @param context HTTP context. 94 | * @return {@code true} if user authentication is required, 95 | * {@code false} otherwise. 96 | */ 97 | @Override 98 | public boolean isAuthenticationRequested(final HttpHost authHost, 99 | final HttpResponse response, 100 | final HttpContext context) { 101 | final StatusLine line = response.getStatusLine(); 102 | final int code = line.getStatusCode(); 103 | final HttpClientContext clientContext = HttpClientContext.adapt(context); 104 | 105 | final AuthState authState = clientContext.getTargetAuthState(); 106 | final AuthProtocolState authProtocolState = authState.getState(); 107 | 108 | if (code == HttpStatus.SC_UNAUTHORIZED) { 109 | if (authProtocolState.equals(AuthProtocolState.CHALLENGED)) { 110 | clientContext.getTargetAuthState().setState(AuthProtocolState.FAILURE); 111 | authFailed(authHost, authState.getAuthScheme(), context); 112 | } 113 | 114 | return true; 115 | } 116 | 117 | return clientContext.getTargetAuthState() == null; 118 | 119 | } 120 | 121 | @Override 122 | public Map getChallenges(final HttpHost authhost, 123 | final HttpResponse response, 124 | final HttpContext context) 125 | throws MalformedChallengeException { 126 | 127 | /* Unfortunately, we have to abuse the challenge functionality in 128 | * because it won't enabled authentication unless at least a single 129 | * challenge is available. The HTTP response for a HTTP Signatures 130 | * API will never contain a challenge header, so effectively any 131 | * headers that are added will never match. */ 132 | 133 | return this.challenges; 134 | } 135 | 136 | @Override 137 | public Queue select(final Map challengeHeaders, 138 | final HttpHost authhost, 139 | final HttpResponse response, 140 | final HttpContext context) { 141 | final HttpClientContext httpClientContext = HttpClientContext.adapt(context); 142 | final AuthState state = httpClientContext.getTargetAuthState(); 143 | final Queue queue = new ArrayDeque<>(); 144 | 145 | if (state == null || !state.getState().equals(AuthProtocolState.CHALLENGED)) { 146 | queue.add(authOption); 147 | } else { 148 | System.out.println("does this happen?"); 149 | } 150 | 151 | return queue; 152 | } 153 | 154 | @Override 155 | public void authSucceeded(final HttpHost authhost, 156 | final AuthScheme authScheme, 157 | final HttpContext context) { 158 | Objects.requireNonNull(authhost, "Authentication host must be present"); 159 | Objects.requireNonNull(authScheme, "Authentication scheme must be present"); 160 | Objects.requireNonNull(context, "HTTP context must be present"); 161 | 162 | LOG.debug("HTTP Signature authentication succeeded"); 163 | 164 | final HttpClientContext clientContext = HttpClientContext.adapt(context); 165 | 166 | AuthCache authCache = clientContext.getAuthCache(); 167 | if (authCache == null) { 168 | authCache = new BasicAuthCache(); 169 | clientContext.setAuthCache(authCache); 170 | } 171 | if (LOG.isDebugEnabled()) { 172 | LOG.debug("Caching '" + authScheme.getSchemeName() 173 | + "' auth scheme for " + authhost); 174 | } 175 | authCache.put(authhost, authScheme); 176 | } 177 | 178 | @Override 179 | public void authFailed(final HttpHost authhost, 180 | final AuthScheme authScheme, 181 | final HttpContext context) { 182 | Objects.requireNonNull(authhost, "Authentication host must be present"); 183 | Objects.requireNonNull(context, "HTTP context must be present"); 184 | 185 | LOG.debug("HTTP Signature authentication failed"); 186 | 187 | final HttpClientContext clientContext = HttpClientContext.adapt(context); 188 | 189 | final AuthCache authCache = clientContext.getAuthCache(); 190 | if (authCache != null) { 191 | if (LOG.isDebugEnabled()) { 192 | LOG.debug("Clearing cached auth scheme for " + authhost); 193 | } 194 | authCache.remove(authhost); 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /apache-http-client/src/main/java/com/joyent/http/signature/apache/httpclient/HttpSignatureCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature.apache.httpclient; 9 | 10 | import com.joyent.http.signature.HttpSignatureException; 11 | import com.joyent.http.signature.Signer; 12 | import org.apache.http.auth.AuthenticationException; 13 | import org.apache.http.auth.Credentials; 14 | 15 | import java.security.KeyPair; 16 | 17 | /** 18 | *

Class that an interface to caching HTTP signatures for a given 19 | * {@link Credentials} instance.

20 | * 21 | *

HTTP signature dates have a resolution of one second. If a two signatures 22 | * are requested for the exact same signature date time (within 1 second), then 23 | * we do not need to recalculate the signature (a computationally expensive 24 | * operation). In order to accomplish this, this class contains two fields 25 | * that cache the last signature date time and the last signature. Using the 26 | * cache, when two requests come through with the same date time, then we only 27 | * need to calculate the signature for a single request.

28 | * 29 | *

This class maintains a cache per {@link Credentials} object because 30 | * the signature will differ due to different credentials using different 31 | * signing keys.

32 | * 33 | * @author Elijah Zupancic 34 | * @since 4.0.0 35 | */ 36 | class HttpSignatureCache { 37 | /** 38 | * Credentials to associate cache with. 39 | */ 40 | private final Credentials credentials; 41 | 42 | /** 43 | * The date time of the last HTTP signature. 44 | */ 45 | private String lastDate = ""; 46 | 47 | /** 48 | * The last generated HTTP signature associated with this credential. 49 | */ 50 | private String lastSignature = ""; 51 | 52 | /** 53 | * Creates a new cache for the specified credential. 54 | * 55 | * @param credentials credentials to associate cache with 56 | */ 57 | HttpSignatureCache(final Credentials credentials) { 58 | if (credentials == null) { 59 | throw new IllegalArgumentException("Credentials must be present"); 60 | } 61 | 62 | if (credentials.getUserPrincipal() == null) { 63 | throw new IllegalArgumentException("User principal must be present"); 64 | } 65 | 66 | if (credentials.getUserPrincipal().getName() == null) { 67 | throw new IllegalArgumentException("User principal name must be present"); 68 | } 69 | 70 | this.credentials = credentials; 71 | } 72 | 73 | /** 74 | * Method that attempts to get a valid cached signature based on the 75 | * specified parameters. If a cached credential is not valid, then 76 | * a new signature will be generated and stored for later use in caching. 77 | * 78 | * @param stringDate date to use for HTTP signature 79 | * @param signer signer class to use for generating signature 80 | * @param keyPair cryptographic key pair to use for generating signature 81 | * 82 | * @return a valid HTTP signature string to be passed as a header value 83 | * 84 | * @throws AuthenticationException thrown if there is a problem authenticating the signature 85 | */ 86 | synchronized String updateAndGetSignature(final String stringDate, 87 | final Signer signer, 88 | final KeyPair keyPair) 89 | throws AuthenticationException { 90 | 91 | // Signing date time is equal, so we returned cached signature 92 | // stringDate parameter should *never* be null or blank 93 | if (lastDate.equals(stringDate)) { 94 | return lastSignature; 95 | } 96 | 97 | lastDate = stringDate; 98 | 99 | final String login = credentials.getUserPrincipal().getName(); 100 | 101 | // If date didn't match, then we calculate signature and store it 102 | try { 103 | final String authz = signer.createAuthorizationHeader( 104 | login, keyPair, stringDate); 105 | lastSignature = authz; 106 | 107 | return authz; 108 | } catch (HttpSignatureException e) { 109 | String details = String.format("Unable to authenticate [%s] " 110 | + "using keypair [%s]", 111 | login, keyPair); 112 | throw new AuthenticationException(details, e); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /apache-http-client/src/main/java/com/joyent/http/signature/apache/httpclient/HttpSignatureConfigurator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature.apache.httpclient; 9 | 10 | import com.joyent.http.signature.ThreadLocalSigner; 11 | import org.apache.http.auth.AuthScheme; 12 | import org.apache.http.auth.Credentials; 13 | import org.apache.http.client.AuthenticationStrategy; 14 | import org.apache.http.impl.client.HttpClientBuilder; 15 | 16 | import java.security.KeyPair; 17 | 18 | /** 19 | * Configuration helper class for configuring a {@link HttpClientBuilder} to use 20 | * HTTP Signatures authentication. 21 | * 22 | * @author Elijah Zupancic 23 | * @since 2.0.5 24 | */ 25 | @SuppressWarnings({"checkstyle:javadocmethod", "checkstyle:javadoctype", 26 | "checkstyle:javadocvariable", "unused"}) 27 | public class HttpSignatureConfigurator { 28 | /** 29 | * Public/private keypair object used to sign HTTP requests. 30 | */ 31 | private final KeyPair keyPair; 32 | 33 | /** 34 | * Credentials containing a username. 35 | */ 36 | private final Credentials credentials; 37 | 38 | /** 39 | * Authentication scheme to use to authenticate requests. 40 | */ 41 | private final HttpSignatureAuthScheme authScheme; 42 | 43 | /** 44 | * Authentication strategy instance that is assigned to all requests configured in the 45 | * {@link HttpClientBuilder}. 46 | */ 47 | private final AuthenticationStrategy authenticationStrategy; 48 | 49 | /** 50 | * Creates a new instance. 51 | * 52 | * @param keyPair public/private keypair object used to sign HTTP requests 53 | * @param credentials credentials containing a username 54 | * @param useNativeCodeToSign true to enable native code acceleration of cryptographic singing 55 | * 56 | * @deprecated Prefer {@link #HttpSignatureConfigurator(KeyPair, 57 | * Credentials, ThreadLocalSigner)} if configuration of Signer 58 | * algorithm, hashes, or providers is required. 59 | */ 60 | @Deprecated 61 | public HttpSignatureConfigurator(final KeyPair keyPair, 62 | final Credentials credentials, 63 | final boolean useNativeCodeToSign) { 64 | this.keyPair = keyPair; 65 | this.credentials = credentials; 66 | this.authScheme = new HttpSignatureAuthScheme(keyPair, useNativeCodeToSign); 67 | this.authenticationStrategy = new HttpSignatureAuthenticationStrategy(authScheme, 68 | credentials); 69 | } 70 | 71 | /** 72 | * Creates a new instance. 73 | * 74 | * @param keyPair public/private keypair object used to sign HTTP requests 75 | * @param credentials credentials containing a username 76 | * @param signer For use with http signature 77 | */ 78 | public HttpSignatureConfigurator(final KeyPair keyPair, 79 | final Credentials credentials, 80 | final ThreadLocalSigner signer) { 81 | this.keyPair = keyPair; 82 | this.credentials = credentials; 83 | this.authScheme = new HttpSignatureAuthScheme(keyPair, signer); 84 | this.authenticationStrategy = new HttpSignatureAuthenticationStrategy(authScheme, 85 | credentials); 86 | } 87 | 88 | 89 | /** 90 | * Configures a {@link HttpClientBuilder} to use HTTP Signature authentication. 91 | * 92 | * @param httpClientBuilder build to configure 93 | */ 94 | public void configure(final HttpClientBuilder httpClientBuilder) { 95 | httpClientBuilder.setTargetAuthenticationStrategy(authenticationStrategy); 96 | } 97 | 98 | public KeyPair getKeyPair() { 99 | return keyPair; 100 | } 101 | 102 | public Credentials getCredentials() { 103 | return credentials; 104 | } 105 | 106 | public AuthScheme getAuthScheme() { 107 | return authScheme; 108 | } 109 | 110 | public AuthenticationStrategy getAuthenticationStrategy() { 111 | return authenticationStrategy; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /apache-http-client/src/main/java/com/joyent/http/signature/apache/httpclient/HttpSignatureRequestInterceptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature.apache.httpclient; 9 | 10 | import org.apache.http.Header; 11 | import org.apache.http.HttpException; 12 | import org.apache.http.HttpRequest; 13 | import org.apache.http.HttpRequestInterceptor; 14 | import org.apache.http.auth.Credentials; 15 | import org.apache.http.protocol.HttpContext; 16 | 17 | import java.io.IOException; 18 | 19 | /** 20 | * Alternative to HTTP Client {@link org.apache.http.auth.AuthScheme} approach 21 | * that uses a {@link org.apache.http.HttpRequestInterceptor} to perform 22 | * HTTP signature authentication. 23 | * 24 | * @author Elijah Zupancic 25 | * @since 3.0.0 26 | */ 27 | public class HttpSignatureRequestInterceptor implements HttpRequestInterceptor { 28 | /** 29 | * Flag indicating that HTTP signature authentication is enabled. 30 | */ 31 | private final boolean authEnabled; 32 | 33 | /** 34 | * Authentication scheme instance to use to create authentication header. 35 | */ 36 | private final HttpSignatureAuthScheme authScheme; 37 | 38 | /** 39 | * Credentials of the user authenticating using HTTP signatures. 40 | */ 41 | private final Credentials credentials; 42 | 43 | /** 44 | * Creates a new instance. 45 | * 46 | * @param authScheme authentication scheme used to generate signature 47 | * @param credentials credentials of user authenticating 48 | * @param authEnabled flag indicating if authentication is enabled 49 | */ 50 | public HttpSignatureRequestInterceptor(final HttpSignatureAuthScheme authScheme, 51 | final Credentials credentials, 52 | final boolean authEnabled) { 53 | this.authScheme = authScheme; 54 | this.credentials = credentials; 55 | this.authEnabled = authEnabled; 56 | } 57 | 58 | @Override 59 | public void process(final HttpRequest request, final HttpContext context) 60 | throws HttpException, IOException { 61 | if (!authEnabled) { 62 | return; 63 | } 64 | 65 | final long start = System.nanoTime(); 66 | final Header authorization = authScheme.authenticate( 67 | this.credentials, request, context); 68 | final long end = System.nanoTime(); 69 | 70 | request.setHeader(authorization); 71 | request.setHeader("x-http-signing-time-ns", String.valueOf(end - start)); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /apache-http-client/src/main/java/com/joyent/http/signature/apache/httpclient/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | /** 10 | * Package containing utility classes for using HTTP Signature with the 11 | * Apache HTTP Client. 12 | * 13 | * There are two primary implementations an {@link org.apache.http.auth.AuthScheme} 14 | * implementation implemented as {@link com.joyent.http.signature.apache.httpclient.HttpSignatureAuthScheme} and 15 | * a {@link org.apache.http.HttpRequestInterceptor} implementation implemented 16 | * as {@link com.joyent.http.signature.apache.httpclient.HttpSignatureRequestInterceptor}. 17 | * Both classes are valid ways of implementing HTTP Signatures with the Apache 18 | * Commons HTTP Client. Depending on your application one implementation may 19 | * be better than another. 20 | * 21 | * @author Elijah Zupancic 22 | */ 23 | package com.joyent.http.signature.apache.httpclient; 24 | -------------------------------------------------------------------------------- /apache-http-client/src/test/java/com/joyent/http/signature/apache/httpclient/HttpSignatureAuthIT.java: -------------------------------------------------------------------------------- 1 | package com.joyent.http.signature.apache.httpclient; 2 | 3 | import com.joyent.http.signature.ThreadLocalSigner; 4 | import org.apache.http.*; 5 | import org.apache.http.auth.Credentials; 6 | import org.apache.http.auth.UsernamePasswordCredentials; 7 | import org.apache.http.client.config.RequestConfig; 8 | import org.apache.http.client.methods.HttpHead; 9 | import org.apache.http.client.protocol.HttpClientContext; 10 | import org.apache.http.entity.ContentType; 11 | import org.apache.http.impl.client.CloseableHttpClient; 12 | import org.apache.http.impl.client.HttpClientBuilder; 13 | import org.apache.http.impl.client.HttpClients; 14 | import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; 15 | import org.apache.http.message.BasicHeader; 16 | 17 | import java.io.File; 18 | import java.io.IOException; 19 | import java.net.InetSocketAddress; 20 | import java.net.Proxy; 21 | import java.net.ProxySelector; 22 | import java.net.URI; 23 | import java.security.KeyPair; 24 | import java.util.*; 25 | 26 | import static org.testng.Assert.assertEquals; 27 | 28 | /** 29 | * Integration test for testing the Apache HTTP Client with HTTP Signature 30 | * authentication. This test needs to be run manually at this time. 31 | */ 32 | @SuppressWarnings("deprecation") 33 | public class HttpSignatureAuthIT { 34 | public static final String SDC_KEY_ID_ENV_KEY = "SDC_KEY_ID"; 35 | private static final String SDC_URL_ENV_KEY = "SDC_URL"; 36 | private static final String SDC_ACCOUNT_ENV_KEY = "SDC_ACCOUNT"; 37 | private static final String SDC_KEY_PATH_ENV_KEY = "SDC_KEY_PATH"; 38 | 39 | private static final ThreadLocalSigner SIGNER = new ThreadLocalSigner(); 40 | 41 | // @Test 42 | public void canAuthenticate() throws IOException { 43 | final KeyPair keyPair = createKeyPair(); 44 | 45 | final String user = System.getenv(SDC_ACCOUNT_ENV_KEY); 46 | Objects.requireNonNull(user, SDC_ACCOUNT_ENV_KEY + " must be set"); 47 | 48 | final String keyId = System.getenv(SDC_KEY_ID_ENV_KEY); 49 | Objects.requireNonNull(keyId, SDC_KEY_ID_ENV_KEY + " must be set"); 50 | 51 | final Credentials credentials = new UsernamePasswordCredentials( 52 | user, keyId); 53 | HttpSignatureConfigurator configurator = new HttpSignatureConfigurator(keyPair, 54 | credentials, true); 55 | 56 | try (CloseableHttpClient conn = createConnection(configurator)) { 57 | String baseUrl = System.getenv(SDC_URL_ENV_KEY); 58 | Objects.requireNonNull(baseUrl, SDC_URL_ENV_KEY + " must be present"); 59 | 60 | URI uri = URI.create(String.format("%s/%s/machines", 61 | baseUrl, user)); 62 | 63 | HttpHead head = new HttpHead(uri); 64 | HttpClientContext context = new HttpClientContext(); 65 | HttpResponse response = conn.execute(head, context); 66 | 67 | assertEquals(response.getStatusLine().getStatusCode(), 68 | HttpStatus.SC_OK); 69 | } 70 | } 71 | 72 | private CloseableHttpClient createConnection(final HttpSignatureConfigurator configurator) { 73 | final PoolingHttpClientConnectionManager connectionManager = 74 | new PoolingHttpClientConnectionManager(); 75 | 76 | final RequestConfig requestConfig = RequestConfig.custom() 77 | .setAuthenticationEnabled(true) 78 | .setContentCompressionEnabled(true) 79 | .build(); 80 | 81 | final Collection headers = Arrays.asList( 82 | new BasicHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.getMimeType()) 83 | ); 84 | 85 | final HttpClientBuilder httpClientBuilder = HttpClients.custom() 86 | .setDefaultHeaders(Collections.unmodifiableCollection(headers)) 87 | .setConnectionManager(connectionManager) 88 | .setDefaultRequestConfig(requestConfig); 89 | 90 | configurator.configure(httpClientBuilder); 91 | 92 | final HttpHost proxyHost = findProxyServer(); 93 | 94 | if (proxyHost != null) { 95 | httpClientBuilder.setProxy(proxyHost); 96 | } 97 | 98 | return httpClientBuilder.build(); 99 | } 100 | 101 | /** 102 | * Creates a {@link KeyPair} object based on the factory's configuration. 103 | * @return an encryption key pair 104 | */ 105 | private KeyPair createKeyPair() { 106 | final KeyPair keyPair; 107 | final String keyPath = System.getenv(SDC_KEY_PATH_ENV_KEY); 108 | Objects.requireNonNull(keyPath, SDC_KEY_PATH_ENV_KEY + " must be set"); 109 | 110 | try { 111 | keyPair = SIGNER.get().getKeyPair(new File(keyPath).toPath()); 112 | } catch (IOException e) { 113 | String msg = String.format("Unable to read key files from path: %s", 114 | keyPath); 115 | throw new RuntimeException(msg, e); 116 | } 117 | 118 | return keyPair; 119 | } 120 | 121 | /** 122 | * Finds the host of the proxy server that was configured as part of the 123 | * JVM settings. 124 | * 125 | * @return proxy server as {@link HttpHost}, if no proxy then null 126 | */ 127 | private HttpHost findProxyServer() { 128 | final ProxySelector proxySelector = ProxySelector.getDefault(); 129 | final String rootURI = System.getenv(SDC_URL_ENV_KEY); 130 | Objects.requireNonNull(rootURI, "SDC_URL must be set"); 131 | List proxies = proxySelector.select(URI.create(rootURI)); 132 | 133 | if (!proxies.isEmpty()) { 134 | /* The Apache HTTP Client doesn't understand the concept of multiple 135 | * proxies, so we use only the first one returned. */ 136 | final Proxy proxy = proxies.get(0); 137 | 138 | switch (proxy.type()) { 139 | case DIRECT: 140 | return null; 141 | case SOCKS: 142 | throw new RuntimeException("SOCKS proxies are unsupported"); 143 | default: 144 | // do nothing and fall through 145 | } 146 | 147 | if (proxy.address() instanceof InetSocketAddress) { 148 | InetSocketAddress sa = (InetSocketAddress) proxy.address(); 149 | 150 | return new HttpHost(sa.getHostName(), sa.getPort()); 151 | } else { 152 | String msg = String.format( 153 | "Expecting proxy to be instance of InetSocketAddress. " 154 | + " Actually: %s", proxy.address()); 155 | throw new RuntimeException(msg); 156 | } 157 | } else { 158 | return null; 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /apache-http-client/src/test/java/com/joyent/http/signature/apache/httpclient/HttpSignatureRequestInterceptorTest.java: -------------------------------------------------------------------------------- 1 | package com.joyent.http.signature.apache.httpclient; 2 | 3 | import com.joyent.http.signature.SignerTestUtil; 4 | import com.joyent.http.signature.ThreadLocalSigner; 5 | import org.apache.http.Header; 6 | import org.apache.http.HttpException; 7 | import org.apache.http.HttpRequest; 8 | import org.apache.http.auth.Credentials; 9 | import org.apache.http.auth.UsernamePasswordCredentials; 10 | import org.apache.http.client.methods.HttpPut; 11 | import org.apache.http.message.BasicHeader; 12 | import org.apache.http.protocol.BasicHttpContext; 13 | import org.apache.http.protocol.HttpContext; 14 | import org.testng.annotations.BeforeClass; 15 | import org.testng.annotations.Optional; 16 | import org.testng.annotations.Parameters; 17 | import org.testng.annotations.Test; 18 | 19 | import java.io.IOException; 20 | import java.security.KeyPair; 21 | import java.security.NoSuchAlgorithmException; 22 | 23 | public class HttpSignatureRequestInterceptorTest { 24 | private KeyPair testKeyPair; 25 | private String testKeyFingerprint; 26 | private boolean useNativeCodeToSign; 27 | private ThreadLocalSigner signer; 28 | 29 | private HttpSignatureAuthScheme authScheme; 30 | 31 | private Credentials credentials; 32 | private HttpSignatureRequestInterceptor interceptor; 33 | 34 | @Parameters({"useNativeCodeToSign"}) 35 | @BeforeClass 36 | @SuppressWarnings("deprecation") 37 | public void beforeClass(@Optional Boolean useNativeCodeToSign) throws IOException, NoSuchAlgorithmException { 38 | if (useNativeCodeToSign == null) { 39 | this.useNativeCodeToSign = true; 40 | } else { 41 | this.useNativeCodeToSign = useNativeCodeToSign; 42 | } 43 | 44 | this.signer = new ThreadLocalSigner(this.useNativeCodeToSign); 45 | // Removes any existing instances - so that we can reset state 46 | this.signer.remove(); 47 | this.testKeyPair = SignerTestUtil.testKeyPair("rsa_2048"); 48 | this.testKeyFingerprint = SignerTestUtil.testKeyMd5Fingerprint("rsa_2048"); 49 | credentials = new UsernamePasswordCredentials("username", testKeyFingerprint); 50 | 51 | this.authScheme = new HttpSignatureAuthScheme(testKeyPair, this.useNativeCodeToSign); 52 | this.interceptor = new HttpSignatureRequestInterceptor(authScheme, credentials, 53 | this.useNativeCodeToSign); 54 | } 55 | 56 | @Test 57 | public void signBenchmark() throws IOException, HttpException { 58 | Header[] headers = new Header[] { 59 | new BasicHeader("Content-Type", "application/json; type=directory"), 60 | new BasicHeader("x-request-id", "bb3ca512-c637-43c8-926b-2990caf64aee"), 61 | new BasicHeader("accept-version", "~1.0"), 62 | new BasicHeader("Accept", "application/json, */*"), 63 | new BasicHeader("Content-Length", "0"), 64 | new BasicHeader("Host", "us-east.manta.joyent.com:443"), 65 | new BasicHeader("Connection", "Keep-Alive"), 66 | new BasicHeader("User-Agent", "Java-Manta-SDK/3.0.0-SNAPSHOT (Java/1.8.0_76/Oracle Corporation)"), 67 | new BasicHeader("Accept-Encoding", "gzip,deflate"), 68 | }; 69 | 70 | HttpContext context = new BasicHttpContext(); 71 | 72 | for (int i = 0; i < 5; i++) { 73 | HttpRequest request = new HttpPut("https://us-east.manta.joyent.com:443"); 74 | request.setHeaders(headers); 75 | interceptor.process(request, context); 76 | String signTime = request.getFirstHeader("x-http-signing-time-ns").getValue(); 77 | System.out.printf("Time to sign: %s\n", signTime); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /apache-http-client/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | [%thread] %-5level %logger - %msg%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /apache-http-client/src/test/resources/testng.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 4.0.0 11 | 12 | 13 | java-http-signature 14 | com.joyent.http-signature 15 | 4.1.3-SNAPSHOT 16 | 17 | 18 | http-signature-common 19 | jar 20 | 21 | 22 | 23 | 1.65 24 | 3.0.0 25 | 1.14 26 | 27 | 28 | 29 | 30 | org.bouncycastle 31 | bcpkix-jdk15on 32 | ${dependency.bouncycastle.version} 33 | 34 | 35 | org.bouncycastle 36 | bcprov-jdk15on 37 | ${dependency.bouncycastle.version} 38 | 39 | 40 | com.squareup.jnagmp 41 | bouncycastle-rsa 42 | ${dependency.jnagmp.version} 43 | 44 | 45 | org.bouncycastle 46 | bcpkix-jdk15on 47 | 48 | 49 | 50 | 51 | commons-codec 52 | commons-codec 53 | ${dependency.commons-codec.version} 54 | test 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.apache.maven.plugins 63 | maven-surefire-plugin 64 | 65 | ${maven.test.skip} 66 | 67 | src/test/resources/testng.xml 68 | 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-jar-plugin 74 | ${maven-jar-plugin.version} 75 | 76 | 77 | package 78 | 79 | test-jar 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /common/src/main/java/com/joyent/http/signature/CryptoException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature; 9 | 10 | /** 11 | * Runtime exception thrown when there was a problem processing cryptography. 12 | * 13 | * @author Elijah Zupancic 14 | * @since 1.0.0 15 | */ 16 | public class CryptoException extends HttpSignatureException { 17 | 18 | private static final long serialVersionUID = -3558219358777167268L; 19 | 20 | /** 21 | * Creates a new exception. 22 | */ 23 | public CryptoException() { 24 | } 25 | 26 | /** 27 | * Creates a new exception with the specified message. 28 | * @param message Message to embed 29 | */ 30 | public CryptoException(final String message) { 31 | super(message); 32 | } 33 | 34 | /** 35 | * Creates a new chained exception with the specified message. 36 | * 37 | * @param message Message to embed 38 | * @param cause exception to chain 39 | */ 40 | public CryptoException(final String message, final Throwable cause) { 41 | super(message, cause); 42 | } 43 | 44 | /** 45 | * Creates a new chained exception. 46 | * 47 | * @param cause exception to chain 48 | */ 49 | public CryptoException(final Throwable cause) { 50 | super(cause); 51 | } 52 | 53 | /** 54 | * Creates a exception with the specified message, cause, 55 | * suppression enabled or disabled, and writable stack trace enabled 56 | * or disabled. 57 | * 58 | * @param message Message to embed 59 | * @param cause exception to chain 60 | * @param enableSuppression whether or not suppression is enabled or disabled 61 | * @param writableStackTrace whether or not the stack trace should be writable 62 | */ 63 | public CryptoException(final String message, 64 | final Throwable cause, 65 | final boolean enableSuppression, 66 | final boolean writableStackTrace) { 67 | super(message, cause, enableSuppression, writableStackTrace); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /common/src/main/java/com/joyent/http/signature/HttpSignatureException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature; 9 | 10 | /** 11 | * General exception that all other exceptions inherit from in this project. 12 | * 13 | * @author Elijah Zupancic 14 | * @since 1.0.0 15 | */ 16 | public class HttpSignatureException extends RuntimeException { 17 | 18 | private static final long serialVersionUID = 6548882825292310527L; 19 | 20 | /** 21 | * Creates a new exception. 22 | */ 23 | public HttpSignatureException() { 24 | } 25 | 26 | /** 27 | * Creates a new exception with the specified message. 28 | * @param message Message to embed 29 | */ 30 | public HttpSignatureException(final String message) { 31 | super(message); 32 | } 33 | 34 | /** 35 | * Creates a new chained exception with the specified message. 36 | * 37 | * @param message Message to embed 38 | * @param cause exception to chain 39 | */ 40 | public HttpSignatureException(final String message, final Throwable cause) { 41 | super(message, cause); 42 | } 43 | 44 | /** 45 | * Creates a new chained exception. 46 | * 47 | * @param cause exception to chain 48 | */ 49 | public HttpSignatureException(final Throwable cause) { 50 | super(cause); 51 | } 52 | 53 | /** 54 | * Creates a exception with the specified message, cause, 55 | * suppression enabled or disabled, and writable stack trace enabled 56 | * or disabled. 57 | * 58 | * @param message Message to embed 59 | * @param cause exception to chain 60 | * @param enableSuppression whether or not suppression is enabled or disabled 61 | * @param writableStackTrace whether or not the stack trace should be writable 62 | */ 63 | public HttpSignatureException(final String message, 64 | final Throwable cause, 65 | final boolean enableSuppression, 66 | final boolean writableStackTrace) { 67 | super(message, cause, enableSuppression, writableStackTrace); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /common/src/main/java/com/joyent/http/signature/KeyLoadException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature; 9 | 10 | /** 11 | * Exception that can occur while loading a {@link java.security.KeyPair}. 12 | * 13 | * @since 4.0.4 14 | * @author Tomas Celayac 15 | */ 16 | public class KeyLoadException extends HttpSignatureException { 17 | 18 | private static final long serialVersionUID = 3842266217250311085L; 19 | 20 | /** 21 | * Creates a new exception with the specified message. 22 | * @param message Message to embed 23 | */ 24 | public KeyLoadException(final String message) { 25 | super(message); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /common/src/main/java/com/joyent/http/signature/ThreadLocalClearException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature; 9 | 10 | /** 11 | * Exception thrown when we have a problem clearing thread local variables. 12 | * 13 | * @author Elijah Zupancic 14 | */ 15 | public class ThreadLocalClearException extends RuntimeException { 16 | 17 | private static final long serialVersionUID = 4658889695319509040L; 18 | 19 | /** 20 | * Constructs a new runtime exception with the specified cause and a 21 | * detail message of (cause==null ? null : cause.toString()) 22 | * (which typically contains the class and detail message of 23 | * cause). This constructor is useful for runtime exceptions 24 | * that are little more than wrappers for other throwables. 25 | * 26 | * @param cause the cause (which is saved for later retrieval by the 27 | * {@link #getCause()} method). (A null value is 28 | * permitted, and indicates that the cause is nonexistent or 29 | * unknown.) 30 | */ 31 | public ThreadLocalClearException(final Throwable cause) { 32 | super(cause); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /common/src/main/java/com/joyent/http/signature/ThreadLocalSigner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature; 9 | 10 | import java.lang.reflect.InvocationTargetException; 11 | import java.lang.reflect.Method; 12 | import java.util.Set; 13 | import java.util.concurrent.CopyOnWriteArraySet; 14 | 15 | /** 16 | * Creates a thread-local copy of {@link Signer}. Cryptographic signature classes 17 | * are not thread safe. This class provides methods to get a singleton instance 18 | * per thread. 19 | * 20 | * @author Elijah Zupancic 21 | */ 22 | public class ThreadLocalSigner extends ThreadLocal { 23 | /** 24 | * Set of threads that have set ThreadLocal references. 25 | */ 26 | private static Set threadsReferencing = 27 | new CopyOnWriteArraySet<>(); 28 | 29 | /** 30 | * {@code Signer.Builder} with configuration that will be used to 31 | * instantiate new {@code Signer}s on demand. 32 | */ 33 | private Signer.Builder builder; 34 | 35 | /** 36 | * Create a new thread-local instance of {@link Signer} with the 37 | * same defaults as in version 3.x. 38 | * 39 | * @deprecated {@link #ThreadLocalSigner(boolean)}) 40 | * 41 | */ 42 | @Deprecated 43 | public ThreadLocalSigner() { 44 | this(true); 45 | } 46 | 47 | /** 48 | * Create a new thread-local instance of {@link Signer} with the 49 | * same defaults as in version 3.0, but optionally toggling {@code 50 | * native.jnagmp} acceleration. 51 | * 52 | * @param useNativeCodeToSign true to enable native code acceleration of cryptographic singing 53 | * 54 | * @deprecated Passing a {@link Signer.Builder} is now the 55 | * preferred constructor. The use of these constructors tends to 56 | * encourage error prone use with multiple {@code 57 | * ThreadLocalSigner} instances unintentionally operating on the 58 | * same keys. 59 | */ 60 | @Deprecated 61 | @SuppressWarnings("checkstyle:avoidinlineconditionals") 62 | public ThreadLocalSigner(final boolean useNativeCodeToSign) { 63 | builder = new Signer.Builder("RSA").providerCode(useNativeCodeToSign ? "native.jnagmp" : "stdlib"); 64 | } 65 | 66 | /** 67 | * Create a new thread-local instance of {@link Signer} with each 68 | * {@link Signer} configured by the given {@link Signer.Builder}. 69 | * 70 | * @param builder {@code Signer.Builder} with configuration that 71 | * will be used to instantiate new {@code Signer}s on demand. 72 | */ 73 | public ThreadLocalSigner(final Signer.Builder builder) { 74 | this.builder = builder; 75 | } 76 | 77 | @Override 78 | protected Signer initialValue() { 79 | threadsReferencing.add(Thread.currentThread()); 80 | return builder.build(); 81 | } 82 | 83 | @Override 84 | public void remove() { 85 | super.remove(); 86 | threadsReferencing.remove(Thread.currentThread()); 87 | } 88 | 89 | @Override 90 | public void set(final Signer value) { 91 | super.set(value); 92 | 93 | if (value == null) { 94 | threadsReferencing.remove(Thread.currentThread()); 95 | } 96 | } 97 | 98 | /** 99 | * Removes all thread-local values. 100 | */ 101 | public void clearAll() { 102 | /* We peel back the layers of ThreadLocal's internal implementation in order to 103 | * provide consumers of the library a method which they can use to mitigate memory 104 | * leaks. 105 | */ 106 | try { 107 | Method getMap = ThreadLocal.class.getDeclaredMethod("getMap", Thread.class); 108 | getMap.setAccessible(true); 109 | Class threadLocalMapClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap"); 110 | Method remove = threadLocalMapClass.getDeclaredMethod("remove", ThreadLocal.class); 111 | remove.setAccessible(true); 112 | 113 | for (Thread t : threadsReferencing) { 114 | Object map = getMap.invoke(this, t); 115 | 116 | if (map != null) { 117 | remove.invoke(map, this); 118 | } 119 | } 120 | } catch (ClassNotFoundException | NoSuchMethodException 121 | | IllegalAccessException | InvocationTargetException 122 | | NullPointerException e) { 123 | throw new ThreadLocalClearException(e); 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /common/src/main/java/com/joyent/http/signature/crypto/JceDigest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature.crypto; 9 | 10 | import com.joyent.http.signature.CryptoException; 11 | import org.bouncycastle.crypto.Digest; 12 | 13 | import java.security.DigestException; 14 | import java.security.MessageDigest; 15 | import java.security.NoSuchAlgorithmException; 16 | 17 | /** 18 | * Wrapper interface that allows JCE {@link MessageDigest} 19 | * instances to function as BouncyCastle {@link Digest} 20 | * classes. 21 | */ 22 | public class JceDigest implements Digest { 23 | /** 24 | * Wrapped message digest. 25 | */ 26 | private final MessageDigest messageDigest; 27 | 28 | /** 29 | * Creates a new digest by the JCE digest name. 30 | * 31 | * @param algorithm digest name 32 | */ 33 | public JceDigest(final String algorithm) { 34 | try { 35 | this.messageDigest = MessageDigest.getInstance(algorithm); 36 | } catch (NoSuchAlgorithmException e) { 37 | throw new CryptoException("Can't find digest algorithm [" 38 | + algorithm + "]", e); 39 | } 40 | } 41 | 42 | /** 43 | * Creates a new instance that wraps the specified digest. 44 | * 45 | * @param messageDigest message digest to wrap 46 | */ 47 | public JceDigest(final MessageDigest messageDigest) { 48 | this.messageDigest = messageDigest; 49 | } 50 | 51 | @Override 52 | public String getAlgorithmName() { 53 | return messageDigest.getAlgorithm(); 54 | } 55 | 56 | @Override 57 | public int getDigestSize() { 58 | return messageDigest.getDigestLength(); 59 | } 60 | 61 | @Override 62 | public void update(final byte in) { 63 | messageDigest.update(in); 64 | } 65 | 66 | @Override 67 | public void update(final byte[] in, final int inOff, final int len) { 68 | messageDigest.update(in, inOff, len); 69 | } 70 | 71 | @Override 72 | public int doFinal(final byte[] out, final int outOff) { 73 | try { 74 | return messageDigest.digest(out, outOff, out.length - outOff); 75 | } catch (DigestException e) { 76 | throw new CryptoException("Can't finalize digest", e); 77 | } 78 | } 79 | 80 | @Override 81 | public void reset() { 82 | messageDigest.reset(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /common/src/main/java/com/joyent/http/signature/crypto/MantaNativeRSACoreEngine.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.joyent.http.signature.crypto; 15 | 16 | import com.squareup.jnagmp.GmpInteger; 17 | import org.bouncycastle.crypto.CipherParameters; 18 | import org.bouncycastle.crypto.DataLengthException; 19 | import org.bouncycastle.crypto.params.ParametersWithRandom; 20 | import org.bouncycastle.crypto.params.RSAKeyParameters; 21 | import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters; 22 | 23 | import java.math.BigInteger; 24 | 25 | import static com.squareup.jnagmp.Gmp.modPowInsecure; 26 | import static com.squareup.jnagmp.Gmp.modPowSecure; 27 | 28 | /** 29 | *

Class copied wholesale from the com.squareup.crypto.rsa project that uses 30 | * native libgmp to improve RSA performance. The only modifications to this 31 | * class are formatting and style.

32 | */ 33 | public class MantaNativeRSACoreEngine { 34 | private RSAKeyParameters key; 35 | private boolean forEncryption; 36 | private boolean isPrivate; 37 | private boolean isSmallExponent; 38 | 39 | // cached components for private CRT key 40 | private GmpInteger p; 41 | private GmpInteger q; 42 | private GmpInteger dP; 43 | private GmpInteger dQ; 44 | private BigInteger qInv; 45 | 46 | // cached components for public key 47 | private GmpInteger exponent; 48 | private GmpInteger modulus; 49 | 50 | /** 51 | * initialise the RSA engine. 52 | * 53 | * @param forEncryption true if we are encrypting, false otherwise. 54 | * @param param the necessary RSA key parameters. 55 | */ 56 | public void init( 57 | boolean forEncryption, 58 | CipherParameters param) { 59 | if (param instanceof ParametersWithRandom) { 60 | ParametersWithRandom rParam = (ParametersWithRandom) param; 61 | 62 | key = (RSAKeyParameters) rParam.getParameters(); 63 | } else { 64 | key = (RSAKeyParameters) param; 65 | } 66 | 67 | this.forEncryption = forEncryption; 68 | 69 | if (key instanceof RSAPrivateCrtKeyParameters) { 70 | isPrivate = true; 71 | // 72 | // we have the extra factors, use the Chinese Remainder Theorem - the author 73 | // wishes to express his thanks to Dirk Bonekaemper at rtsffm.com for 74 | // advice regarding the expression of this. 75 | // 76 | RSAPrivateCrtKeyParameters crtKey = (RSAPrivateCrtKeyParameters) key; 77 | 78 | p = new GmpInteger(crtKey.getP()); 79 | q = new GmpInteger(crtKey.getQ()); 80 | dP = new GmpInteger(crtKey.getDP()); 81 | dQ = new GmpInteger(crtKey.getDQ()); 82 | qInv = crtKey.getQInv(); 83 | 84 | exponent = modulus = null; 85 | } else { 86 | isPrivate = false; 87 | exponent = new GmpInteger(key.getExponent()); 88 | modulus = new GmpInteger(key.getModulus()); 89 | isSmallExponent = exponent.bitLength() < 64; 90 | 91 | p = q = dP = dQ = null; 92 | qInv = null; 93 | } 94 | } 95 | 96 | /** 97 | * Return the maximum size for an input block to this engine. 98 | * For RSA this is always one byte less than the key size on 99 | * encryption, and the same length as the key size on decryption. 100 | * 101 | * @return maximum size for an input block. 102 | */ 103 | public int getInputBlockSize() { 104 | int bitSize = key.getModulus().bitLength(); 105 | 106 | if (forEncryption) { 107 | return (bitSize + 7) / 8 - 1; 108 | } else { 109 | return (bitSize + 7) / 8; 110 | } 111 | } 112 | 113 | /** 114 | * Return the maximum size for an output block to this engine. 115 | * For RSA this is always one byte less than the key size on 116 | * decryption, and the same length as the key size on encryption. 117 | * 118 | * @return maximum size for an output block. 119 | */ 120 | public int getOutputBlockSize() { 121 | int bitSize = key.getModulus().bitLength(); 122 | 123 | if (forEncryption) { 124 | return (bitSize + 7) / 8; 125 | } else { 126 | return (bitSize + 7) / 8 - 1; 127 | } 128 | } 129 | 130 | public BigInteger convertInput( 131 | byte[] in, 132 | int inOff, 133 | int inLen) { 134 | if (inLen > (getInputBlockSize() + 1)) { 135 | throw new DataLengthException("input too large for RSA cipher."); 136 | } else if (inLen == (getInputBlockSize() + 1) && !forEncryption) { 137 | throw new DataLengthException("input too large for RSA cipher."); 138 | } 139 | 140 | byte[] block; 141 | 142 | if (inOff != 0 || inLen != in.length) { 143 | block = new byte[inLen]; 144 | 145 | System.arraycopy(in, inOff, block, 0, inLen); 146 | } else { 147 | block = in; 148 | } 149 | 150 | BigInteger res = new BigInteger(1, block); 151 | if (res.compareTo(key.getModulus()) >= 0) { 152 | throw new DataLengthException("input too large for RSA cipher."); 153 | } 154 | 155 | return res; 156 | } 157 | 158 | public byte[] convertOutput( 159 | BigInteger result) { 160 | byte[] output = result.toByteArray(); 161 | 162 | if (forEncryption) { 163 | if (output[0] == 0 && output.length > getOutputBlockSize()) // have ended up with an extra zero byte, copy down. 164 | { 165 | byte[] tmp = new byte[output.length - 1]; 166 | 167 | System.arraycopy(output, 1, tmp, 0, tmp.length); 168 | 169 | return tmp; 170 | } 171 | 172 | if (output.length < getOutputBlockSize()) // have ended up with less bytes than normal, lengthen 173 | { 174 | byte[] tmp = new byte[getOutputBlockSize()]; 175 | 176 | System.arraycopy(output, 0, tmp, tmp.length - output.length, output.length); 177 | 178 | return tmp; 179 | } 180 | } else { 181 | if (output[0] == 0) // have ended up with an extra zero byte, copy down. 182 | { 183 | byte[] tmp = new byte[output.length - 1]; 184 | 185 | System.arraycopy(output, 1, tmp, 0, tmp.length); 186 | 187 | return tmp; 188 | } 189 | } 190 | 191 | return output; 192 | } 193 | 194 | public BigInteger processBlock(BigInteger input) { 195 | if (isPrivate) { 196 | BigInteger mP, mQ, h, m; 197 | 198 | // mP = ((input mod p) ^ dP)) mod p 199 | mP = modPowSecure(input.remainder(p), dP, p); 200 | 201 | // mQ = ((input mod q) ^ dQ)) mod q 202 | mQ = modPowSecure(input.remainder(q), dQ, q); 203 | 204 | // h = qInv * (mP - mQ) mod p 205 | h = mP.subtract(mQ); 206 | h = h.multiply(qInv); 207 | h = h.mod(p); // mod (in Java) returns the positive residual 208 | 209 | // m = h * q + mQ 210 | m = h.multiply(q); 211 | m = m.add(mQ); 212 | 213 | return m; 214 | } else { 215 | if (isSmallExponent) { 216 | // Public key with reasonable (small) exponent, no need for secure. 217 | return modPowInsecure(input, exponent, modulus); 218 | } else { 219 | // Client mistakenly configured private key as public? Better be safe than sorry. 220 | return modPowSecure(input, exponent, modulus); 221 | } 222 | } 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /common/src/main/java/com/joyent/http/signature/crypto/NativeRSABlindedEngine.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2000 - 2016 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 | * OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | package com.joyent.http.signature.crypto; 23 | 24 | import com.squareup.jnagmp.Gmp; 25 | import org.bouncycastle.crypto.CipherParameters; 26 | import org.bouncycastle.crypto.DataLengthException; 27 | import org.bouncycastle.crypto.engines.RSABlindedEngine; 28 | import org.bouncycastle.crypto.params.ParametersWithRandom; 29 | import org.bouncycastle.crypto.params.RSAKeyParameters; 30 | import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters; 31 | import org.bouncycastle.util.BigIntegers; 32 | 33 | import java.math.BigInteger; 34 | import java.security.SecureRandom; 35 | 36 | /** 37 | *

This is a copy of {@link RSABlindedEngine} with the RSA core engine 38 | * replace with a native implementation. We copied the library code here 39 | * because there is no better way to for us to inherit the properties.

40 | * 41 | *

Note: changes from the original are only using libgmp to do modpow.

42 | * 43 | *

Relevant copyright belongs to:
44 | * Copyright (c) 2000 - 2015 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) 45 | *

46 | * 47 | * @see org.bouncycastle.crypto.engines.RSABlindedEngine 48 | */ 49 | public class NativeRSABlindedEngine extends RSABlindedEngine { 50 | /** 51 | * The constant value of 1 as a {@link BigInteger}. 52 | */ 53 | private static final BigInteger ONE = BigInteger.valueOf(1); 54 | 55 | /** 56 | * Reference to the native implementation of a {@link org.bouncycastle.crypto.engines.RSACoreEngine}. 57 | */ 58 | private MantaNativeRSACoreEngine core = new MantaNativeRSACoreEngine(); 59 | 60 | /** 61 | * RSA Key parameters. 62 | */ 63 | private RSAKeyParameters key; 64 | 65 | /** 66 | * Source of randomness. 67 | */ 68 | private SecureRandom random; 69 | 70 | /** 71 | * initialise the RSA engine. 72 | * 73 | * @param forEncryption true if we are encrypting, false otherwise. 74 | * @param param the necessary RSA key parameters. 75 | */ 76 | @Override 77 | public void init(final boolean forEncryption, final CipherParameters param) { 78 | core.init(forEncryption, param); 79 | 80 | if (param instanceof ParametersWithRandom) { 81 | ParametersWithRandom rParam = (ParametersWithRandom)param; 82 | 83 | key = (RSAKeyParameters)rParam.getParameters(); 84 | random = rParam.getRandom(); 85 | } else { 86 | key = (RSAKeyParameters)param; 87 | random = new SecureRandom(); 88 | } 89 | } 90 | 91 | /** 92 | * Return the maximum size for an input block to this engine. 93 | * For RSA this is always one byte less than the key size on 94 | * encryption, and the same length as the key size on decryption. 95 | * 96 | * @return maximum size for an input block. 97 | */ 98 | @Override 99 | public int getInputBlockSize() { 100 | return core.getInputBlockSize(); 101 | } 102 | 103 | /** 104 | * Return the maximum size for an output block to this engine. 105 | * For RSA this is always one byte less than the key size on 106 | * decryption, and the same length as the key size on encryption. 107 | * 108 | * @return maximum size for an output block. 109 | */ 110 | @Override 111 | public int getOutputBlockSize() { 112 | return core.getOutputBlockSize(); 113 | } 114 | 115 | /** 116 | * Process a single block using the basic RSA algorithm. 117 | * 118 | * @param in the input array. 119 | * @param inOff the offset into the input buffer where the data starts. 120 | * @param inLen the length of the data to be processed. 121 | * @return the result of the RSA process. 122 | * @exception DataLengthException the input block is too large. 123 | */ 124 | @Override 125 | public byte[] processBlock(final byte[] in, final int inOff, final int inLen) { 126 | if (key == null) { 127 | throw new IllegalStateException("RSA engine not initialised"); 128 | } 129 | 130 | BigInteger input = core.convertInput(in, inOff, inLen); 131 | 132 | BigInteger result; 133 | if (key instanceof RSAPrivateCrtKeyParameters) { 134 | RSAPrivateCrtKeyParameters k = (RSAPrivateCrtKeyParameters)key; 135 | 136 | BigInteger e = k.getPublicExponent(); 137 | // can't do blinding without a public exponent 138 | if (e != null) { 139 | BigInteger m = k.getModulus(); 140 | BigInteger r = BigIntegers.createRandomInRange(ONE, m.subtract(ONE), random); 141 | 142 | // This is a modification to use the GMP native library method 143 | BigInteger blindedModPow = Gmp.modPowSecure(r, e, m); 144 | 145 | BigInteger blindedInput = blindedModPow.multiply(input).mod(m); 146 | BigInteger blindedResult = core.processBlock(blindedInput); 147 | 148 | // This is a modification to use the GMP native library method 149 | BigInteger rInv = Gmp.modInverse(r, m); 150 | 151 | result = blindedResult.multiply(rInv).mod(m); 152 | // defence against Arjen Lenstra’s CRT attack 153 | // This is a modification to use the GMP native library method 154 | if (!input.equals(Gmp.modPowInsecure(result, e, m))) { 155 | throw new IllegalStateException("RSA engine faulty decryption/signing detected"); 156 | } 157 | } else { 158 | result = core.processBlock(input); 159 | } 160 | } else { 161 | result = core.processBlock(input); 162 | } 163 | 164 | return core.convertOutput(result); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /common/src/main/java/com/joyent/http/signature/crypto/NativeRSAProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature.crypto; 9 | 10 | import java.security.Provider; 11 | 12 | /** 13 | * JCE provider used for loading in native RSA SHA256 signing implementation. 14 | * @author Elijah Zupancic 15 | */ 16 | public class NativeRSAProvider extends Provider { 17 | 18 | private static final long serialVersionUID = -8156926325751209127L; 19 | 20 | /** 21 | * Creates an instance of a JCE provider that supports native RSA via jnagmp. 22 | */ 23 | public NativeRSAProvider() { 24 | // This constructor signature will need to be upgraded in the future 25 | super("native-rsa", 1.0, "SHA Digest with RSA Native implementation"); 26 | put("Signature.SHA1withNativeRSA", NativeRSAWithSHA.SHA1.class.getName()); 27 | put("Signature.SHA256withNativeRSA", NativeRSAWithSHA.SHA256.class.getName()); 28 | put("Signature.SHA512withNativeRSA", NativeRSAWithSHA.SHA512.class.getName()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /common/src/main/java/com/joyent/http/signature/crypto/NativeRSAWithSHA.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature.crypto; 9 | 10 | import com.joyent.http.signature.CryptoException; 11 | import org.bouncycastle.asn1.ASN1ObjectIdentifier; 12 | import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; 13 | import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; 14 | import org.bouncycastle.crypto.AsymmetricBlockCipher; 15 | import org.bouncycastle.crypto.Digest; 16 | import org.bouncycastle.crypto.encodings.PKCS1Encoding; 17 | import org.bouncycastle.jcajce.provider.asymmetric.rsa.DigestSignatureSpi; 18 | import org.bouncycastle.jcajce.provider.util.DigestFactory; 19 | 20 | /** 21 | * Simple wrapper class for providing RSA signing using native libraries. 22 | * @author Elijah Zupancic 23 | */ 24 | public abstract class NativeRSAWithSHA extends DigestSignatureSpi { 25 | 26 | /** 27 | * @see org.bouncycastle.jcajce.provider.asymmetric.rsa.DigestSignatureSpi 28 | * 29 | * @param objId reflected to parent 30 | * @param digest reflected to parent 31 | * @param cipher reflected to parent 32 | */ 33 | protected NativeRSAWithSHA(final ASN1ObjectIdentifier objId, 34 | final Digest digest, 35 | final AsymmetricBlockCipher cipher) { 36 | super(objId, digest, cipher); 37 | } 38 | 39 | /** 40 | * Finds checksum by name. 41 | * 42 | * @param digest digest name 43 | * @return digest instance 44 | */ 45 | private static Digest getDigest(final String digest) { 46 | try { 47 | return new JceDigest(digest); 48 | } catch (CryptoException e) { 49 | String noHyphen = digest.replaceFirst("-", ""); 50 | return DigestFactory.getDigest(noHyphen); 51 | } 52 | } 53 | 54 | /** {@inheritDoc}. 55 | */ 56 | public static class SHA1 extends NativeRSAWithSHA { 57 | /** 58 | * Creates a new instance configured with the default configuration. 59 | */ 60 | public SHA1() { 61 | super(OIWObjectIdentifiers.idSHA1, 62 | getDigest("SHA-1"), 63 | new PKCS1Encoding(new NativeRSABlindedEngine())); 64 | } 65 | } 66 | 67 | /** {@inheritDoc}. 68 | */ 69 | public static class SHA256 extends NativeRSAWithSHA { 70 | /** 71 | * Creates a new instance configured with the default configuration. 72 | */ 73 | public SHA256() { 74 | super(NISTObjectIdentifiers.id_sha256, 75 | getDigest("SHA-256"), 76 | new PKCS1Encoding(new NativeRSABlindedEngine())); 77 | } 78 | } 79 | 80 | /** {@inheritDoc}. 81 | */ 82 | public static class SHA512 extends NativeRSAWithSHA { 83 | /** 84 | * Creates a new instance configured with the default configuration. 85 | */ 86 | public SHA512() { 87 | super(NISTObjectIdentifiers.id_sha512, 88 | getDigest("SHA-512"), 89 | new PKCS1Encoding(new NativeRSABlindedEngine())); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /common/src/main/java/com/joyent/http/signature/crypto/NssBridgeKeyConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature.crypto; 9 | 10 | import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; 11 | 12 | /** 13 | * There is an unfortunate naming discrepancy between BouncyCastle and 14 | * other security providers over the name of ECDSA. BouncyCastle uses 15 | * the string "ECDSA", while several standard library providers use 16 | * the string "EC". This means that regardless of the configured 17 | * provider preferences, JcaPEMKeyConverter will always end up using 18 | * the "BC" (BouncyCastle) provider for ECDSA. This is generally 19 | * benign because the same algorithms are implimented, and 20 | * BouncyCastle is often faster. However, it does prevent calling out 21 | * to NSS via the PKCS#11 provider. In the case of ECDSA, 22 | * microbenchmarks demonstrate that NSS is significantly faster than 23 | * BouncyCastle. 24 | * 25 | * @see PKCS#11 Reference Guide 26 | * @see Network Security Services 27 | */ 28 | @Deprecated 29 | public class NssBridgeKeyConverter extends JcaPEMKeyConverter { 30 | } 31 | -------------------------------------------------------------------------------- /common/src/main/java/com/joyent/http/signature/crypto/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | /** 10 | * Package containing cryptographic implementations for signing requests. 11 | * 12 | * @author Elijah Zupancic 13 | */ 14 | package com.joyent.http.signature.crypto; 15 | -------------------------------------------------------------------------------- /common/src/main/java/com/joyent/http/signature/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | /** 10 | * Root package for HTTP Signature. General commonly shared classes live here. 11 | * 12 | * @author Elijah Zupancic 13 | */ 14 | package com.joyent.http.signature; 15 | -------------------------------------------------------------------------------- /common/src/test/java/com/joyent/http/signature/KeyFingerprinterIntegrationCycle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature; 9 | 10 | import org.testng.Assert; 11 | 12 | import java.io.BufferedReader; 13 | import java.io.File; 14 | import java.io.IOException; 15 | import java.io.InputStreamReader; 16 | import java.io.UncheckedIOException; 17 | import java.nio.charset.StandardCharsets; 18 | import java.nio.file.Files; 19 | import java.nio.file.Path; 20 | import java.nio.file.Paths; 21 | import java.util.stream.Stream; 22 | 23 | /** 24 | * Utility stress test class for comparing generated fingerprints to 25 | * openssh on randomly generated keys. For example, to generate 1000 26 | * RSA keys and compare the KeyFingerprinter result to ssh-keygen -l. 27 | */ 28 | public class KeyFingerprinterIntegrationCycle { 29 | 30 | private final String keyType; 31 | private final int bits; 32 | private final int iterations; 33 | private final Path tmpDir; 34 | 35 | public KeyFingerprinterIntegrationCycle(String keyType, int bits, int iterations) { 36 | this.keyType = keyType; 37 | this.bits = bits; 38 | this.iterations = iterations; 39 | try { 40 | this.tmpDir = Files.createTempDirectory("keys-"); 41 | } catch (IOException e) { 42 | throw new RuntimeException(e); 43 | } 44 | System.out.println(this.tmpDir); 45 | } 46 | 47 | private String privateFileName(int iteration) { 48 | return tmpDir.toString() + File.separator + iteration; 49 | } 50 | 51 | public void keygen(int iteration) throws IOException, InterruptedException { 52 | Process p = Runtime.getRuntime().exec(new String[] { 53 | "ssh-keygen", "-t", keyType, "-b", Integer.toString(bits), "-f", privateFileName(iteration), "-P", ""}); 54 | if (p.waitFor() > 0) { 55 | throw new RuntimeException("gen cmd failed: " + p); 56 | } 57 | } 58 | 59 | // NOTE: need -E md5 for openssh > 6.7 60 | @SuppressWarnings("StringSplitter") 61 | public String readMd5Fingerprint(int iteration) throws IOException, InterruptedException { 62 | Process p = Runtime.getRuntime().exec(new String[] { 63 | "ssh-keygen", "-l", "-f", privateFileName(iteration) + ".pub"}); 64 | if (p.waitFor() > 0) { 65 | throw new RuntimeException("check cmd failed: " + p); 66 | } 67 | BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream(), 68 | StandardCharsets.US_ASCII)); 69 | String line = reader.readLine(); 70 | return line.split(" ")[1]; 71 | } 72 | 73 | public void doIteration(int iteration) throws IOException, InterruptedException { 74 | keygen(iteration); 75 | String expected = readMd5Fingerprint(iteration); 76 | String fingerprint = KeyFingerprinter.md5Fingerprint(KeyPairLoader.getKeyPair(Paths.get(privateFileName(iteration)))); 77 | try { 78 | Assert.assertEquals(fingerprint, expected); 79 | } catch (AssertionError e) { 80 | System.out.println("fname: " + privateFileName(iteration)); 81 | throw e; 82 | } 83 | } 84 | 85 | public void run() { 86 | for (int i = 0; i< iterations; i++) { 87 | try { 88 | doIteration(i); 89 | } catch (Exception e) { 90 | throw new RuntimeException(e); 91 | } 92 | } 93 | try (Stream paths = Files.walk(tmpDir)){ 94 | paths.map(Path::toFile) 95 | .sorted((o1, o2) -> -o1.compareTo(o2)) 96 | .forEach(File::delete); 97 | } catch (IOException e) { 98 | throw new UncheckedIOException(e); 99 | } 100 | } 101 | 102 | // Example: 103 | // mvn -Dexec.mainClass=com.joyent.http.signature.KeyFingerprinterIntegrationCycle -Dexec.classpathScope=test test-compile exec:java -Dexec.args="rsa 10" 104 | public static void main(String[] args) { 105 | String keyType = args[0]; 106 | int bits = Integer.parseInt(args[1]); 107 | int iterations = Integer.parseInt(args[2]); 108 | KeyFingerprinterIntegrationCycle test = new KeyFingerprinterIntegrationCycle(keyType, bits, iterations); 109 | test.run(); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /common/src/test/java/com/joyent/http/signature/KeyFingerprinterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature; 9 | 10 | import org.bouncycastle.util.encoders.Hex; 11 | import org.testng.Assert; 12 | import org.testng.annotations.Test; 13 | 14 | 15 | @Test 16 | public class KeyFingerprinterTest { 17 | 18 | public void rsaMd5() throws Exception { 19 | Assert.assertEquals(KeyFingerprinter.md5Fingerprint(SignerTestUtil.testKeyPair("rsa_1024")), 20 | SignerTestUtil.testKeyMd5Fingerprint("rsa_1024")); 21 | Assert.assertEquals(KeyFingerprinter.md5Fingerprint(SignerTestUtil.testKeyPair("rsa_2048")), 22 | SignerTestUtil.testKeyMd5Fingerprint("rsa_2048")); 23 | Assert.assertEquals(KeyFingerprinter.md5Fingerprint(SignerTestUtil.testKeyPair("rsa_3072")), 24 | SignerTestUtil.testKeyMd5Fingerprint("rsa_3072")); 25 | Assert.assertEquals(KeyFingerprinter.md5Fingerprint(SignerTestUtil.testKeyPair("rsa_4096")), 26 | SignerTestUtil.testKeyMd5Fingerprint("rsa_4096")); 27 | } 28 | 29 | public void rsaSha256() throws Exception { 30 | Assert.assertEquals(KeyFingerprinter.sha256Fingerprint(SignerTestUtil.testKeyPair("rsa_1024")), 31 | SignerTestUtil.testKeySha256Fingerprint("rsa_1024")); 32 | Assert.assertEquals(KeyFingerprinter.sha256Fingerprint(SignerTestUtil.testKeyPair("rsa_2048")), 33 | SignerTestUtil.testKeySha256Fingerprint("rsa_2048")); 34 | Assert.assertEquals(KeyFingerprinter.sha256Fingerprint(SignerTestUtil.testKeyPair("rsa_3072")), 35 | SignerTestUtil.testKeySha256Fingerprint("rsa_3072")); 36 | Assert.assertEquals(KeyFingerprinter.sha256Fingerprint(SignerTestUtil.testKeyPair("rsa_4096")), 37 | SignerTestUtil.testKeySha256Fingerprint("rsa_4096")); 38 | } 39 | 40 | public void dsaMd5() throws Exception { 41 | Assert.assertEquals(KeyFingerprinter.md5Fingerprint(SignerTestUtil.testKeyPair("dsa_1024")), 42 | SignerTestUtil.testKeyMd5Fingerprint("dsa_1024")); 43 | } 44 | 45 | public void dsaSha256() throws Exception { 46 | Assert.assertEquals(KeyFingerprinter.sha256Fingerprint(SignerTestUtil.testKeyPair("dsa_1024")), 47 | SignerTestUtil.testKeySha256Fingerprint("dsa_1024")); 48 | } 49 | 50 | public void ecdsaMd5() throws Exception { 51 | Assert.assertEquals(KeyFingerprinter.md5Fingerprint(SignerTestUtil.testKeyPair("ecdsa_256")), 52 | SignerTestUtil.testKeyMd5Fingerprint("ecdsa_256")); 53 | Assert.assertEquals(KeyFingerprinter.md5Fingerprint(SignerTestUtil.testKeyPair("ecdsa_384")), 54 | SignerTestUtil.testKeyMd5Fingerprint("ecdsa_384")); 55 | Assert.assertEquals(KeyFingerprinter.md5Fingerprint(SignerTestUtil.testKeyPair("ecdsa_521")), 56 | SignerTestUtil.testKeyMd5Fingerprint("ecdsa_521")); 57 | } 58 | 59 | public void ecdsaSha256() throws Exception { 60 | Assert.assertEquals(KeyFingerprinter.sha256Fingerprint(SignerTestUtil.testKeyPair("ecdsa_256")), 61 | SignerTestUtil.testKeySha256Fingerprint("ecdsa_256")); 62 | Assert.assertEquals(KeyFingerprinter.sha256Fingerprint(SignerTestUtil.testKeyPair("ecdsa_384")), 63 | SignerTestUtil.testKeySha256Fingerprint("ecdsa_384")); 64 | Assert.assertEquals(KeyFingerprinter.sha256Fingerprint(SignerTestUtil.testKeyPair("ecdsa_521")), 65 | SignerTestUtil.testKeySha256Fingerprint("ecdsa_521")); 66 | } 67 | 68 | public void testVerifyDefault() throws Exception { 69 | Assert.assertTrue(KeyFingerprinter.verifyFingerprint(SignerTestUtil.testKeyPair("rsa_2048"), 70 | SignerTestUtil.testKeyMd5Fingerprint("rsa_2048"))); 71 | Assert.assertFalse(KeyFingerprinter.verifyFingerprint(SignerTestUtil.testKeyPair("rsa_2048"), 72 | "1" + SignerTestUtil.testKeyMd5Fingerprint("rsa_2048"))); 73 | } 74 | 75 | public void testVerifyMd5() throws Exception { 76 | Assert.assertTrue(KeyFingerprinter.verifyFingerprint(SignerTestUtil.testKeyPair("rsa_2048"), 77 | "MD5:" + SignerTestUtil.testKeyMd5Fingerprint("rsa_2048"))); 78 | Assert.assertFalse(KeyFingerprinter.verifyFingerprint(SignerTestUtil.testKeyPair("rsa_2048"), 79 | "MD5:foo")); 80 | } 81 | 82 | public void testVerifySha256() throws Exception { 83 | Assert.assertTrue(KeyFingerprinter.verifyFingerprint(SignerTestUtil.testKeyPair("rsa_2048"), 84 | "SHA256:" + SignerTestUtil.testKeySha256Fingerprint("rsa_2048"))); 85 | Assert.assertFalse(KeyFingerprinter.verifyFingerprint(SignerTestUtil.testKeyPair("rsa_2048"), 86 | "SHA256:LP3pWCEhg6rdmE05GhUKbZ7uOZqsJd0sK0AR3sVoMq4")); 87 | } 88 | 89 | public void canColonifyByteArray() { 90 | final String expected = "9f:0b:50:ae:e3:da:f6:eb:b5:71:9a:69:ee:79:9e:c2"; 91 | final byte[] bytes = Hex.decode(expected.replaceAll(":", "")); 92 | final String actual = KeyFingerprinter.colonify(bytes); 93 | 94 | Assert.assertEquals(actual, expected); 95 | } 96 | 97 | public void canColonifyEmptyByteArray() { 98 | final String actual = KeyFingerprinter.colonify(new byte[0]); 99 | 100 | Assert.assertEquals(actual, ""); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /common/src/test/java/com/joyent/http/signature/SignerDSATest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature; 9 | 10 | import org.testng.annotations.DataProvider; 11 | 12 | public class SignerDSATest extends SignerTest { 13 | 14 | @Override 15 | public String getKeyCode() { 16 | return "dsa_1024"; 17 | } 18 | 19 | @Override 20 | @DataProvider(name = "testData") 21 | public Object[][] testData() { 22 | String[] hashes = {"SHA1", "SHA256"}; 23 | String[] providerCodes = {"stdlib"}; 24 | return permuteParameters(hashes, providerCodes); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /common/src/test/java/com/joyent/http/signature/SignerECDSATest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature; 9 | 10 | import org.testng.annotations.DataProvider; 11 | 12 | public class SignerECDSATest extends SignerTest { 13 | 14 | @Override 15 | public String getKeyCode() { 16 | return "ecdsa_256"; 17 | } 18 | 19 | @Override 20 | @DataProvider(name = "testData") 21 | public Object[][] testData() { 22 | String[] hashes = {"SHA256", "SHA384", "SHA512"}; 23 | String[] providerCodes = {"stdlib"}; 24 | return permuteParameters(hashes, providerCodes); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /common/src/test/java/com/joyent/http/signature/SignerLegacyConstructorTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Joyent, Inc. All rights reserved. 3 | */ 4 | package com.joyent.http.signature; 5 | 6 | import org.testng.Assert; 7 | import org.testng.annotations.BeforeClass; 8 | import org.testng.annotations.Optional; 9 | import org.testng.annotations.Parameters; 10 | import org.testng.annotations.Test; 11 | 12 | import java.io.IOException; 13 | import java.nio.charset.StandardCharsets; 14 | import java.security.KeyPair; 15 | import java.security.NoSuchAlgorithmException; 16 | 17 | @SuppressWarnings("deprecation") 18 | public class SignerLegacyConstructorTest { 19 | private KeyPair testKeyPair; 20 | private String testKeyFingerprint; 21 | private boolean useNativeCodeToSign; 22 | 23 | @Parameters({"useNativeCodeToSign"}) 24 | @BeforeClass 25 | public void beforeClass(@Optional Boolean useNativeCodeToSign) throws IOException, NoSuchAlgorithmException { 26 | if (useNativeCodeToSign == null) { 27 | this.useNativeCodeToSign = true; 28 | } else { 29 | this.useNativeCodeToSign = useNativeCodeToSign; 30 | } 31 | 32 | System.out.printf("Using native libgmp: %s\n", this.useNativeCodeToSign); 33 | 34 | this.testKeyPair = SignerTestUtil.testKeyPair("rsa_2048"); 35 | this.testKeyFingerprint = SignerTestUtil.testKeyMd5Fingerprint("rsa_2048"); 36 | } 37 | 38 | @Test 39 | public void signHeader() { 40 | final Signer signer = new Signer(useNativeCodeToSign); 41 | final String now = signer.defaultSignDateAsString(); 42 | final String authzHeader = signer.createAuthorizationHeader( 43 | "testy", testKeyFingerprint, testKeyPair, now); 44 | final boolean verified = signer.verifyAuthorizationHeader( 45 | testKeyPair, authzHeader, now); 46 | Assert.assertTrue(verified, "Unable to verify signed authorization header"); 47 | } 48 | 49 | @Test 50 | public void signData() { 51 | final Signer signer = new Signer(useNativeCodeToSign); 52 | final byte[] data = "Hello World".getBytes(StandardCharsets.US_ASCII); 53 | final byte[] signedData = signer.sign( 54 | "testy", testKeyFingerprint, testKeyPair, data); 55 | final boolean verified = signer.verify( 56 | "testy", testKeyFingerprint, testKeyPair, data, signedData); 57 | 58 | Assert.assertTrue(verified, "Signature couldn't be verified"); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /common/src/test/java/com/joyent/http/signature/SignerRSATest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature; 9 | 10 | import org.testng.annotations.DataProvider; 11 | 12 | public class SignerRSATest extends SignerTest { 13 | 14 | @Override 15 | public String getKeyCode() { 16 | return "rsa_2048"; 17 | } 18 | 19 | @Override 20 | @DataProvider(name = "testData") 21 | public Object[][] testData() { 22 | String[] hashes = {"SHA1", "SHA256", "SHA512"}; 23 | String[] providerCodes = {"stdlib", "native.jnagmp"}; 24 | return permuteParameters(hashes, providerCodes); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /common/src/test/java/com/joyent/http/signature/SignerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature; 9 | 10 | import org.testng.Assert; 11 | import org.testng.annotations.BeforeClass; 12 | import org.testng.annotations.DataProvider; 13 | import org.testng.annotations.Test; 14 | 15 | import java.io.IOException; 16 | import java.nio.charset.StandardCharsets; 17 | import java.security.KeyPair; 18 | import java.security.NoSuchAlgorithmException; 19 | import java.time.Instant; 20 | import java.time.ZoneOffset; 21 | import java.time.ZonedDateTime; 22 | import java.util.ArrayList; 23 | import java.util.Date; 24 | import java.util.List; 25 | 26 | public abstract class SignerTest { 27 | private KeyPair testKeyPair; 28 | private String testKeyFingerprint; 29 | 30 | public abstract String getKeyCode(); 31 | 32 | @DataProvider(name = "testData") 33 | public abstract Object[][] testData(); 34 | 35 | 36 | @BeforeClass 37 | public void beforeClass() throws IOException, NoSuchAlgorithmException { 38 | this.testKeyPair = SignerTestUtil.testKeyPair(getKeyCode()); 39 | this.testKeyFingerprint = SignerTestUtil.testKeyMd5Fingerprint(getKeyCode()); 40 | } 41 | 42 | @Test(dataProvider = "testData") 43 | public void signHeader(String hash, String providerCode) { 44 | final Signer signer = new Signer.Builder(testKeyPair).hash(hash).providerCode(providerCode).build(); 45 | final String now = signer.defaultSignDateAsString(); 46 | final String authzHeader = signer.createAuthorizationHeader( 47 | "testy", testKeyPair, now); 48 | final boolean verified = signer.verifyAuthorizationHeader( 49 | testKeyPair, authzHeader, now); 50 | Assert.assertTrue(verified, "Unable to verify signed authorization header"); 51 | } 52 | 53 | @Test(dataProvider = "testData") 54 | @SuppressWarnings("deprecation") 55 | public void signHeaderFixedLegacyDate(String hash, String providerCode) { 56 | final Signer signer = new Signer.Builder(testKeyPair).hash(hash).providerCode(providerCode).build(); 57 | final Date legacyDate = new Date(1_000_000_000L * 1000); 58 | final String stringDate = "Sun, 9 Sep 2001 01:46:40 GMT"; 59 | final String authzHeader = signer.createAuthorizationHeader( 60 | "testy", testKeyPair, legacyDate); 61 | final boolean verified = signer.verifyAuthorizationHeader( 62 | testKeyPair, authzHeader, stringDate); 63 | Assert.assertTrue(verified, "Unable to verify signed authorization header"); 64 | } 65 | 66 | @Test(dataProvider = "testData") 67 | public void signHeaderDateTime(String hash, String providerCode) { 68 | final Signer signer = new Signer.Builder(testKeyPair).hash(hash).providerCode(providerCode).build(); 69 | final ZonedDateTime dt = ZonedDateTime.ofInstant(Instant.ofEpochSecond(1_000_000_000L), ZoneOffset.UTC); 70 | final String stringDate = "Sun, 9 Sep 2001 01:46:40 GMT"; 71 | final String authzHeader = signer.createAuthorizationHeader( 72 | "testy", testKeyPair, dt); 73 | final boolean verified = signer.verifyAuthorizationHeader( 74 | testKeyPair, authzHeader, stringDate); 75 | Assert.assertTrue(verified, "Unable to verify signed authorization header"); 76 | } 77 | 78 | @Test(dataProvider = "testData") 79 | public void signData(String hash, String providerCode) { 80 | final Signer signer = new Signer.Builder(testKeyPair).hash(hash).providerCode(providerCode).build(); 81 | final byte[] data = "Hello World".getBytes(StandardCharsets.US_ASCII); 82 | final byte[] signedData = signer.sign( 83 | "testy", testKeyPair, data); 84 | final boolean verified = signer.verify( 85 | "testy", testKeyPair, data, signedData); 86 | 87 | Assert.assertTrue(verified, "Signature couldn't be verified"); 88 | } 89 | 90 | protected Object[][] permuteParameters(String[] hashes, String[] providerCodes) { 91 | List permutations = new ArrayList<>(); 92 | for (int i=0; i< hashes.length; i++) { 93 | for (int j=0; j< providerCodes.length; j++) { 94 | permutations.add(new String[] {hashes[i], providerCodes[j]}); 95 | } 96 | } 97 | String[][] ret = new String[permutations.size()][]; 98 | for (int i = 0; i < permutations.size(); i++) { 99 | ret[i] = permutations.get(i); 100 | } 101 | return ret; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /common/src/test/java/com/joyent/http/signature/SignerTestUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature; 9 | 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.security.KeyPair; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | public class SignerTestUtil { 17 | @SuppressWarnings({"serial", "DoubleBraceInitialization"}) 18 | public static final Map keys = new HashMap() {{ 19 | put("rsa_1024", new TestKeyResource("9f:0b:50:ae:e3:da:f6:eb:b5:71:9a:69:ee:79:9e:c2", 20 | "LP3pWCEhg6rdmE05GhUKbZ7uOZqsJd0sK0AR3sVoMq4", 21 | "keys/rsa/id_rsa_1024")); 22 | put("rsa_2048", new TestKeyResource("a5:75:e2:5e:54:0e:99:9d:f0:a9:16:8c:1d:88:dc:b4", 23 | "HRNiA5tYl1tjJKAh/Z7YAjq4ilXKs/jsbawuvtSNGQI", 24 | "keys/rsa/id_rsa_2048")); 25 | put("rsa_3072", new TestKeyResource("a8:42:93:a4:98:99:e9:ff:8c:c5:f1:d5:6d:26:2e:23", 26 | "3utlYbfy35NuXLFSLdd+ZTIm/l102INyMM/Dsx+7Rzw", 27 | "keys/rsa/id_rsa_3072")); 28 | put("rsa_4096", new TestKeyResource("82:c2:6b:ad:41:b5:dd:65:88:ec:46:d2:a6:4a:f0:9b", 29 | "oM3St/9DeJslewe4G9BxWcBt8P0L8OVVTEqGT0OBaZA", 30 | "keys/rsa/id_rsa_4096")); 31 | put("dsa_1024", new TestKeyResource("5e:9a:ce:fe:5a:24:f9:7a:06:d8:94:b8:e4:ae:c4:99", 32 | "VKNhg7WSNC7PYPHkUoe2CEXcYRR67clcjsQEJQ3jWWE", 33 | "keys/dsa/id_dsa_1024")); 34 | put("ecdsa_256", new TestKeyResource("d3:11:55:74:8f:25:78:f0:29:2a:e7:b7:30:ed:3d:a0", 35 | "02guwBUSayfOYL5AgTvQ9KnSnK4d31OWPRf5berK9aI", 36 | "keys/ecdsa/ecdsa_256")); 37 | put("ecdsa_384", new TestKeyResource("c7:b6:fe:e5:64:70:8d:03:ec:f2:a8:56:b2:fb:10:16", 38 | "9Wvr6LiU0Tb2auluS+F2MiIFrIUvuAjcoz2xSNwL6s0", 39 | "keys/ecdsa/ecdsa_384")); 40 | put("ecdsa_521", new TestKeyResource("6d:7c:31:25:03:15:a9:94:f7:0c:eb:ed:72:91:91:ac", 41 | "O7LoGu1rijNCSkCsho/43x+ffBAsSQf+sKz36/h0HXI", 42 | "keys/ecdsa/ecdsa_521")); 43 | 44 | }}; 45 | 46 | public static String testKeyMd5Fingerprint(String keyId) { 47 | return keys.get(keyId).md5Fingerprint; 48 | } 49 | 50 | public static String testKeySha256Fingerprint(String keyId) { 51 | return keys.get(keyId).sha256Fingerprint; 52 | } 53 | 54 | public static KeyPair testKeyPair(String keyId) throws IOException { 55 | final ClassLoader loader = SignerTestUtil.class.getClassLoader(); 56 | 57 | try (InputStream is = loader.getResourceAsStream(keys.get(keyId).resourcePath)) { 58 | KeyPair classPathPair = KeyPairLoader.getKeyPair(is, null, null); 59 | return classPathPair; 60 | } 61 | } 62 | 63 | public static class TestKeyResource { 64 | public final String md5Fingerprint; 65 | public final String sha256Fingerprint; 66 | public final String resourcePath; 67 | 68 | public TestKeyResource(String md5Fingerprint, String sha256Fingerprint, String resourcePath) { 69 | this.md5Fingerprint = md5Fingerprint; 70 | this.sha256Fingerprint = sha256Fingerprint; 71 | this.resourcePath = resourcePath; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /common/src/test/resources/keys/dsa/id_dsa_1024: -------------------------------------------------------------------------------- 1 | -----BEGIN DSA PRIVATE KEY----- 2 | MIIBvAIBAAKBgQDFB4NcAuhG8rRk5PJqci61mr7kJaT2XuaRU2S5huwS80Yf2uSt 3 | WrpNoyoNHJv0Uer9QUVy7gWnQ710P1YMXdkzlCt6KTxAJCw0Pt/htg/ZKfSlLLxh 4 | k0vJdneS8OjWMz0zlBS1Zb1eMcxp/AetmI+2sfQp7pbrgoAglMy5NeIdzQIVAMKi 5 | S1NYGmz+qsvelQ8x7uNRSQtVAoGASLyH6SovjEMbIlMgO46+0fqu72jAQN4Smdb4 6 | iSCdl26Fkp8K6WL0sZPw1lsbGA8uEdHu7XzfYeO2/DJ2bgQ6E9iYhaKBgjVj7jP9 7 | m7MpXwJpyew9JhXN31Ja2S2O5Vr9gRKshhymUO4cTxarPejSujNlbBAopNvk7RZ7 8 | /cKDzxgCgYEAuHWMlE9dgZPPyr5c0SdpiHNHLQ0dduWPnwz1VAxBinHBxtCvpGB5 9 | +yqUKMnaBvah6dlImZV7QkffpShDbegmmRVJoYxXi7eUE4Znmbgjv+dpEeh+ZfuZ 10 | ChXqDJ64iZPTxRQqFU6GUW1VBEE/GDJRCNfw0RTpDvkFWw2xhwx3zPwCFQCuqT8p 11 | kabbx35DV8UUfubw6j9rGQ== 12 | -----END DSA PRIVATE KEY----- 13 | -------------------------------------------------------------------------------- /common/src/test/resources/keys/dsa/id_dsa_1024.pub: -------------------------------------------------------------------------------- 1 | ssh-dss AAAAB3NzaC1kc3MAAACBAMUHg1wC6EbytGTk8mpyLrWavuQlpPZe5pFTZLmG7BLzRh/a5K1auk2jKg0cm/RR6v1BRXLuBadDvXQ/Vgxd2TOUK3opPEAkLDQ+3+G2D9kp9KUsvGGTS8l2d5Lw6NYzPTOUFLVlvV4xzGn8B62Yj7ax9CnuluuCgCCUzLk14h3NAAAAFQDCoktTWBps/qrL3pUPMe7jUUkLVQAAAIBIvIfpKi+MQxsiUyA7jr7R+q7vaMBA3hKZ1viJIJ2XboWSnwrpYvSxk/DWWxsYDy4R0e7tfN9h47b8MnZuBDoT2JiFooGCNWPuM/2bsylfAmnJ7D0mFc3fUlrZLY7lWv2BEqyGHKZQ7hxPFqs96NK6M2VsECik2+TtFnv9woPPGAAAAIEAuHWMlE9dgZPPyr5c0SdpiHNHLQ0dduWPnwz1VAxBinHBxtCvpGB5+yqUKMnaBvah6dlImZV7QkffpShDbegmmRVJoYxXi7eUE4Znmbgjv+dpEeh+ZfuZChXqDJ64iZPTxRQqFU6GUW1VBEE/GDJRCNfw0RTpDvkFWw2xhwx3zPw= java-http-signature test 2 | -------------------------------------------------------------------------------- /common/src/test/resources/keys/ecdsa/ecdsa_256: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEIAczHgb1G5zEDUVj9j4+9SPe7bOuMbH/ujtPlE+9A2yFoAoGCCqGSM49 3 | AwEHoUQDQgAEj8rtaW/LNlfUQhPN9CZgV4v9/ybiNpq8FMIWZrjNCRoIlwj+o6G2 4 | tevOhJfVFsmBYPXliOoncqnstM7Boovg5w== 5 | -----END EC PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /common/src/test/resources/keys/ecdsa/ecdsa_256.pub: -------------------------------------------------------------------------------- 1 | ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBI/K7WlvyzZX1EITzfQmYFeL/f8m4jaavBTCFma4zQkaCJcI/qOhtrXrzoSX1RbJgWD15YjqJ3Kp7LTOwaKL4Oc= java-http-signature test 2 | -------------------------------------------------------------------------------- /common/src/test/resources/keys/ecdsa/ecdsa_384: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MIGkAgEBBDB6jUp6sFTu0KLcN+RunsTrgy1fVLLfNXrRazHO31vEv+wZ8MlBXZkw 3 | DInCMMjYMwOgBwYFK4EEACKhZANiAAS3IY9P64s78voSAS6ELdnkjEY+jM+oIXI8 4 | MPXuEknB2gNQ7UdZkjtHHHL+eBiTyyHc9M0nMBKkYje8t1Ej+p74Bi7uXL0rKGuT 5 | o/lcxgyfWefBvxZO0Rz3Wahps+YZnvo= 6 | -----END EC PRIVATE KEY----- 7 | -------------------------------------------------------------------------------- /common/src/test/resources/keys/ecdsa/ecdsa_384.pub: -------------------------------------------------------------------------------- 1 | ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBLchj0/rizvy+hIBLoQt2eSMRj6Mz6ghcjww9e4SScHaA1DtR1mSO0cccv54GJPLIdz0zScwEqRiN7y3USP6nvgGLu5cvSsoa5Oj+VzGDJ9Z58G/Fk7RHPdZqGmz5hme+g== java-http-signature test 2 | -------------------------------------------------------------------------------- /common/src/test/resources/keys/ecdsa/ecdsa_521: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MIHcAgEBBEIBZPAd2Xfstiob/3idpZP1qsLpwQCgWywygMgZCdp+FZKxXm1vHa34 3 | ImnpBZQq0Ji7pYMG4IgTSoQZdAPkX/ixozWgBwYFK4EEACOhgYkDgYYABAGN+Bh3 4 | TRlPnGBUIwxykjrzhsu6LSbJ0OYhZPvkoIlCj9TgQwo4iipOLrwPnXf8J0DhTDJx 5 | b1ZiIOkvcsrqP44cVABWVifWnaFpapKV0xF7j6BQiJGYphyaEaWVYPbuHnJYsFy6 6 | Lc9YIH/DSItU2GRKg5cJXvnAVbZwgd+Qw1o0Ml1jvg== 7 | -----END EC PRIVATE KEY----- 8 | -------------------------------------------------------------------------------- /common/src/test/resources/keys/ecdsa/ecdsa_521.pub: -------------------------------------------------------------------------------- 1 | ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAGN+Bh3TRlPnGBUIwxykjrzhsu6LSbJ0OYhZPvkoIlCj9TgQwo4iipOLrwPnXf8J0DhTDJxb1ZiIOkvcsrqP44cVABWVifWnaFpapKV0xF7j6BQiJGYphyaEaWVYPbuHnJYsFy6Lc9YIH/DSItU2GRKg5cJXvnAVbZwgd+Qw1o0Ml1jvg== java-http-signature test 2 | -------------------------------------------------------------------------------- /common/src/test/resources/keys/rsa/id_rsa_1024: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXQIBAAKBgQCvlCD6FzwBSmMBN0cT+3XXOk2OzCYVZLIEzRXnBazCa2Uc5Bx+ 3 | VhXssSefihCDC9AkLr4ukxytF5S9fxFFmrgruH0Y1izdhilzsTb4Btv46DWQI7ec 4 | se4E4/yKHFWL/lZN5JRXYVuf95rx5D3hzVEktcOMGCDzBqMRFPibg142mwIDAQAB 5 | AoGAV5DL5Y6i6Y3c1KVp+IhH3CCCv74HHFJNCmHyE3bcE1aWZhNKNqPeaV5tAX9M 6 | /t5sXy9EwFu0SItjLiLqTUl9odSn7Z5INydi6MW28VXCFXcMKrC9fQ+7Us8ApQoi 7 | NbxnVId945E+mgYbHepczDtH+X0pvxnAAUjvHE2HbNnbWMkCQQDfxgN/RjQiH1Ds 8 | Vy33C1UarjSAWXBIa6aeiGYzVKq/RPFMGM6T890wG1zRSKCdpxheBbXgzkdNAYfa 9 | u2A+cWBXAkEAyN1JkFg+K085QDRUyF14rU0ycthaCKbKzy6KshjHANLeNOIdM12J 10 | nQPAvE+km/nej8e0FjL7GzoHou2o6XUhXQJBALwIyhuCxH//zKIsR6WV0jePNvK7 11 | NQzF2MbSWv67IJeEuTZ2ie/tNWodNAtwefa/2Ev5hHGZJHTi2mPEnAUchBUCQE5d 12 | BrYptxducQd7YxhIXyw9UdE8VuPJLpZlsfaJdBn+KnK3RG0lsOpw7GCLMc0oTowJ 13 | fVirywt0OEZ4Mc3ixIECQQCnBqShQcfCnWfVluQaWViJIOG0joHp8RiggIAVgt3Y 14 | E/6vTCtTQQRmePkIWUup38HiF+EPdXdT6Unn6PMCpfr+ 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /common/src/test/resources/keys/rsa/id_rsa_1024.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCvlCD6FzwBSmMBN0cT+3XXOk2OzCYVZLIEzRXnBazCa2Uc5Bx+VhXssSefihCDC9AkLr4ukxytF5S9fxFFmrgruH0Y1izdhilzsTb4Btv46DWQI7ecse4E4/yKHFWL/lZN5JRXYVuf95rx5D3hzVEktcOMGCDzBqMRFPibg142mw== java-http-signature test 2 | -------------------------------------------------------------------------------- /common/src/test/resources/keys/rsa/id_rsa_2048: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEArcDUQ81RRfcdZ8i7nnY7NMIZvaHTHd51g8f46I3rHmTwoHq5 3 | 2PTjFST2r8FCCWF4sNV0c0ng5zKxOhgvpADY7EG4nkfnJIV4M9Zklrn84C7nlfoA 4 | NicDG3AsJ1emTuVIZuMw6rH+b0vWThNAYlikvGsTEUxWbQJZGQvrHRiPZ06c/I+9 5 | o7wnlCYXfQIcrWfV+btf2k24zeBODHa6aSSEKCdo5/iFTVI1nPrw9oC7xg/+qkEW 6 | NoNtEfZafyDNSVkg1E3sG7oNeIz8UTeKkAwMhRFWY8R8s2lEyIdc/zB1yH3VjG62 7 | 1GRKws9zQPZepDR9+GijS6cWCTCN3FMsZIm+8QIDAQABAoIBACX1Iynwt5mOQ4lB 8 | q+MRNxvub0sjCAjVaVyxmenL3xpTuNGGHgWMcfNTkOSSVofQGFqhNwBcmlpHk7n6 9 | QKYWXU03ikphsNgkpD7GzlCzLtAWzxbPd65c1WvwFGU/26AOrMN1TGFpIwlTSuzh 10 | fcY+UTasOY0k3RiRU7+KAAHqNKG/iW4DDKH1jCnGgJUp20eYI+ab7W/T0MtCtHKh 11 | EsFohEb0WVL0mXeCRBjK76DAUvZnNjA/NcvhcTs/hd0Z3w9ZGKVTZ1UP77IwKv0f 12 | pRt5vS0Awa00ROPtIbpQjvk9qOW42h/Q6ZU3GLuqiDA6/CrgMB15PpXPa3s6jiEV 13 | UNoDI+UCgYEA4H7/4rWCg7P18lj7FQanEGmjOggVQMSI4+3yPgt6GVgIk4bqKH+4 14 | /82DUBaSo70RQNamRLoNYM4a5MGDxMT5sQKAZQ5RfqWZnslPMiSjWCPaow6gw9c6 15 | Dc49/unMF8YHz1bwYtz5jng0DHnUYPHuGQ/kRQf/Xa+WYmVnHEhfEaMCgYEAxiLl 16 | wthMzmP+ZOjn0eS9BpmXlWt9ml3IpCeJvGN422+bOb0k0DRB1WVYCxeu8aHIpDH0 17 | b7G5UEoQ+awYTQhUowplEuPdzpEmzizLGyhbzQYu+1JQufusFTXulNIGCxbeF+9P 18 | fa4hDKuIykVneAWOvX76WE8ZS0PpgCwsGGszPlsCgYBkUZw7TO0juXmAcK7SuIRT 19 | W0Frn00QAkNBx4TmoO5RAPvkIPfmXRzogofZdBzXOpfJJ6JpN9LenXt7GdauSW9i 20 | 4EFL51gXhhzdzgur1p9/tZXwI4tHuJfir3UYwpVHUmEPEESfiSujtKddo9t57uJ9 21 | JJLXHjnJPbuwAKxRAS4qjQKBgCAZ41PQK5F8zeiXe4ecIawCEncY9T6fVzSRW1V0 22 | YE5dJK8UH/TDoq7qPnt9mNB5P8KqfUnjZ3zEoz9wTHeek+fedUW8OWGQTtM0mquJ 23 | vGXZTJ9XVNDtH5JILKFb7bK6P71s/loEbkJZQE5VNFBTxi6ZdgH8vsJc/GXRwksq 24 | gyDlAoGACLNP/v7Rs2Ui9oJE401RomT6UDX2I15nd514lbWVeywR/+yEf/HDPqgF 25 | ZExEK+0rxmh4diFpsAgJGKKiB9dims0DxtEdV75tjidyZZNwI8Yp/lAVJrHVSK6m 26 | B32JmbLT+uMkzWIvxy62pMQ6sNKyVfGlf2Cpsa3Gx/1JXe2/QCM= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /common/src/test/resources/keys/rsa/id_rsa_2048.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCtwNRDzVFF9x1nyLuedjs0whm9odMd3nWDx/jojeseZPCgernY9OMVJPavwUIJYXiw1XRzSeDnMrE6GC+kANjsQbieR+ckhXgz1mSWufzgLueV+gA2JwMbcCwnV6ZO5Uhm4zDqsf5vS9ZOE0BiWKS8axMRTFZtAlkZC+sdGI9nTpz8j72jvCeUJhd9AhytZ9X5u1/aTbjN4E4MdrppJIQoJ2jn+IVNUjWc+vD2gLvGD/6qQRY2g20R9lp/IM1JWSDUTewbug14jPxRN4qQDAyFEVZjxHyzaUTIh1z/MHXIfdWMbrbUZErCz3NA9l6kNH34aKNLpxYJMI3cUyxkib7x java-http-signature test 2 | -------------------------------------------------------------------------------- /common/src/test/resources/keys/rsa/id_rsa_3072: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIG4wIBAAKCAYEA6RhtlzAbb7cAi2lR10LnqGb0LIFJCTAcAyG5Hz3d+8Qyh3IF 3 | ox0XZOCBFqswpccHg+Rz4wfavOFHO638BKZ5erIQUMM2NtG+3U7PJG34vYluhTmZ 4 | mX3qjFi80nERaSW4jYIh7ECIe3+BOz5SfY3C4SPoO3gqB/EbK/viZofnCSoOFNHF 5 | Q/LBpk+Oz0HjJQbY87lNEFVaw8LZtA/EodV5SPXRVR3dphs/AFbUGMhxkeURNaMe 6 | IB9++CFmzgy0ytQ5gTvqpbbZIc7bV9GgMbyYkbVYPpQOHwFwSwb1lMLafBMjmyOm 7 | VNHya0GzG2w4cxvF3xRM6hucEtSdGqC74+jIg5nw9ltiVKV14JQYISOLoEaYlI+B 8 | 5ifr6JYJ1nJ8qHidNMnMKtu/ys4rqgMW9Od3vmjX8+4c+HYzYwM3n87iY9bmTmay 9 | vJaMTdVexIGXiGwS4SvWcfrg2UxkF4+f4B7ErN5Y1rv28CEFkIq8GmrhFGHGQH9k 10 | /Cko0SRk2bWlWBhhAgMBAAECggGBAIgIeJSeP7TPwSKbQWb2JcoS43dBsEULbZ2q 11 | RaZk7bLrym1fbsjFxoxZtRxspcRpxgoD/lnFkLNgabj7jbONECI53umzM2WEvPXc 12 | 82iYQW0j0nsvpWN9p69mbjQGRpntqKn1AyUkeEJSpDHV6kBZxhysXvCt7SGbZTcj 13 | FfquX3fqsjMWRSClgLw1e+ua0RwfxedfAgmBNqo+gdziwueLdO/Ofe9FscAp8Heb 14 | NMaN3DG/1G5QFtm6Z9gZbizcH3qrpRJjJcd46Q/l7dm6o1AhCEBCtNG4CUxlGLCB 15 | XwaQjZY4kpAGvkGFpGgZOdfLdHTTzagMR+IgNWTTXzfUwMO3HQq0bZS9zZ8+E+DZ 16 | OVE0WSASBMvoMFIPYsPrHnioamFrEGrBJgMtw8Ie4CvYznSvlTYeEVTmwEVeVZ2W 17 | aRXX24D8hHYbuA3Q9ehKbvuFxhi88TdJ897BkvvCgJ5qmJmW5Ny4aHwz6fpcBh8O 18 | 5ujIK5ihRVjff93QGww3XJ5ZTCupnQKBwQD9kQZdAl8zE+S+f9oQCY7CQbh31/e0 19 | zcyaECGW4UCb1APRtVKqOdNAnvQknEuAMlIPQ0sZ7zgqlutrslawrt2OhIgmlzF6 20 | YWNOL/+Ee0OZ3cCAoi17sLVSclSDODqI7EIuc9gB9qSGSo8Q9hEZ7zmB6BnecavQ 21 | Rj5Z4tYgLyffpauh7o5UeMuFVr0CO7uZBcOE9P6CgxKhUGO8Efkz+zzHfH0ibro1 22 | R3GIcM/kCNr0pJihDWU3PpoyTyc/xJRK93cCgcEA61Ub3H7z63vvDswub3k+2mOL 23 | 08tclATfXXG/ZZIOdI5yxgPrxUm1tUVCsgyxVFafyAMuR+4QrOLU7deS4QT+xwzo 24 | L0OpuHW6xp5QTDE7Qg9BMMMWKovZllWZCmJ/lSJBAJUX8mLLXQVXCS85SlOTCUUu 25 | pV2Qg7OFf5kte6De7d3VfIKmUGbRwXwFByz7h+1Xdtv5RSqep1Mjr3NZk8xrtoU5 26 | dCna5D3SNJrnS4Gslqx6fqz+81WvVCA73qSC7pTnAoHAAg3xwkEpqk1H9i8tAiJB 27 | sNs9dmMek61zMfBdhU5v6pNOMJ3KfKyZRJXFHqLRjclaltsRubURfG7am3XHdAkG 28 | rIfwXnetI/O0DUcgR9+DzbRZQfGOcskcvv1EKAgFmWRoqN2xa1qnPJtQC3Ai9VXV 29 | 3nKV+xJqtPZMvQe0enBHX22FNXusVYzhAUSRpXn8bCH60pH1mfMb7IitNjHjFgO8 30 | G1Wr4eKJybK5fcSEOrHDrGXFAtEL9+We9Ddl9wNNvEnDAoHALPJLaPiiU7aO+Q+M 31 | SuQoWgOrQB9WJjssAifoe1jDBAX069bx3/NtDhsVWvZLpCULTWM2IqzYaHzGWErV 32 | Fb8jfhb7/ZL5xm5N5wNzqKm6fKp3M/3+rM+bFjhxC2e2oGAzdA4FzdiKqNCNSAbJ 33 | 9WUy7xFUkGbnZBAUjkNOSY93JI066hX19+M7aq3qM+2meNae3ueym0BBnwmlBiyM 34 | zkq7w9d8GrCnyvH3xO5VALnaoch9eNZHt2gFHZcueB6Jl1G/AoHAe+5QV132r72y 35 | fuOhSLy/RQ/wIfvZykYP6f3UBz+zagAHPyVvNCjDBMrgKlpnEJTOOoq0Fax+MbVF 36 | DVHlgxZjqrfYntV7eQfaKkKVA7t5fS4HpUFFmUChwPvrhwnR1xGhHEfEp4fwOl6z 37 | hXE8XaxFA9XEsCfPK8CQ04oMMB6hGFVitka/31eMwoh86Zh3W/j6bEsjuJdnbkkX 38 | GEVe7Z8QPS7l+/vVC3afWkyMiEwzkyyhTxqvTfz56GIIW0lJpNPH 39 | -----END RSA PRIVATE KEY----- 40 | -------------------------------------------------------------------------------- /common/src/test/resources/keys/rsa/id_rsa_3072.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDpGG2XMBtvtwCLaVHXQueoZvQsgUkJMBwDIbkfPd37xDKHcgWjHRdk4IEWqzClxweD5HPjB9q84Uc7rfwEpnl6shBQwzY20b7dTs8kbfi9iW6FOZmZfeqMWLzScRFpJbiNgiHsQIh7f4E7PlJ9jcLhI+g7eCoH8Rsr++Jmh+cJKg4U0cVD8sGmT47PQeMlBtjzuU0QVVrDwtm0D8Sh1XlI9dFVHd2mGz8AVtQYyHGR5RE1ox4gH374IWbODLTK1DmBO+qlttkhzttX0aAxvJiRtVg+lA4fAXBLBvWUwtp8EyObI6ZU0fJrQbMbbDhzG8XfFEzqG5wS1J0aoLvj6MiDmfD2W2JUpXXglBghI4ugRpiUj4HmJ+volgnWcnyoeJ00ycwq27/KziuqAxb053e+aNfz7hz4djNjAzefzuJj1uZOZrK8loxN1V7EgZeIbBLhK9Zx+uDZTGQXj5/gHsSs3ljWu/bwIQWQirwaauEUYcZAf2T8KSjRJGTZtaVYGGE= java-http-signature test 2 | -------------------------------------------------------------------------------- /common/src/test/resources/keys/rsa/id_rsa_4096: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIJKAIBAAKCAgEAqJT1V4lXUFKUikh7HYEjILEpK1Z7zTf/2VpOhBGYnxq8RaZF 3 | 5vUkU2c0QbcaNJKuDpMZvobBZmfPd49HuP68nCbXqGbIGxBi58l3oklAwWLy6WhM 4 | +2YbuYszYFuLrk6EwcJHSQhcb/TyvQ1+/cgdQNCgsb6ZMVS2SM/KzsSLmWOUQgsL 5 | JjGjY/HHoL6qlPwg1UCAOk+2P+FpujXT6DoL5qyYguW5h/zHDRKn+jlkiiVJ3HG0 6 | 2wqtoP38q7Td6PwRYkqrwEV0UEMUDLqP25Eov99f4dAZgQUbl+HpBL69HYEv5/eq 7 | ybtlF4l+uyH89HqzHrIhODZd2xPwtIMZYBfwzZKeiOxQPoi44g8cyumn4HyH1sLa 8 | vLGqgfpY4zpftO0DwPzbiQp1vKai0NX+9IJpHXqpn1mLibw7HI9C0IR/+ADcemPD 9 | L1hYBnzgASfZA+0GwMRA4Yt375fSOp/6vLVMU0/XpsSD57C4WjkQY0YmBDWWFq4R 10 | gDV/DHzRzJrlGQjgdnAkpQRj7l37H3ccgz3Y/BO2cJY0Tp9up/xT1uRbROq0Fr/B 11 | j3ohBZjLtzS1WqoNGSNGJVw7vKf4mDoykpjRJppU6Rw8TxuZZ2UGm6dXfSVUgAh8 12 | WmAhLOrHhjzDxsKL48tXqqwzf9xlm681XBETK4/J89FTpfHjYsKPF+fuyFMCAwEA 13 | AQKCAgABk6sbz4AOk0cqZ/Um2H9gneeZrSxpRf3RpaOYXqXDzzc1m5GJ0H8vfIaj 14 | mrfXCAwyvLN/8bi7YXsyThfIpjFTcJWR0KxMfIHAU0UYMoE1jdxhzITz0QevW5r2 15 | asTKs5dQTKXoC/j5XxwqIocujoGfipQGNZcvO/Cci70E5Be+Lo1DdOD80XgKxkcs 16 | vLvRuu3/sKPCZ953iH85q6k4wdo8pR6kEswCdp3SBHunvoOvOxwOwM2FQvtOxzFm 17 | 1cFBnaH3/mTO3pdw7L+yQsh3sErmSIuYDB9A8rBIYmeQkMwqC3xMOifRzTvr1XBn 18 | BbFdBAzK27Wt1exY0T3uIk/Jc0/gSHzX5acO13f3MZbXt+D5G6Mu5ALJJQgEdTRo 19 | 2QS+EEsYSGqt3U0u3yvH3+WIeTbW+CpZGDvlviNTRXgPXLF2UzRlxPePXqLxMFmB 20 | BjbgLGIDFM7eLcFPya+fjtE8M2skAQlWZE4ujpu2OZIVYZOfLEmg6VJdM4S1LeUN 21 | oRy5JqXLLApfnzv/ZX8upuOiPIva7QyXS6F0jdekDT7L6C9kRK6YrI/CtGXHGH6C 22 | 8Y8g6wjkjEgAG61agymv+/kKR4JujhR+o53qbryRZp9ugYwlw7hVy9kJm2DrrwtX 23 | DXVyGMfZEhO8amRt8gISI+zCCGG8rKf+K/JCY/YYb1Na0nqjgQKCAQEA0iYfRwi7 24 | He8PAzw7gXDzup6KJL1UQPafLaaZQiX3NlePjeLkvGQ88xem6m3yECc+OK/SCkVO 25 | pw5xeRU3T5NSx2FfjtrtUEhloJZiNs5Gu9EdLnwZeacysKcf/OVtJFtT64kvPaLB 26 | KUm7zy8mtU6c8+5+eXb9RUbCLPhHddIpOZPRespeJPJmPd2mcGqqSJQitvp9CxwL 27 | IPT0WeBGYKC+Oxq5pDDbBC1JzOr1NSWr9Me1f85xD/d7dihGmhWsXd5aFcy38Ty5 28 | nMB31GjCXTLj5BSpIoGFgNybN6G1DafiyiPLYANWe+5S1U/0PMel/Q2IG2dW4ETU 29 | 5zpzWsPUoneIwQKCAQEAzV0bWAnqxD06Yk8ZwUvPr9UIdv5wc47uZm9yt6p2tBnJ 30 | O568iO7DoA+HmfXrKkvReWLu5Mj0fYz3mMEWmGQTnWhSo+iI8cwMEQkmyP4MboWg 31 | vKDfMi9PfOvxG9yB0zM48v1FJOrQK3GcoAqZ1ioyQbzPWhL8GMRRk9MJdfHVwzYr 32 | FUgK5kjCYrE/XXClhA5ciMkr1Eg2bG+5HHWWyVidNOA1yRzp2Y+3gbS5nc72DXet 33 | e/uCUYC67GfaVBieK53+1fJiZnxuYnL6wsz8YNbKltHKSxzi5bq6pBqAQAXxPVYz 34 | LZpIcRDVJSV2K3nXAv7u4RraxkfjvghngPBefGYiEwKCAQBpIJsK/QomWl4HP02B 35 | XpD+7aqr4G7SpM1SmAIZPkbiT4FH+JZ2zCpVbaKZqfCrNgaBcys+6hcDqU0Ixp1J 36 | 2KhTh5m3LDwFqoH0iOaPBjgKYv0XMQvJeOP5zVFKQTeNNYbQ0GrqiY3Z6sHiTnMt 37 | xirsOIXam7TtwA0JVBbjkQQAgrsLk0rvbrfJASXvHN0cu8Z2BO8i7J+AmCnZAEIU 38 | urUPejoeMjaOa118/pk2Qic4p+NbEVosI6EGpfVTxb3Zo48rUqM+MiOkkAAiKVn6 39 | J6grhE5sLaEsqq+Xb/TT5QWmG6v01xafnsSfY4UKFtIpumg5VUP3o1diaTIl2Mt5 40 | jxTBAoIBAQCuIa6X3gDU2N3ykgPSpQe7njWJQIH1WlLHROTO25m/aMX66cQPj/UM 41 | eV6Pq7f1q0g5Vywdjk/RTYB8Y+1W1jTTmavJE9lavZvVrh7W+qNr6565kubuVLNf 42 | x2gwKa9jd+FoGF1SylAtJi6afaKnuCvZFdH0HHjqPzy0zZMOETsiRYIOj4G+dh9t 43 | M8H45xVRfiP0UC/71idflvQtL3/lxl+2gA7bMssPEQ+WyblCGEUGcl4F7OM5XKAi 44 | 6IR2HlGvxk5Y9yd8ozGuBLSgDNRLAJXTgJu5SQsTpFux3k60+tTtH3RRzKp0RMOW 45 | tccXYj5wad3/vZeX3Uk8zz+EU0yqyqdFAoIBACfxPlfmgN9oc1T9nrGYHr6TaXaF 46 | ZJn9z9t3ZK+3px1B+HoFU/6FEoSuVGPgFFcWF6cFF3q5wb8iBfNqxg1KoA2JZt6t 47 | /29axzf/vUPkhWg+mvMaGs/SsTDl6Z3auawpQ6QVPxuuzlSqx51Wq2Gp4BxIlouJ 48 | LHGtdse50ow94P42L8LgTbwQHU36KdO1A3/d8NvvGj+VwfwrmBKKSXTrQ/7jY1Nb 49 | RxQ6QLqAeR8ehvb7A7M+ZHGPxcgDQ8e5bZ0HSkNkhfUO3y9MIG1ttRhGWAvQcdRp 50 | hM+Qo+26Blb3x0H6/e53ERlpW++g4edzj2+r41irjsPI08m2cxg5l3yej+E= 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /common/src/test/resources/keys/rsa/id_rsa_4096.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQColPVXiVdQUpSKSHsdgSMgsSkrVnvNN//ZWk6EEZifGrxFpkXm9SRTZzRBtxo0kq4Okxm+hsFmZ893j0e4/rycJteoZsgbEGLnyXeiSUDBYvLpaEz7Zhu5izNgW4uuToTBwkdJCFxv9PK9DX79yB1A0KCxvpkxVLZIz8rOxIuZY5RCCwsmMaNj8cegvqqU/CDVQIA6T7Y/4Wm6NdPoOgvmrJiC5bmH/McNEqf6OWSKJUnccbTbCq2g/fyrtN3o/BFiSqvARXRQQxQMuo/bkSi/31/h0BmBBRuX4ekEvr0dgS/n96rJu2UXiX67Ifz0erMesiE4Nl3bE/C0gxlgF/DNkp6I7FA+iLjiDxzK6afgfIfWwtq8saqB+ljjOl+07QPA/NuJCnW8pqLQ1f70gmkdeqmfWYuJvDscj0LQhH/4ANx6Y8MvWFgGfOABJ9kD7QbAxEDhi3fvl9I6n/q8tUxTT9emxIPnsLhaORBjRiYENZYWrhGANX8MfNHMmuUZCOB2cCSlBGPuXfsfdxyDPdj8E7ZwljROn26n/FPW5FtE6rQWv8GPeiEFmMu3NLVaqg0ZI0YlXDu8p/iYOjKSmNEmmlTpHDxPG5lnZQabp1d9JVSACHxaYCEs6seGPMPGwovjy1eqrDN/3GWbrzVcERMrj8nz0VOl8eNiwo8X5+7IUw== java-http-signature test 2 | -------------------------------------------------------------------------------- /common/src/test/resources/testng.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /google-http-client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | java-http-signature 11 | com.joyent.http-signature 12 | 4.1.3-SNAPSHOT 13 | 14 | 15 | 4.0.0 16 | 17 | google-http-client-signature 18 | jar 19 | 20 | 21 | 22 | 4.1.3-SNAPSHOT 23 | 1.32.1 24 | 25 | 26 | 27 | 28 | com.joyent.http-signature 29 | http-signature-common 30 | ${dependency.http-signature-common.version} 31 | 32 | 33 | com.joyent.http-signature 34 | http-signature-common 35 | ${dependency.http-signature-common.version} 36 | test-jar 37 | test 38 | 39 | 40 | com.google.http-client 41 | google-http-client 42 | ${dependency.google-http-client.version} 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.apache.maven.plugins 50 | maven-surefire-plugin 51 | 52 | ${maven.test.skip} 53 | 54 | src/test/resources/testng.xml 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /google-http-client/src/main/java/com/joyent/http/signature/google/httpclient/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | /** 10 | * Package containing classes for supporting HTTP Signing with the 11 | * Google HTTP Client. 12 | * 13 | * @author Elijah Zupancic 14 | */ 15 | package com.joyent.http.signature.google.httpclient; 16 | -------------------------------------------------------------------------------- /google-http-client/src/test/java/com/joyent/http/signature/google/httpclient/RequestHttpSignerTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Joyent, Inc. All rights reserved. 3 | */ 4 | package com.joyent.http.signature.google.httpclient; 5 | 6 | import com.google.api.client.http.GenericUrl; 7 | import com.google.api.client.http.HttpRequest; 8 | import com.google.api.client.http.HttpRequestFactory; 9 | import com.google.api.client.http.HttpTransport; 10 | import com.google.api.client.testing.http.MockHttpTransport; 11 | import com.google.common.primitives.Ints; 12 | import com.joyent.http.signature.Signer; 13 | import com.joyent.http.signature.SignerTestUtil; 14 | import com.joyent.http.signature.ThreadLocalSigner; 15 | import org.testng.Assert; 16 | import org.testng.annotations.*; 17 | import org.testng.log4testng.Logger; 18 | 19 | import java.io.IOException; 20 | import java.net.URI; 21 | import java.security.KeyPair; 22 | import java.security.NoSuchAlgorithmException; 23 | 24 | @SuppressWarnings("deprecation") 25 | public class RequestHttpSignerTest { 26 | private static final Logger LOG = Logger.getLogger(RequestHttpSignerTest.class); 27 | 28 | private KeyPair testKeyPair; 29 | private String testKeyFingerprint; 30 | private boolean useNativeCodeToSign; 31 | private ThreadLocalSigner signer; 32 | 33 | @Parameters({"useNativeCodeToSign"}) 34 | @BeforeClass 35 | public void beforeClass(@Optional Boolean useNativeCodeToSign) throws IOException, NoSuchAlgorithmException { 36 | if (useNativeCodeToSign == null) { 37 | this.useNativeCodeToSign = true; 38 | } else { 39 | this.useNativeCodeToSign = useNativeCodeToSign; 40 | } 41 | 42 | this.signer = new ThreadLocalSigner(this.useNativeCodeToSign); 43 | // Removes any existing instances - so that we can reset state 44 | this.signer.remove(); 45 | this.testKeyPair = SignerTestUtil.testKeyPair("rsa_2048"); 46 | this.testKeyFingerprint = SignerTestUtil.testKeyMd5Fingerprint("rsa_2048"); 47 | } 48 | 49 | @AfterClass 50 | public void cleanUp() { 51 | if (signer != null) { 52 | signer.clearAll(); 53 | } 54 | } 55 | 56 | @Test 57 | public void canSignUri() throws IOException { 58 | final String login = "user"; 59 | final Signer signer = new Signer(this.useNativeCodeToSign); 60 | RequestHttpSigner requestSigner = new RequestHttpSigner(testKeyPair, 61 | login, testKeyFingerprint, useNativeCodeToSign); 62 | URI uri = URI.create("http://localhost/foo/bar"); 63 | 64 | URI signedUri = requestSigner.signURI(uri, "GET", 0L); 65 | 66 | String expected = "http://localhost/foo/bar?algorithm=RSA-SHA256&expires=0&keyId=%2Fuser%2Fkeys%2Fa5%3A75%3Ae2%3A5e%3A54%3A0e%3A99%3A9d%3Af0%3Aa9%3A16%3A8c%3A1d%3A88%3Adc%3Ab4&signature=l7ScY1r7R4E%2BmCgDWBJ5ShoOVqp93h2csUuISZXz63V2xBKLJiQEXUW626ur2X3rRRVDa0KS2eWf%2BwWy9SgqMUjwoCAbXivuvsKEkJVuBz9RrDb%2BC9oZgWRqNfGoBY824FoMgIJFZBF0yIFlIa1Qij%2FNOeOP%2BCzMXFdi2J5RjIQ7PZqKUwe%2BAM3vS2TkBoyRk%2FYw0tCTDglx4oOAS7ulNQUHzyKma0k2z5C6jIfv1ab19tl8lYnmgvk6FFV6iLT3dlqMzFtXdD1DeHKkXR2JUIxm9%2BdD3FULGnMl8sunlN%2FUb4paytxc%2Ff81f2o%2BI0369y3Y8N3E9Ly1Q0ATQq6QJQ%3D%3D"; 67 | 68 | Assert.assertEquals(signedUri.toString(), expected); 69 | } 70 | 71 | @Test 72 | public void canSignRequest() throws IOException { 73 | final String login = "user"; 74 | RequestHttpSigner requestSigner = new RequestHttpSigner(testKeyPair, login, 75 | testKeyFingerprint, useNativeCodeToSign); 76 | 77 | Signer signer = requestSigner.getSignerThreadLocal().get(); 78 | 79 | System.out.printf("Signer implementation: %s", signer); 80 | 81 | HttpTransport transport = new MockHttpTransport(); 82 | HttpRequestFactory factory = transport.createRequestFactory(); 83 | 84 | GenericUrl get = new GenericUrl("http://localhost/foo/bar"); 85 | HttpRequest request = factory.buildGetRequest(get); 86 | 87 | long running = 0L; 88 | int iterations = 5; 89 | 90 | for (int i = 0; i < iterations; i++) { 91 | long start = System.currentTimeMillis(); 92 | requestSigner.signRequest(request); 93 | long end = System.currentTimeMillis(); 94 | 95 | long total = end - start; 96 | running += total; 97 | Assert.assertTrue(requestSigner.verifyRequest(request)); 98 | System.out.println(String.format("Total signing time for request: %dms", total)); 99 | } 100 | 101 | long average = Ints.saturatedCast(running / iterations); 102 | System.out.println(String.format("Average signing time: %dms", average)); 103 | 104 | String authorization = request.getHeaders().getAuthorization(); 105 | 106 | LOG.info("Authorization: " + authorization); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /google-http-client/src/test/resources/testng.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /jaxrs-client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 4.0.0 11 | 12 | 13 | java-http-signature 14 | com.joyent.http-signature 15 | 4.1.3-SNAPSHOT 16 | 17 | 18 | jaxrs-client-signature 19 | jar 20 | 21 | 22 | 23 | 1.6.0.Final 24 | 1.0.2 25 | 4.1.3-SNAPSHOT 26 | 8.0.1 27 | 2.1.1 28 | 2.30.1 29 | 5.201 30 | 1.6.0.Final 31 | 32 | 33 | 34 | 35 | 36 | org.jboss.arquillian 37 | arquillian-bom 38 | ${dependency.arquillian.version} 39 | import 40 | pom 41 | 42 | 43 | 44 | 45 | 46 | 47 | com.joyent.http-signature 48 | http-signature-common 49 | ${dependency.http-signature-common.version} 50 | 51 | 52 | com.joyent.http-signature 53 | http-signature-common 54 | ${dependency.http-signature-common.version} 55 | test-jar 56 | test 57 | 58 | 59 | javax.ws.rs 60 | javax.ws.rs-api 61 | ${dependency.jax-rs-api.version} 62 | 63 | 64 | org.slf4j 65 | slf4j-api 66 | ${dependency.slfj.version} 67 | 68 | 69 | 70 | javax 71 | javaee-api 72 | ${dependency.javaee.version} 73 | provided 74 | 75 | 76 | 77 | ch.qos.logback 78 | logback-classic 79 | ${dependency.logback.version} 80 | test 81 | 82 | 83 | fish.payara.extras 84 | payara-embedded-all 85 | ${dependency.payara-embedded-web.version} 86 | test 87 | 88 | 89 | org.glassfish.jersey.media 90 | jersey-media-json-processing 91 | ${dependency.jersey-client.version} 92 | 93 | 94 | org.jboss.arquillian.container 95 | arquillian-glassfish-embedded-3.1 96 | ${dependency.arquillian-glassfish-embedded-3.1.version} 97 | test 98 | 99 | 100 | org.jboss.arquillian.testng 101 | arquillian-testng-container 102 | ${dependency.arquillian-testng-container} 103 | test 104 | 105 | 106 | 107 | 108 | 109 | 110 | org.apache.maven.plugins 111 | maven-failsafe-plugin 112 | 113 | 114 | integration-tests 115 | 116 | integration-test 117 | verify 118 | 119 | 120 | 121 | 122 | -Djava.awt.headless=true 123 | ${maven.integration.test.skip} 124 | 125 | src/test/resources/testng-it.xml 126 | 127 | 128 | ${project.build.directory}/derby.log 129 | ${project.build.testOutputDirectory}/logging.properties 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /jaxrs-client/src/main/java/com/joyent/http/signature/jaxrs/client/SignedRequestClientRequestFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature.jaxrs.client; 9 | 10 | import com.joyent.http.signature.KeyPairLoader; 11 | import com.joyent.http.signature.Signer; 12 | import com.joyent.http.signature.ThreadLocalSigner; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | import javax.ws.rs.client.ClientRequestContext; 17 | import javax.ws.rs.client.ClientRequestFilter; 18 | import javax.ws.rs.core.MultivaluedMap; 19 | import java.io.IOException; 20 | import java.nio.file.Paths; 21 | import java.security.KeyPair; 22 | import java.time.ZoneOffset; 23 | import java.time.ZonedDateTime; 24 | import java.time.format.DateTimeFormatter; 25 | import java.util.Objects; 26 | 27 | 28 | /** 29 | * A Jersey ClientFilter for signing HTTP requests for Joyent Cloud API and Manta service. 30 | * 31 | * @author Phillip Ross 32 | */ 33 | public class SignedRequestClientRequestFilter implements ClientRequestFilter { 34 | 35 | /** 36 | * The static logger instance. 37 | */ 38 | private static final Logger logger = LoggerFactory.getLogger(SignedRequestClientRequestFilter.class); 39 | 40 | /** 41 | * Name of the authorization HTTP header. 42 | */ 43 | public static final String AUTHORIZATION_HEADER_NAME = "Authorization"; 44 | 45 | /** 46 | * Name of the date HTTP header. 47 | */ 48 | public static final String DATE_HEADER_NAME = "Date"; 49 | 50 | /** 51 | * Login name associated with the Joyent Cloud or Manta service. 52 | */ 53 | private String loginName; 54 | 55 | /** 56 | * Key used to access the Joyent Cloud or Manta service. 57 | */ 58 | private KeyPair keyPair; 59 | 60 | /** 61 | * Cryptographic signer instance. 62 | */ 63 | private ThreadLocal signer; 64 | 65 | 66 | /** 67 | * Creates a new filter instance with the specified credentials for signing requests. 68 | * 69 | * @param loginName Login name associated with the Joyent Cloud or Manta service 70 | * @param keyId RSA key fingerprint of the key used to access the Joyent Cloud or Manta service 71 | * @param keyPath Path on the filesystem to the private RSA key used to access the Joyent Cloud or Manta service 72 | * @throws IOException if an I/O exception occurs loading the key 73 | * 74 | * @deprecated The fingerprint is now calculated from the given key. 75 | */ 76 | @Deprecated 77 | public SignedRequestClientRequestFilter(final String loginName, final String keyId, final String keyPath) 78 | throws IOException { 79 | this(loginName, keyId, KeyPairLoader.getKeyPair(Paths.get(keyPath))); 80 | } 81 | 82 | /** 83 | * Creates a new filter instance with the specified credentials for signing requests. 84 | * 85 | * @param loginName Login name associated with the Joyent Cloud or Manta service 86 | * @param keyPath Path on the filesystem to the private RSA key used to access the Joyent Cloud or Manta service 87 | * @throws IOException if an I/O exception occurs loading the key 88 | */ 89 | public SignedRequestClientRequestFilter(final String loginName, final String keyPath) 90 | throws IOException { 91 | this(loginName, KeyPairLoader.getKeyPair(Paths.get(keyPath))); 92 | } 93 | 94 | 95 | /** 96 | * Creates a new filter instance with the specified credentials for signing requests. 97 | * 98 | * @param loginName Login name associated with the Joyent Cloud or Manta service 99 | * @param keyId RSA key fingerprint of the key used to access the Joyent Cloud or Manta service (ignored) 100 | * @param keyPair Private key used to access the Joyent Cloud or Manta service 101 | * 102 | * @deprecated The fingerprint is now calculated from the given key. 103 | */ 104 | @Deprecated 105 | public SignedRequestClientRequestFilter(final String loginName, final String keyId, final KeyPair keyPair) { 106 | this(loginName, keyPair, new ThreadLocalSigner(new Signer.Builder(keyPair))); 107 | } 108 | 109 | /** 110 | * Creates a new filter instance with the specified credentials for signing requests. 111 | * 112 | * @param loginName Login name associated with the Joyent Cloud or Manta service 113 | * @param keyPair Private key used to access the Joyent Cloud or Manta service 114 | */ 115 | public SignedRequestClientRequestFilter(final String loginName, final KeyPair keyPair) { 116 | this(loginName, keyPair, new ThreadLocalSigner(new Signer.Builder(keyPair))); 117 | } 118 | 119 | 120 | /** 121 | * Creates a new filter instance with the specified credentials for signing requests. 122 | * 123 | * @param loginName Login name associated with the Joyent Cloud or Manta service 124 | * @param keyId RSA key fingerprint of the key used to access the Joyent Cloud or Manta service (ignored) 125 | * @param keyPair Private key used to access the Joyent Cloud or Manta service 126 | * @param signer {@link 127 | * com.joyent.http.signature.ThreadLocalSigner} to use for all 128 | * signed requests. 129 | * 130 | * @deprecated The fingerprint is now calculated from the given key. 131 | */ 132 | @Deprecated 133 | public SignedRequestClientRequestFilter(final String loginName, final String keyId, final KeyPair keyPair, 134 | final ThreadLocalSigner signer) { 135 | this(loginName, keyPair, signer); 136 | } 137 | 138 | /** 139 | * Creates a new filter instance with the specified credentials for signing requests. 140 | * 141 | * @param loginName Login name associated with the Joyent Cloud or Manta service 142 | * @param keyPair Private key used to access the Joyent Cloud or Manta service 143 | * @param signer {@link 144 | * com.joyent.http.signature.ThreadLocalSigner} to use for all 145 | * signed requests. 146 | */ 147 | public SignedRequestClientRequestFilter(final String loginName, final KeyPair keyPair, 148 | final ThreadLocalSigner signer) { 149 | Objects.requireNonNull(loginName, "loginName must be specified"); 150 | Objects.requireNonNull(keyPair, "keyPair must be specified"); 151 | this.loginName = loginName; 152 | this.keyPair = keyPair; 153 | this.signer = signer; 154 | } 155 | 156 | /** 157 | * Adds date and authorization headers to the request. 158 | * 159 | * @param requestContext request context. 160 | * @throws IOException if an I/O exception occurs. 161 | */ 162 | @Override 163 | public void filter(final ClientRequestContext requestContext) throws IOException { 164 | final MultivaluedMap headers = requestContext.getHeaders(); 165 | final ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC); 166 | final String dateHeaderValue = DateTimeFormatter.RFC_1123_DATE_TIME.format(now); 167 | headers.add(DATE_HEADER_NAME, dateHeaderValue); 168 | final String authHeaderValue = signer.get().createAuthorizationHeader( 169 | loginName, 170 | keyPair, 171 | now 172 | ); 173 | headers.add(AUTHORIZATION_HEADER_NAME, authHeaderValue); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /jaxrs-client/src/main/java/com/joyent/http/signature/jaxrs/client/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | /** 10 | * Package containing classes for supporting HTTP Signing with a 11 | * JAX-RS Client. 12 | * 13 | * @author Phillip Ross 14 | */ 15 | package com.joyent.http.signature.jaxrs.client; 16 | -------------------------------------------------------------------------------- /jaxrs-client/src/test/java/com/joyent/http/signature/jaxrs/client/SignedRequestClientRequestFilterIT.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Joyent, Inc. All rights reserved. 3 | */ 4 | package com.joyent.http.signature.jaxrs.client; 5 | 6 | import com.joyent.http.signature.SignerTestUtil; 7 | import com.joyent.http.signature.jaxrs.client.testapp.TestApplication; 8 | import com.joyent.http.signature.jaxrs.client.testapp.TestResource; 9 | import org.jboss.arquillian.container.test.api.Deployment; 10 | import org.jboss.arquillian.test.api.ArquillianResource; 11 | import org.jboss.arquillian.testng.Arquillian; 12 | import org.jboss.shrinkwrap.api.ShrinkWrap; 13 | import org.jboss.shrinkwrap.api.spec.WebArchive; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | import org.testng.Assert; 17 | import org.testng.annotations.Test; 18 | 19 | import javax.json.JsonObject; 20 | import javax.json.JsonValue; 21 | import javax.ws.rs.client.ClientBuilder; 22 | import javax.ws.rs.client.Invocation; 23 | import javax.ws.rs.core.MediaType; 24 | import javax.ws.rs.core.Response; 25 | import java.io.IOException; 26 | import java.net.URISyntaxException; 27 | import java.net.URL; 28 | 29 | 30 | /** 31 | * Tests the functionality of the SignedRequestClientRequestFilter with a Jersey client. 32 | * 33 | * @author Phillip Ross 34 | */ 35 | public class SignedRequestClientRequestFilterIT extends Arquillian { 36 | 37 | private static final Logger logger = LoggerFactory.getLogger(SignedRequestClientRequestFilterIT.class); 38 | 39 | private static final String TEST_LOGIN_NAME = "testy"; 40 | 41 | private static final String TEST_KEY_FINGERPRINT = SignerTestUtil.testKeyMd5Fingerprint("rsa_2048"); 42 | 43 | /** 44 | * URL path element corresponding to the test JAX-RS application. This value must match 45 | * the application path defined by the application. 46 | */ 47 | private static final String TEST_JAXRS_APPLICATION_ENDPOINT = "api-endpoint"; 48 | 49 | /** 50 | * URL path element corresponding to the test JAX-RS resource. This value must match 51 | * the resource path defined in the test resource. 52 | */ 53 | private static final String TEST_RESOURCE_PATH = "testResource"; 54 | 55 | /** 56 | * URL path element corresponding to the test method within the JAX-RS resource. 57 | * This value must match the method path defined in the method of the test resource. 58 | */ 59 | private static final String TEST_RESOURCE_METHOD_PATH = "returnHeaders"; 60 | 61 | /** 62 | * Arquillian will populate this with the corresponding base URL for the application as is 63 | * deployed within the container. The test methods can then use this base URL to build 64 | * HTTP requests from. 65 | */ 66 | @ArquillianResource 67 | URL endpointBaseUrl; 68 | 69 | 70 | /** 71 | * Create the deployment. 72 | * 73 | * This will archive the JAX-RS application and resource used for testing into a war file 74 | * which will then be deployed into the embedded container by arquillian. The testable 75 | * attribute for the annotation denotes that this test class will run as a client OUTSIDE 76 | * of the deployed application. 77 | * 78 | * @return {@link org.jboss.shrinkwrap.api.spec.WebArchive} containing the test application 79 | */ 80 | @Deployment(testable = false) 81 | public static WebArchive createDeployment() { 82 | return ShrinkWrap.create(WebArchive.class) 83 | .addClasses( 84 | TestApplication.class, 85 | TestResource.class 86 | ); 87 | } 88 | 89 | 90 | /** 91 | * Test and verify that the Authorization and Date headers are set by the filter. 92 | * 93 | * This simply tests that the headers are set by the filter, received by a JAX-RS resource, wrapped in a 94 | * specially formatted JSON object, and returned to the client in a GET request. The signature is 95 | * not actually validated. Only that the headers are received and that the authorization header contains 96 | * the correct components (keyId, algorithm, and signature) 97 | * 98 | * @throws URISyntaxException if the endpoint URI is malformed. 99 | * @throws IOException if unable to read test key 100 | */ 101 | @Test 102 | @SuppressWarnings("deprecation") 103 | public void testSignedRequestWithFilter() throws URISyntaxException, IOException { 104 | Assert.assertNotNull(endpointBaseUrl); 105 | 106 | final SignedRequestClientRequestFilter signedRequestClientRequestFilter = new SignedRequestClientRequestFilter( 107 | TEST_LOGIN_NAME, 108 | TEST_KEY_FINGERPRINT, 109 | SignerTestUtil.testKeyPair("rsa_2048") 110 | ); 111 | 112 | Invocation.Builder builder = ClientBuilder.newClient() 113 | .register(signedRequestClientRequestFilter) 114 | .target(endpointBaseUrl.toURI()) 115 | .path(TEST_JAXRS_APPLICATION_ENDPOINT) 116 | .path(TEST_RESOURCE_PATH) 117 | .path(TEST_RESOURCE_METHOD_PATH) 118 | .request(MediaType.APPLICATION_JSON_TYPE); 119 | 120 | final Response response; 121 | 122 | try { 123 | response = builder.get(); 124 | } catch (RuntimeException e) { 125 | logger.error("Error accessing endpoint: {}", endpointBaseUrl, e); 126 | throw e; 127 | } 128 | 129 | Assert.assertNotNull(response); 130 | Assert.assertEquals(response.getStatus(), 200); 131 | logger.debug("response status code: {}", response.getStatus()); 132 | Assert.assertNotNull(response.getMediaType()); 133 | Assert.assertEquals(response.getMediaType(), MediaType.APPLICATION_JSON_TYPE); 134 | logger.debug("response media type: {}", response.getMediaType()); 135 | 136 | JsonObject jsonObject = response.readEntity(JsonObject.class); 137 | Assert.assertNotNull(jsonObject); 138 | logger.debug("response entity json object content: {}", jsonObject); 139 | 140 | Assert.assertNotNull(jsonObject.get(SignedRequestClientRequestFilter.DATE_HEADER_NAME.toLowerCase())); 141 | Assert.assertEquals( 142 | jsonObject.get(SignedRequestClientRequestFilter.DATE_HEADER_NAME.toLowerCase()) 143 | .getValueType(), 144 | JsonValue.ValueType.ARRAY 145 | ); 146 | Assert.assertNotNull(jsonObject.get(SignedRequestClientRequestFilter.AUTHORIZATION_HEADER_NAME.toLowerCase())); 147 | Assert.assertEquals( 148 | jsonObject.get(SignedRequestClientRequestFilter.AUTHORIZATION_HEADER_NAME.toLowerCase()) 149 | .getValueType(), 150 | JsonValue.ValueType.ARRAY 151 | ); 152 | 153 | String authorizationString = jsonObject.getJsonArray( 154 | SignedRequestClientRequestFilter.AUTHORIZATION_HEADER_NAME.toLowerCase() 155 | ).getString(0); 156 | 157 | Assert.assertTrue( 158 | authorizationString.startsWith("Signature keyId=") 159 | && authorizationString.contains("algorithm=\"rsa-sha256\"") 160 | && authorizationString.contains("signature=") 161 | ); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /jaxrs-client/src/test/java/com/joyent/http/signature/jaxrs/client/testapp/TestApplication.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Joyent, Inc. All rights reserved. 3 | */ 4 | package com.joyent.http.signature.jaxrs.client.testapp; 5 | 6 | import javax.ws.rs.ApplicationPath; 7 | import javax.ws.rs.core.Application; 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | 11 | 12 | /** 13 | * Test application enumerating the resource classes to be included. 14 | * 15 | * @author Phillip Ross 16 | */ 17 | @ApplicationPath("api-endpoint") 18 | public class TestApplication extends Application { 19 | 20 | 21 | @Override 22 | public Set> getClasses() { 23 | Set> classes = new HashSet<>(); 24 | classes.add(TestResource.class); 25 | return classes; 26 | } 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /jaxrs-client/src/test/java/com/joyent/http/signature/jaxrs/client/testapp/TestResource.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Joyent, Inc. All rights reserved. 3 | */ 4 | package com.joyent.http.signature.jaxrs.client.testapp; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import javax.enterprise.context.RequestScoped; 10 | import javax.inject.Named; 11 | import javax.json.Json; 12 | import javax.json.JsonArrayBuilder; 13 | import javax.json.JsonObject; 14 | import javax.json.JsonObjectBuilder; 15 | import javax.ws.rs.GET; 16 | import javax.ws.rs.Path; 17 | import javax.ws.rs.Produces; 18 | import javax.ws.rs.core.Context; 19 | import javax.ws.rs.core.HttpHeaders; 20 | import javax.ws.rs.core.Response; 21 | 22 | 23 | /** 24 | * JAX-RS resource class for testing REST requests. 25 | * 26 | * @author Phillip Ross 27 | */ 28 | @Path("/testResource") 29 | @Named 30 | @RequestScoped 31 | public class TestResource { 32 | 33 | private static final Logger logger = LoggerFactory.getLogger(TestResource.class); 34 | 35 | 36 | @GET 37 | @Path("/returnHeaders") 38 | @Produces("application/json") 39 | public Response test(@Context HttpHeaders headers) { 40 | JsonObjectBuilder jsonObjectBuilder = Json.createObjectBuilder(); 41 | logger.debug("invoked getIt() method."); 42 | for (String key : headers.getRequestHeaders().keySet()) { 43 | logger.debug("key: {}", key); 44 | JsonArrayBuilder jsonArrayBuilder = Json.createArrayBuilder(); 45 | for (String value : headers.getRequestHeader(key)) { 46 | logger.debug(" value: {}", value); 47 | jsonArrayBuilder.add(value); 48 | } 49 | jsonObjectBuilder.add(key, jsonArrayBuilder); 50 | } 51 | JsonObject jsonObject = jsonObjectBuilder.build(); 52 | return Response.ok(jsonObject).build(); 53 | } 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /jaxrs-client/src/test/resources/arquillian.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 48765 11 | 48766 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /jaxrs-client/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | [%thread] %-5level %logger [%X{mantaRequestId}] - %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /jaxrs-client/src/test/resources/testng-it.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /license-history.txt: -------------------------------------------------------------------------------- 1 | Until version 2.2.0, Java HTTP Signature has been licensed under the MIT license. 2 | As of 2.2.0, it is licensed under the MPL v2 license. 3 | 4 | For public record, non-Joyent contributers have agreed to change the license to the 5 | MPL v2. 6 | 7 | Delivered-To: elijah.zupancic@joyent.com 8 | Received: by 10.176.64.7 with SMTP id h7csp2185498uad; 9 | Tue, 19 Apr 2016 16:45:13 -0700 (PDT) 10 | X-Received: by 10.129.77.65 with SMTP id a62mr3875834ywb.87.1461109513180; 11 | Tue, 19 Apr 2016 16:45:13 -0700 (PDT) 12 | Return-Path: 13 | Received: from mail-yw0-x230.google.com (mail-yw0-x230.google.com. [2607:f8b0:4002:c05::230]) 14 | by mx.google.com with ESMTPS id r85si15968712ywg.169.2016.04.19.16.45.13 15 | for 16 | (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); 17 | Tue, 19 Apr 2016 16:45:13 -0700 (PDT) 18 | Received-SPF: pass (google.com: domain of phillip.w.g.ross at gmail.com designates 2607:f8b0:4002:c05::230 as permitted sender) client-ip=2607:f8b0:4002:c05::230; 19 | Authentication-Results: mx.google.com; 20 | dkim=pass header.i=@gmail.com; 21 | spf=pass (google.com: domain of phillip.w.g.ross at gmail.com designates 2607:f8b0:4002:c05::230 as permitted sender) smtp.mailfrom=phillip.w.g.ross at gmail.com; 22 | dmarc=pass (p=NONE dis=NONE) header.from=gmail.com 23 | Received: by mail-yw0-x230.google.com with SMTP id t10so36163738ywa.0 24 | for ; Tue, 19 Apr 2016 16:45:13 -0700 (PDT) 25 | DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; 26 | d=gmail.com; s=20120113; 27 | h=mime-version:in-reply-to:references:date:message-id:subject:from:to; 28 | bh=mCItCpF6iryE+gFaPVfK9kwGvBYeW+ltbOswLqvWGZQ=; 29 | b=bldaKXPMseZjfqqeIf5EPfIWLAESTaTrTfD1H7OgDKBdlfcj/12UvsgTP1hZrTMtRt 30 | m/aH8CXDW0gblYkMYzJFkFKcU08fBu/rTBhS9ZTQ5F06uPm9Rom9pAIrj/dMoBnh6LeG 31 | Zd/mfmZ0wpRREhAnrEeqcp1WMHMM8dQRsA5l3qfSVUtdJ3j1TWZy/r3KoMulYy8tZnl+ 32 | 5sNyjO1WNK1VywdpO6q+FkLFhmVqEWAs+yJRfsXDtDKimSLI3ZWJJjBwAdaxLWVwXDSn 33 | EoK7P20jkxumVqlqLtoOGbWDsf/z3idEaR+SFaCO4vWK/iqjIGyipVc6ezOA9g+/YiEo 34 | aY6w== 35 | X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; 36 | d=1e100.net; s=20130820; 37 | h=x-gm-message-state:mime-version:in-reply-to:references:date 38 | :message-id:subject:from:to; 39 | bh=mCItCpF6iryE+gFaPVfK9kwGvBYeW+ltbOswLqvWGZQ=; 40 | b=fapasJ3riC0ZhpOnuSVl/A4ldmOEmedK6PddygHy4/6+ZRigC7k5sqNei71fvzdFew 41 | 03s//FjOQHJJwaEFWKl2KYnafO5KJPU+njgIpaDU//XFgfWaMbwieh8ujw4n4/kBEQA0 42 | p/4tXtXk/JByERJJDEOzLx17eONWAp8dz4ht97+gyPILi2KT77BUWHOZemD02xlaJm1k 43 | nRL5soP6cwHxbRENWpON1QinVAiXDw7wxhEAdVypWOOuTYIRs/Y2EJ8rfllBIkOdHAHu 44 | 3WekcWbb7HLo88K3M0XNu+gksyI+Eug9h54tbTF/AHqobn/iG67Q0hh0WJ+D7vf9LCPb 45 | rZzQ== 46 | X-Gm-Message-State: AOPr4FVoDE+rGneBUW6mJWLqIn1hmPt6b48AHl8hZcslT1cdBGoc+Wvv2VKoKgVm/TeoEbtxrKiFmKjxyqhogw== 47 | MIME-Version: 1.0 48 | X-Received: by 10.129.154.82 with SMTP id r79mr2619721ywg.85.1461109512941; 49 | Tue, 19 Apr 2016 16:45:12 -0700 (PDT) 50 | Received: by 10.37.34.194 with HTTP; Tue, 19 Apr 2016 16:45:12 -0700 (PDT) 51 | In-Reply-To: 52 | References: 53 | Date: Tue, 19 Apr 2016 19:45:12 -0400 54 | Message-ID: 55 | Subject: Re: Move to the MPLv2 56 | From: Phillip Ross 57 | To: Elijah Zupancic 58 | Content-Type: multipart/alternative; boundary=94eb2c0b8d147f38e70530df1167 59 | 60 | --94eb2c0b8d147f38e70530df1167 61 | Content-Type: text/plain; charset=UTF-8 62 | 63 | Yep, not a problem in either case. I hope everything is going well! 64 | 65 | Please see the confirmations inline: 66 | 67 | On Tue, Apr 19, 2016 at 7:38 PM, Elijah Zupancic wrote: 69 | 70 | > Do you agree to change the licensing from the MIT license for the Java 71 | > Manta Project (https://github.com/joyent/java-manta) to the MPL v2? 72 | > 73 | 74 | Yes. 75 | 76 | 77 | Do you agree to change the licensing from the MIT license for the Java HTTP 78 | > Signature Project (https://github.com/joyent/java-http-signature) to the 79 | > MPL v2? 80 | > 81 | 82 | Yes 83 | 84 | --94eb2c0b8d147f38e70530df1167 85 | Content-Type: text/html; charset=UTF-8 86 | Content-Transfer-Encoding: quoted-printable 87 | 88 |
Yep, not a problem in either case.=C2=A0 I hope every= 89 | thing is going well!

Please see the confirmations = 90 | inline:

On Tue, Apr 19, 2016 at 7:38 PM, Elijah Zu= 91 | pancic <elijah.zupancic@joyent.com> wrote:
107 | 108 | --94eb2c0b8d147f38e70530df1167-- -------------------------------------------------------------------------------- /microbench/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 4.0.0 11 | 12 | 13 | java-http-signature 14 | com.joyent.http-signature 15 | 4.1.3-SNAPSHOT 16 | 17 | 18 | microbench 19 | jar 20 | 21 | 22 | 23 | 4.1.3-SNAPSHOT 24 | 1.21 25 | benchmarks 26 | 27 | 28 | 29 | 30 | com.joyent.http-signature 31 | http-signature-common 32 | ${dependency.http-signature-common.version} 33 | 34 | 35 | com.joyent.http-signature 36 | http-signature-common 37 | ${dependency.http-signature-common.version} 38 | test-jar 39 | 41 | compile 42 | 43 | 44 | 45 | org.openjdk.jmh 46 | jmh-core 47 | ${dependency.jmh.version} 48 | 49 | 50 | org.openjdk.jmh 51 | jmh-generator-annprocess 52 | ${dependency.jmh.version} 53 | provided 54 | 55 | 56 | 57 | 58 | 59 | 61 | 62 | maven-compiler-plugin 63 | 64 | 65 | default-compile 66 | compile 67 | 68 | compile 69 | 70 | 71 | 72 | javac-with-errorprone 73 | 74 | -Xlint:all 75 | -Xlint:-processing 76 | -XepDisableAllChecks 77 | 78 | 79 | 80 | 81 | 82 | 83 | org.apache.maven.plugins 84 | maven-shade-plugin 85 | 2.2 86 | 87 | 88 | package 89 | 90 | shade 91 | 92 | 93 | ${uberjar.name} 94 | 95 | 96 | org.openjdk.jmh.Main 97 | 98 | 99 | 100 | 101 | 105 | *:* 106 | 107 | META-INF/*.SF 108 | META-INF/*.DSA 109 | META-INF/*.RSA 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /microbench/src/main/java/com/joyent/http/signature/BenchmarkDSASigner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature; 9 | 10 | import org.openjdk.jmh.annotations.Param; 11 | 12 | import static com.joyent.http.signature.KeyPairLoader.PROVIDER_BOUNCY_CASTLE; 13 | import static com.joyent.http.signature.KeyPairLoader.PROVIDER_PKCS11_NSS; 14 | 15 | 16 | @SuppressWarnings({"checkstyle:javadocmethod", "checkstyle:javadoctype", "checkstyle:javadocvariable"}) 17 | public class BenchmarkDSASigner extends BenchmarkSigner { 18 | @Param({"SHA1", "SHA256"}) 19 | private String hash; 20 | 21 | @Param({"stdlib"}) 22 | private String providerCode; 23 | 24 | @Param({PROVIDER_PKCS11_NSS, PROVIDER_BOUNCY_CASTLE}) 25 | private String keyProviderCode; 26 | 27 | @Override 28 | public String getKeyCode() { 29 | return "dsa_1024"; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /microbench/src/main/java/com/joyent/http/signature/BenchmarkECDSASigner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature; 9 | 10 | import org.openjdk.jmh.annotations.Param; 11 | 12 | import static com.joyent.http.signature.KeyPairLoader.PROVIDER_BOUNCY_CASTLE; 13 | import static com.joyent.http.signature.KeyPairLoader.PROVIDER_PKCS11_NSS; 14 | 15 | 16 | @SuppressWarnings({"checkstyle:javadocmethod", "checkstyle:javadoctype", "checkstyle:javadocvariable"}) 17 | public class BenchmarkECDSASigner extends BenchmarkSigner { 18 | @Param({"SHA256", "SHA384", "SHA512"}) 19 | private String hash; 20 | 21 | @Param({"stdlib"}) 22 | private String providerCode; 23 | 24 | @Param({PROVIDER_PKCS11_NSS, PROVIDER_BOUNCY_CASTLE}) 25 | private String keyProviderCode; 26 | 27 | @Override 28 | public String getKeyCode() { 29 | return "ecdsa_256"; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /microbench/src/main/java/com/joyent/http/signature/BenchmarkKeyFingerprinter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature; 9 | 10 | import org.openjdk.jmh.annotations.Benchmark; 11 | import org.openjdk.jmh.annotations.BenchmarkMode; 12 | import org.openjdk.jmh.annotations.Mode; 13 | import org.openjdk.jmh.annotations.OutputTimeUnit; 14 | import org.openjdk.jmh.annotations.Param; 15 | import org.openjdk.jmh.annotations.Scope; 16 | import org.openjdk.jmh.annotations.Setup; 17 | import org.openjdk.jmh.annotations.State; 18 | 19 | import java.io.IOException; 20 | import java.security.KeyPair; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | /* See http://openjdk.java.net/projects/code-tools/jmh/ for general 24 | * information on JMH and running benchmarks. In general one would 25 | * "package" this module, and run `java -jar target/benchmark.jar`. */ 26 | @State(Scope.Thread) 27 | @SuppressWarnings({"checkstyle:javadocmethod", "checkstyle:javadoctype", "checkstyle:javadocvariable", 28 | "checkstyle:visibilitymodifier"}) 29 | public class BenchmarkKeyFingerprinter { 30 | protected KeyPair keyPair; 31 | 32 | @Param({"dsa_1024", "rsa_2048", "ecdsa_256"}) 33 | private String keyCode; 34 | 35 | @Setup 36 | public void setup() throws IOException { 37 | keyPair = SignerTestUtil.testKeyPair(keyCode); 38 | } 39 | 40 | @Benchmark 41 | @BenchmarkMode(Mode.Throughput) 42 | @OutputTimeUnit(TimeUnit.SECONDS) 43 | public String fingerprintThroughput() { 44 | String fingerprint = KeyFingerprinter.md5Fingerprint(keyPair); 45 | return fingerprint; 46 | } 47 | 48 | @Benchmark 49 | @BenchmarkMode(Mode.SampleTime) 50 | @OutputTimeUnit(TimeUnit.MICROSECONDS) 51 | public String fingerprintLatency() { 52 | String fingerprint = KeyFingerprinter.md5Fingerprint(keyPair); 53 | return fingerprint; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /microbench/src/main/java/com/joyent/http/signature/BenchmarkRSASigner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature; 9 | 10 | import org.openjdk.jmh.annotations.Param; 11 | 12 | import static com.joyent.http.signature.KeyPairLoader.PROVIDER_BOUNCY_CASTLE; 13 | import static com.joyent.http.signature.KeyPairLoader.PROVIDER_PKCS11_NSS; 14 | 15 | 16 | @SuppressWarnings({"checkstyle:javadocmethod", "checkstyle:javadoctype", "checkstyle:javadocvariable"}) 17 | public class BenchmarkRSASigner extends BenchmarkSigner { 18 | @Param({"SHA1", "SHA256", "SHA512"}) 19 | private String hash; 20 | 21 | @Param({"stdlib", "native.jnagmp"}) 22 | private String providerCode; 23 | 24 | @Param({PROVIDER_PKCS11_NSS, PROVIDER_BOUNCY_CASTLE}) 25 | private String keyProviderCode; 26 | 27 | @Override 28 | public String getKeyCode() { 29 | return "rsa_2048"; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /microbench/src/main/java/com/joyent/http/signature/BenchmarkSigner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature; 9 | 10 | import org.openjdk.jmh.annotations.Benchmark; 11 | import org.openjdk.jmh.annotations.BenchmarkMode; 12 | import org.openjdk.jmh.annotations.Mode; 13 | import org.openjdk.jmh.annotations.OutputTimeUnit; 14 | import org.openjdk.jmh.annotations.Param; 15 | import org.openjdk.jmh.annotations.Scope; 16 | import org.openjdk.jmh.annotations.Setup; 17 | import org.openjdk.jmh.annotations.State; 18 | 19 | import java.io.IOException; 20 | import java.security.KeyPair; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | /* See http://openjdk.java.net/projects/code-tools/jmh/ for general 24 | * information on JMH and running benchmarks. In general one would 25 | * "package" this module, and run `java -jar target/benchmark.jar`. */ 26 | @State(Scope.Thread) 27 | @SuppressWarnings({"checkstyle:javadocmethod", "checkstyle:javadoctype", "checkstyle:javadocvariable", 28 | "checkstyle:visibilitymodifier"}) 29 | public abstract class BenchmarkSigner { 30 | protected KeyPair keyPair; 31 | protected String testKeyFingerprint; 32 | protected Signer signer; 33 | protected String verifyNow; 34 | protected String verifyHeader; 35 | private boolean firstSetup = true; 36 | 37 | @Param({"SHA1", "SHA256", "SHA512"}) 38 | private String hash; 39 | 40 | 41 | @Param({"stdlib", "native.jnagmp"}) 42 | private String providerCode; 43 | 44 | public abstract String getKeyCode(); 45 | 46 | @Setup 47 | public void setup() throws IOException { 48 | testKeyFingerprint = SignerTestUtil.testKeyMd5Fingerprint(getKeyCode()); 49 | keyPair = SignerTestUtil.testKeyPair(getKeyCode()); 50 | signer = new Signer.Builder(keyPair).hash(hash).providerCode(providerCode).build(); 51 | 52 | verifyNow = signer.defaultSignDateAsString(); 53 | verifyHeader = signHeader(verifyNow); 54 | if (firstSetup) { 55 | System.out.println("\n#Signature-->Provider: " + signer.getSignature().getProvider().getName()); 56 | firstSetup = false; 57 | } 58 | } 59 | 60 | @Benchmark 61 | @BenchmarkMode(Mode.Throughput) 62 | @OutputTimeUnit(TimeUnit.SECONDS) 63 | public String signHeaderThroughput() { 64 | String now = signer.defaultSignDateAsString(); 65 | return signHeader(now); 66 | } 67 | 68 | @Benchmark 69 | @BenchmarkMode(Mode.SampleTime) 70 | @OutputTimeUnit(TimeUnit.MICROSECONDS) 71 | public String signHeaderLatency() { 72 | String now = signer.defaultSignDateAsString(); 73 | return signHeader(now); 74 | } 75 | 76 | @Benchmark 77 | @BenchmarkMode(Mode.Throughput) 78 | @OutputTimeUnit(TimeUnit.SECONDS) 79 | public boolean verifyHeaderThroughput() { 80 | return verifyHeader(verifyNow, verifyHeader); 81 | } 82 | 83 | @Benchmark 84 | @BenchmarkMode(Mode.SampleTime) 85 | @OutputTimeUnit(TimeUnit.MICROSECONDS) 86 | public boolean verifyHeaderLatency() { 87 | return verifyHeader(verifyNow, verifyHeader); 88 | } 89 | 90 | protected String signHeader(final String now) { 91 | return signer.createAuthorizationHeader("bench", keyPair, now); 92 | } 93 | 94 | protected boolean verifyHeader(final String ts, final String header) { 95 | return signer.verifyAuthorizationHeader(keyPair, header, ts); 96 | 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /microbench/src/main/java/com/joyent/http/signature/DefaultSignDateAsStringBenchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | package com.joyent.http.signature; 9 | 10 | import org.openjdk.jmh.annotations.Benchmark; 11 | import org.openjdk.jmh.annotations.Scope; 12 | import org.openjdk.jmh.annotations.Setup; 13 | import org.openjdk.jmh.annotations.State; 14 | import org.openjdk.jmh.annotations.Threads; 15 | 16 | import java.io.IOException; 17 | import java.security.KeyPair; 18 | 19 | 20 | @State(Scope.Benchmark) 21 | @SuppressWarnings({"checkstyle:javadocmethod", "checkstyle:javadoctype", "checkstyle:javadocvariable", 22 | "checkstyle:magicnumber"}) 23 | public class DefaultSignDateAsStringBenchmark { 24 | 25 | private KeyPair keyPair; 26 | private String testKeyFingerprint; 27 | private Signer signer; 28 | private boolean firstSetup = true; 29 | 30 | @Setup 31 | public void setup() throws IOException { 32 | testKeyFingerprint = SignerTestUtil.testKeyMd5Fingerprint("rsa_1024"); 33 | keyPair = SignerTestUtil.testKeyPair("rsa_1024"); 34 | signer = new Signer.Builder(keyPair).hash("SHA256").providerCode("stdlib").build(); 35 | 36 | if (firstSetup) { 37 | System.out.println("\n#Signature-->Provider: " + signer.getSignature().getProvider().getName()); 38 | firstSetup = false; 39 | } 40 | } 41 | 42 | @Benchmark 43 | @Threads(1) 44 | public String thread1() { 45 | return signer.defaultSignDateAsString(); 46 | } 47 | 48 | @Benchmark 49 | @Threads(4) 50 | public String thread4() { 51 | return signer.defaultSignDateAsString(); 52 | } 53 | 54 | @Threads(8) 55 | public String thread8() { 56 | return signer.defaultSignDateAsString(); 57 | } 58 | 59 | @Benchmark 60 | @Threads(64) 61 | public String thread64() { 62 | return signer.defaultSignDateAsString(); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /microbench/src/main/java/com/joyent/http/signature/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Joyent, Inc. All rights reserved. 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | /** 10 | * Microbenchmarks for http signatures. 11 | */ 12 | package com.joyent.http.signature; 13 | -------------------------------------------------------------------------------- /suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | --------------------------------------------------------------------------------