Skip to content

Commit ee64687

Browse files
committed
Initial commit
0 parents  commit ee64687

File tree

10 files changed

+1201
-0
lines changed

10 files changed

+1201
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.classpath
2+
.project
3+
.settings/
4+
target/

pom.xml

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<groupId>io.bspk</groupId>
6+
<artifactId>httpsig</artifactId>
7+
<version>0.0.1-SNAPSHOT</version>
8+
<name>HTTP Messsage Signatures</name>
9+
<properties>
10+
<java.version>14</java.version>
11+
<maven.compiler.source>14</maven.compiler.source>
12+
<maven.compiler.target>14</maven.compiler.target>
13+
</properties>
14+
<dependencies>
15+
<dependency>
16+
<groupId>org.greenbytes.http</groupId>
17+
<artifactId>structured-fields</artifactId>
18+
<version>0.4</version>
19+
</dependency>
20+
<dependency>
21+
<groupId>org.bouncycastle</groupId>
22+
<artifactId>bcpkix-jdk18on</artifactId>
23+
<version>1.71</version>
24+
</dependency>
25+
<dependency>
26+
<groupId>com.fasterxml.jackson.core</groupId>
27+
<artifactId>jackson-annotations</artifactId>
28+
<version>2.11.4</version>
29+
</dependency>
30+
<dependency>
31+
<groupId>com.nimbusds</groupId>
32+
<artifactId>nimbus-jose-jwt</artifactId>
33+
<version>9.4.1</version>
34+
<scope>compile</scope>
35+
</dependency>
36+
<dependency>
37+
<groupId>org.slf4j</groupId>
38+
<artifactId>slf4j-api</artifactId>
39+
<version>1.7.30</version>
40+
</dependency>
41+
<dependency>
42+
<groupId>com.google.guava</groupId>
43+
<artifactId>guava</artifactId>
44+
<version>28.1-jre</version>
45+
</dependency>
46+
<dependency>
47+
<groupId>org.springframework</groupId>
48+
<artifactId>spring-web</artifactId>
49+
<version>5.2.22.RELEASE</version>
50+
<scope>provided</scope>
51+
</dependency>
52+
<dependency>
53+
<groupId>javax.servlet</groupId>
54+
<artifactId>servlet-api</artifactId>
55+
<version>2.5</version>
56+
<scope>provided</scope>
57+
</dependency>
58+
</dependencies>
59+
<distributionManagement>
60+
<snapshotRepository>
61+
<id>ossrh</id>
62+
<url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
63+
</snapshotRepository>
64+
<repository>
65+
<id>ossrh</id>
66+
<url>https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</url>
67+
</repository>
68+
</distributionManagement>
69+
<build>
70+
<plugins>
71+
<plugin>
72+
<groupId>org.sonatype.plugins</groupId>
73+
<artifactId>nexus-staging-maven-plugin</artifactId>
74+
<version>1.6.7</version>
75+
<extensions>true</extensions>
76+
<configuration>
77+
<serverId>ossrh</serverId>
78+
<nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>
79+
<autoReleaseAfterClose>true</autoReleaseAfterClose>
80+
</configuration>
81+
</plugin>
82+
<plugin>
83+
<groupId>org.apache.maven.plugins</groupId>
84+
<artifactId>maven-source-plugin</artifactId>
85+
<version>2.2.1</version>
86+
<executions>
87+
<execution>
88+
<id>attach-sources</id>
89+
<goals>
90+
<goal>jar-no-fork</goal>
91+
</goals>
92+
</execution>
93+
</executions>
94+
</plugin>
95+
<plugin>
96+
<groupId>org.apache.maven.plugins</groupId>
97+
<artifactId>maven-javadoc-plugin</artifactId>
98+
<version>3.0.1</version>
99+
<configuration>
100+
<doclint>all,-missing</doclint>
101+
</configuration>
102+
<executions>
103+
<execution>
104+
<id>attach-javadocs</id>
105+
<goals>
106+
<goal>jar</goal>
107+
</goals>
108+
</execution>
109+
</executions>
110+
</plugin>
111+
<plugin>
112+
<groupId>org.apache.maven.plugins</groupId>
113+
<artifactId>maven-gpg-plugin</artifactId>
114+
<version>1.5</version>
115+
<executions>
116+
<execution>
117+
<id>sign-artifacts</id>
118+
<phase>verify</phase>
119+
<goals>
120+
<goal>sign</goal>
121+
</goals>
122+
</execution>
123+
</executions>
124+
</plugin>
125+
</plugins>
126+
</build>
127+
</project>
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package io.bspk.httpsig;
2+
3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import com.fasterxml.jackson.annotation.JsonValue;
5+
6+
/**
7+
* Signature algorithms.
8+
* @author jricher
9+
*
10+
*/
11+
public class HttpSigAlgorithm {
12+
13+
public static final HttpSigAlgorithm RSAPSS = new HttpSigAlgorithm("rsa-pss-sha512");
14+
public static final HttpSigAlgorithm RSA15 = new HttpSigAlgorithm("rsa-v1_5-sha256");
15+
public static final HttpSigAlgorithm HMAC = new HttpSigAlgorithm("hmac-sha256");
16+
public static final HttpSigAlgorithm ECDSA = new HttpSigAlgorithm("ecdsa-p256-sha256");
17+
public static final HttpSigAlgorithm ED25519 = new HttpSigAlgorithm("ed25519");
18+
public static final HttpSigAlgorithm JOSE = new HttpSigAlgorithm(null);
19+
20+
@JsonValue
21+
private final String explicitAlg;
22+
23+
/**
24+
* @param object
25+
*/
26+
private HttpSigAlgorithm(String explicitAlg) {
27+
this.explicitAlg = explicitAlg;
28+
}
29+
30+
/**
31+
* @return the explicitAlg
32+
*/
33+
public String getExplicitAlg() {
34+
return explicitAlg;
35+
}
36+
37+
/**
38+
* @return
39+
*/
40+
@JsonCreator
41+
public static HttpSigAlgorithm of(String alg) {
42+
if (alg == null) {
43+
return null;
44+
} else if (alg.equalsIgnoreCase("jose")) {
45+
return JOSE;
46+
} else {
47+
return new HttpSigAlgorithm(alg);
48+
}
49+
}
50+
51+
@Override
52+
public boolean equals(Object obj) {
53+
if (this == obj) {
54+
return true;
55+
}
56+
if (obj == null) {
57+
return false;
58+
}
59+
if (getClass() != obj.getClass()) {
60+
return false;
61+
}
62+
HttpSigAlgorithm other = (HttpSigAlgorithm) obj;
63+
if (getExplicitAlg() == null) {
64+
if (other.getExplicitAlg() != null) {
65+
return false;
66+
}
67+
} else if (!getExplicitAlg().equals(other.getExplicitAlg())) {
68+
return false;
69+
}
70+
return true;
71+
}
72+
73+
@Override
74+
public int hashCode() {
75+
final int prime = 31;
76+
int result = 1;
77+
result = prime * result + ((getExplicitAlg() == null) ? 0 : getExplicitAlg().hashCode());
78+
return result;
79+
}
80+
81+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package io.bspk.httpsig;
2+
3+
import java.security.InvalidAlgorithmParameterException;
4+
import java.security.InvalidKeyException;
5+
import java.security.MessageDigest;
6+
import java.security.NoSuchAlgorithmException;
7+
import java.security.PrivateKey;
8+
import java.security.Signature;
9+
import java.security.SignatureException;
10+
import java.security.spec.MGF1ParameterSpec;
11+
import java.security.spec.PSSParameterSpec;
12+
13+
import javax.crypto.Mac;
14+
import javax.crypto.SecretKey;
15+
16+
import org.bouncycastle.jcajce.provider.digest.SHA256;
17+
import org.bouncycastle.jcajce.provider.digest.SHA512;
18+
import org.slf4j.Logger;
19+
import org.slf4j.LoggerFactory;
20+
21+
import com.nimbusds.jose.JOSEException;
22+
import com.nimbusds.jose.JWSAlgorithm;
23+
import com.nimbusds.jose.JWSHeader;
24+
import com.nimbusds.jose.JWSSigner;
25+
import com.nimbusds.jose.crypto.factories.DefaultJWSSignerFactory;
26+
import com.nimbusds.jose.crypto.impl.ECDSA;
27+
import com.nimbusds.jose.jwk.JWK;
28+
import com.nimbusds.jose.jwk.KeyType;
29+
import com.nimbusds.jose.util.Base64URL;
30+
31+
/**
32+
* @author jricher
33+
*
34+
*/
35+
public class HttpSign {
36+
37+
private static final Logger log = LoggerFactory.getLogger(HttpSign.class);
38+
39+
40+
private HttpSigAlgorithm alg;
41+
private JWK signingKey; // TODO: other key formats?
42+
43+
public HttpSign(HttpSigAlgorithm alg, JWK signingKey) {
44+
this.alg = alg;
45+
this.signingKey = signingKey;
46+
}
47+
48+
public byte[] sign(byte[] base) {
49+
try {
50+
if (alg.equals(HttpSigAlgorithm.RSAPSS)) {
51+
if (signingKey.getKeyType().equals(KeyType.RSA)) {
52+
PrivateKey privateKey = signingKey.toRSAKey().toPrivateKey();
53+
54+
Signature signer = Signature.getInstance("RSASSA-PSS");
55+
signer.setParameter(
56+
new PSSParameterSpec("SHA-512", "MGF1", MGF1ParameterSpec.SHA512, 64, 1));
57+
58+
MessageDigest sha = new SHA512.Digest();
59+
byte[] hash = sha.digest(base);
60+
61+
signer.initSign(privateKey);
62+
signer.update(hash);
63+
byte[] s = signer.sign();
64+
return s;
65+
}
66+
} else if (alg.equals(HttpSigAlgorithm.RSA15)) {
67+
if (signingKey.getKeyType().equals(KeyType.RSA)) {
68+
PrivateKey privateKey = signingKey.toRSAKey().toPrivateKey();
69+
70+
Signature signer = Signature.getInstance("SHA256withRSA");
71+
72+
MessageDigest sha = new SHA256.Digest();
73+
byte[] hash = sha.digest(base);
74+
75+
signer.initSign(privateKey);
76+
signer.update(hash);
77+
byte[] s = signer.sign();
78+
return s;
79+
}
80+
} else if (alg.equals(HttpSigAlgorithm.HMAC)) {
81+
if (signingKey.getKeyType().equals(KeyType.OCT)) {
82+
SecretKey secretKey = signingKey.toOctetSequenceKey().toSecretKey();
83+
84+
Mac mac = Mac.getInstance("HmacSHA256");
85+
mac.init(secretKey);
86+
mac.update(base);
87+
byte[] s = mac.doFinal();
88+
return s;
89+
}
90+
} else if (alg.equals(HttpSigAlgorithm.ECDSA)) {
91+
if (signingKey.getKeyType().equals(KeyType.EC)) {
92+
PrivateKey privateKey = signingKey.toECKey().toPrivateKey();
93+
94+
Signature signer = Signature.getInstance("SHA256withECDSA");
95+
96+
MessageDigest sha = new SHA256.Digest();
97+
byte[] hash = sha.digest(base);
98+
99+
signer.initSign(privateKey);
100+
signer.update(hash);
101+
byte[] rs = signer.sign();
102+
byte[] s = ECDSA.transcodeSignatureToConcat(rs, 64);
103+
return s;
104+
}
105+
} else if (alg.equals(HttpSigAlgorithm.JOSE)) {
106+
// do a JOSE signature based on what's in the key
107+
JWSSigner signer = new DefaultJWSSignerFactory().createJWSSigner(signingKey);
108+
109+
// create a fake header
110+
JWSAlgorithm alg = new JWSAlgorithm(signingKey.getAlgorithm().getName());
111+
JWSHeader header = new JWSHeader.Builder(alg).build();
112+
113+
Base64URL s = signer.sign(header, base);
114+
return s.decode();
115+
}
116+
} catch (InvalidKeyException | NoSuchAlgorithmException | InvalidAlgorithmParameterException | SignatureException | JOSEException e) {
117+
log.warn("Could not sign input", e);
118+
}
119+
return null;
120+
}
121+
122+
}

0 commit comments

Comments
 (0)