0.11.3 to master (#728)

Merged 0.11.3 patch release into mainline development branch
This commit is contained in:
Les Hazlewood 2022-04-23 17:32:28 -04:00 committed by GitHub
parent d91881fbac
commit b78473262d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 306 additions and 53 deletions

View File

@ -1,12 +1,61 @@
## Release Notes
### 0.11.3 (pending release)
### 0.11.4 (pending release)
This patch release:
* Adds additional handling for rare JSON parsing exceptions and wraps them in a `JwtException` to allow the application to handle these conditions as JWT concerns.
* Upgrades the `jjwt-jackson` module's Jackson dependency to `2.12.6.1`.
### 0.11.3
This patch release adds security guards against an ECDSA bug in Java SE versions 15-15.0.6, 17-17.0.2, and 18
([CVE-2022-21449](https://nvd.nist.gov/vuln/detail/CVE-2022-21449)). This patch allows JJWT users using those JVM
versions to upgrade to JJWT 0.11.3, even if they are unable to upgrade their JVM to patched/fixed JVM version in a
timely manner. Note: if your application does not use these JVM versions, you are not exposed to the JVM vulnerability.
Note that the CVE is not a bug within JJWT itself - it is a bug within the above listed JVM versions, and the
JJWT 0.11.3 release adds additional precautions within JJWT in case an application team is not able to upgrade
their JVM in a timely manner.
However, even with these additional JJWT security guards, the root cause of the issue is the JVM, so it **strongly
recommended** to upgrade your JVM to version
15.0.7, 17.0.3, or 18.0.1 or later to ensure the bug does not surface elsewhere in your application code or any other
third party library in your application that may not contain similar security guards.
Issues included in this patch are listed in the [JJWT 0.11.3 milestone](https://github.com/jwtk/jjwt/milestone/24).
#### Backwards Compatibility Warning
In addition to additional protections against
[r or s values of zero in ECDSA signatures](https://neilmadden.blog/2022/04/19/psychic-signatures-in-java/), this
release also disables by default legacy DER-encoded signatures that might be included in an ECDSA-signed JWT.
(DER-encoded signatures are not supported by the JWT RFC specifications, so they are not frequently encountered.)
However, if you are using an application that needs to consume such legacy JWTs (either produced by a very
early version of JJWT, or a different JWT library), you may re-enable DER-encoded ECDSA signatures by setting the
`io.jsonwebtoken.impl.crypto.EllipticCurveSignatureValidator.derEncodingSupported` System property to the _exact_
`String` value `true`. For example:
```java
System.setProperty("io.jsonwebtoken.impl.crypto.EllipticCurveSignatureValidator.derEncodingSupported", "true");
```
*BUT BE CAREFUL*: **DO NOT** set this System property if your application may run on one of the vulnerable JVMs
noted above (Java SE versions 15-15.0.6, 17-17.0.2, and 18).
You may safely set this property to a `String` value of `true` on all other versions of the JVM if you need to
support these legacy JWTs, *otherwise it is best to ignore (not set) the property entirely*.
#### Credits
Thank you to [Neil Madden](https://neilmadden.blog), the security researcher that first discovered the JVM
vulnerability as covered in his [Psychic Signatures in Java](https://neilmadden.blog/2022/04/19/psychic-signatures-in-java/) blog post.
We'd also like to thank Toshiki Sasazaki, a member of [LINE Corporation](https://linecorp.com)'s Application Security
Team as the first person to report the concern directly to the JJWT team, as well as for working with us during testing
leading to our conclusions and subsequent 0.11.3 patch release.
### 0.11.2
This patch release:

View File

@ -264,18 +264,18 @@ If you're building a (non-Android) JDK project, you will want to define the foll
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
<version>0.11.3</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<version>0.11.3</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
<version>0.11.2</version>
<version>0.11.3</version>
<scope>runtime</scope>
</dependency>
<!-- Uncomment this next dependency if you are using JDK 10 or earlier and you also want to use
@ -295,11 +295,11 @@ If you're building a (non-Android) JDK project, you will want to define the foll
```groovy
dependencies {
implementation 'io.jsonwebtoken:jjwt-api:0.11.2'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.2',
// Uncomment the next line if you want to use RSASSA-PSS (PS256, PS384, PS512) algorithms:
//'org.bouncycastle:bcprov-jdk15on:1.60',
'io.jsonwebtoken:jjwt-jackson:0.11.2' // or 'io.jsonwebtoken:jjwt-gson:0.11.2' for gson
compile 'io.jsonwebtoken:jjwt-api:0.11.3'
runtime 'io.jsonwebtoken:jjwt-impl:0.11.3',
// Uncomment the next line if you want to use RSASSA-PSS (PS256, PS384, PS512) algorithms:
//'org.bouncycastle:bcprov-jdk15on:1.60',
'io.jsonwebtoken:jjwt-jackson:0.11.3' // or 'io.jsonwebtoken:jjwt-gson:0.11.3' for gson
}
```
@ -315,9 +315,9 @@ Add the dependencies to your project:
```groovy
dependencies {
api 'io.jsonwebtoken:jjwt-api:0.11.2'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.2'
runtimeOnly('io.jsonwebtoken:jjwt-orgjson:0.11.2') {
api 'io.jsonwebtoken:jjwt-api:0.11.3'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.3'
runtimeOnly('io.jsonwebtoken:jjwt-orgjson:0.11.3') {
exclude group: 'org.json', module: 'json' //provided by Android natively
}
// Uncomment the next line if you want to use RSASSA-PSS (PS256, PS384, PS512) algorithms:
@ -1328,7 +1328,7 @@ scope which is the typical JJWT default). That is:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.2</version>
<version>0.11.3</version>
<scope>compile</scope> <!-- Not runtime -->
</dependency>
```
@ -1337,7 +1337,7 @@ scope which is the typical JJWT default). That is:
```groovy
dependencies {
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.2'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.3'
}
```
@ -1436,7 +1436,7 @@ scope which is the typical JJWT default). That is:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-gson</artifactId>
<version>0.11.2</version>
<version>0.11.3</version>
<scope>compile</scope> <!-- Not runtime -->
</dependency>
```
@ -1445,7 +1445,7 @@ scope which is the typical JJWT default). That is:
```groovy
dependencies {
implementation 'io.jsonwebtoken:jjwt-gson:0.11.2'
implementation 'io.jsonwebtoken:jjwt-gson:0.11.3'
}
```

View File

@ -21,7 +21,7 @@
<parent>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-root</artifactId>
<version>0.11.3-SNAPSHOT</version>
<version>0.11.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -21,7 +21,7 @@
<parent>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-root</artifactId>
<version>0.11.3-SNAPSHOT</version>
<version>0.11.4-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@ -21,7 +21,7 @@
<parent>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-root</artifactId>
<version>0.11.3-SNAPSHOT</version>
<version>0.11.4-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@ -21,7 +21,7 @@
<parent>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-root</artifactId>
<version>0.11.3-SNAPSHOT</version>
<version>0.11.4-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@ -21,7 +21,7 @@
<parent>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-root</artifactId>
<version>0.11.3-SNAPSHOT</version>
<version>0.11.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -21,7 +21,7 @@
<parent>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-root</artifactId>
<version>0.11.3-SNAPSHOT</version>
<version>0.11.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -19,6 +19,7 @@ import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.Strings;
import io.jsonwebtoken.security.SignatureException;
import java.security.Key;
import java.security.KeyPair;
@ -238,6 +239,15 @@ public abstract class EllipticCurveProvider extends SignatureProvider {
* @throws JwtException If the ECDSA JWS signature format is invalid.
*/
public static byte[] transcodeSignatureToDER(byte[] jwsSignature) throws JwtException {
try {
return concatToDER(jwsSignature);
} catch (Exception e) { // CVE-2022-21449 guard
String msg = "Invalid ECDSA signature format.";
throw new SignatureException(msg, e);
}
}
private static byte[] concatToDER(byte[] jwsSignature) throws ArrayIndexOutOfBoundsException {
int rawLen = jwsSignature.length / 2;
@ -247,6 +257,10 @@ public abstract class EllipticCurveProvider extends SignatureProvider {
i--;
}
if (i == 0) { // r == 0, JVM bug CVE-2202-21449 guard:
throw new JwtException("Invalid ECDSA Signature format");
}
int j = i;
if (jwsSignature[rawLen - i] < 0) {
@ -273,7 +287,7 @@ public abstract class EllipticCurveProvider extends SignatureProvider {
int offset;
final byte derSignature[];
final byte[] derSignature;
if (len < 128) {
derSignature = new byte[2 + 2 + j + 2 + l];

View File

@ -30,6 +30,9 @@ public class EllipticCurveSignatureValidator extends EllipticCurveProvider imple
private static final String EC_PUBLIC_KEY_REQD_MSG =
"Elliptic Curve signature validation requires an ECPublicKey instance.";
private static final String DER_ENCODING_SYS_PROPERTY_NAME =
"io.jsonwebtoken.impl.crypto.EllipticCurveSignatureValidator.derEncodingSupported";
public EllipticCurveSignatureValidator(SignatureAlgorithm alg, Key key) {
super(alg, key);
Assert.isTrue(key instanceof ECPublicKey, EC_PUBLIC_KEY_REQD_MSG);
@ -41,14 +44,20 @@ public class EllipticCurveSignatureValidator extends EllipticCurveProvider imple
PublicKey publicKey = (PublicKey) key;
try {
int expectedSize = getSignatureByteArrayLength(alg);
/**
*
* If the expected size is not valid for JOSE, fall back to ASN.1 DER signature.
* This fallback is for backwards compatibility ONLY (to support tokens generated by previous versions of jjwt)
* and backwards compatibility will possibly be removed in a future version of this library.
*
* **/
byte[] derSignature = expectedSize != signature.length && signature[0] == 0x30 ? signature : EllipticCurveProvider.transcodeSignatureToDER(signature);
/*
* If the expected size is not valid for JOSE, fall back to ASN.1 DER signature IFF the application
* is configured to do so. This fallback is for backwards compatibility ONLY (to support tokens
* generated by early versions of jjwt) and backwards compatibility will be removed in a future
* version of this library. This fallback is only enabled if the system property is set to 'true' due to
* the risk of CVE-2022-21449 attacks on early JVM versions 15, 17 and 18.
*/
//TODO: remove for 1.0 (DER-encoding support is not in the JWT RFCs)
byte[] derSignature;
if (expectedSize != signature.length && signature[0] == 0x30 && "true".equalsIgnoreCase(System.getProperty(DER_ENCODING_SYS_PROPERTY_NAME))) {
derSignature = signature;
} else {
derSignature = EllipticCurveProvider.transcodeSignatureToDER(signature);
}
return doVerify(sig, publicKey, data, derSignature);
} catch (Exception e) {
String msg = "Unable to verify Elliptic Curve signature using configured ECPublicKey. " + e.getMessage();

View File

@ -21,7 +21,10 @@ import io.jsonwebtoken.io.Decoders
import io.jsonwebtoken.security.SignatureException
import org.junit.Test
import java.security.*
import java.security.InvalidKeyException
import java.security.KeyFactory
import java.security.PublicKey
import java.security.Signature
import java.security.spec.X509EncodedKeySpec
import static org.junit.Assert.*
@ -73,8 +76,8 @@ class EllipticCurveSignatureValidatorTest {
verifier("eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJ0ZXN0IjoidGVzdCJ9.AV26tERbSEwcoDGshneZmhokg-tAKUk0uQBoHBohveEd51D5f6EIs6cskkgwtfzs4qAGfx2rYxqQXr7LTXCNquKiAJNkTIKVddbPfped3_TQtmHZTmMNiqmWjiFj7Y9eTPMMRRu26w4gD1a8EQcBF-7UGgeH4L_1CwHJWAXGbtu7uMUn")
}
@Test
void legacySignatureCompatTest() {
@Test // asserts guard for JVM security bug CVE-2022-21449:
void legacySignatureCompatDefaultTest() {
def withoutSignature = "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoidGVzdCIsImlhdCI6MTQ2NzA2NTgyN30"
def keypair = EllipticCurveProvider.generateKeyPair()
def signature = Signature.getInstance(SignatureAlgorithm.ES512.jcaName)
@ -82,7 +85,76 @@ class EllipticCurveSignatureValidatorTest {
signature.initSign(keypair.private)
signature.update(data)
def signed = signature.sign()
assert new EllipticCurveSignatureValidator(SignatureAlgorithm.ES512, keypair.public).isValid(data, signed)
assertFalse new EllipticCurveSignatureValidator(SignatureAlgorithm.ES512, keypair.public).isValid(data, signed)
}
@Test
void legacySignatureCompatWhenEnabledTest() {
try {
System.setProperty(EllipticCurveSignatureValidator.DER_ENCODING_SYS_PROPERTY_NAME, 'true')
def withoutSignature = "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoidGVzdCIsImlhdCI6MTQ2NzA2NTgyN30"
def keypair = EllipticCurveProvider.generateKeyPair()
def signature = Signature.getInstance(SignatureAlgorithm.ES512.jcaName)
def data = withoutSignature.getBytes("US-ASCII")
signature.initSign(keypair.private)
signature.update(data)
def signed = signature.sign()
assertTrue new EllipticCurveSignatureValidator(SignatureAlgorithm.ES512, keypair.public).isValid(data, signed)
} finally {
System.clearProperty(EllipticCurveSignatureValidator.DER_ENCODING_SYS_PROPERTY_NAME)
}
}
@Test // asserts guard for JVM security bug CVE-2022-21449:
void testSignatureAllZeros() {
try {
byte[] forgedSig = new byte[64]
def withoutSignature = "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoidGVzdCIsImlhdCI6MTQ2NzA2NTgyN30"
def keypair = EllipticCurveProvider.generateKeyPair()
def data = withoutSignature.getBytes("US-ASCII")
new EllipticCurveSignatureValidator(SignatureAlgorithm.ES256, keypair.public).isValid(data, forgedSig)
fail("SignatureException expected")
} catch(SignatureException expected) {
assertEquals 'Unable to verify Elliptic Curve signature using configured ECPublicKey. Invalid ECDSA signature format.', expected.getMessage()
}
}
@Test // asserts guard for JVM security bug CVE-2022-21449:
void testSignatureRZero() {
try {
byte[] r = new byte[32]
byte[] s = new byte[32]; Arrays.fill(s, Byte.MAX_VALUE)
byte[] sig = new byte[r.length + s.length]
System.arraycopy(r, 0, sig, 0, r.length)
System.arraycopy(s, 0, sig, r.length, s.length)
def withoutSignature = "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoidGVzdCIsImlhdCI6MTQ2NzA2NTgyN30"
def keypair = EllipticCurveProvider.generateKeyPair()
def data = withoutSignature.getBytes("US-ASCII")
new EllipticCurveSignatureValidator(SignatureAlgorithm.ES256, keypair.public).isValid(data, sig)
fail("SignatureException expected")
} catch(SignatureException expected) {
assertEquals 'Unable to verify Elliptic Curve signature using configured ECPublicKey. Invalid ECDSA signature format.', expected.getMessage()
}
}
@Test // asserts guard for JVM security bug CVE-2022-21449:
void testSignatureSZero() {
try {
byte[] r = new byte[32]; Arrays.fill(r, Byte.MAX_VALUE);
byte[] s = new byte[32]
byte[] sig = new byte[r.length + s.length]
System.arraycopy(r, 0, sig, 0, r.length)
System.arraycopy(s, 0, sig, r.length, s.length)
def withoutSignature = "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoidGVzdCIsImlhdCI6MTQ2NzA2NTgyN30"
def keypair = EllipticCurveProvider.generateKeyPair()
def data = withoutSignature.getBytes("US-ASCII")
new EllipticCurveSignatureValidator(SignatureAlgorithm.ES256, keypair.public).isValid(data, sig)
fail("SignatureException expected")
} catch(SignatureException expected) {
assertEquals 'Unable to verify Elliptic Curve signature using configured ECPublicKey. Invalid ECDSA signature format.', expected.getMessage()
}
}
@Test
@ -104,7 +176,7 @@ class EllipticCurveSignatureValidatorTest {
EllipticCurveProvider.transcodeSignatureToDER(signature)
fail()
} catch (JwtException e) {
assertEquals e.message, 'Invalid ECDSA signature format'
assertEquals e.message, 'Invalid ECDSA signature format.'
}
}
@ -132,12 +204,6 @@ class EllipticCurveSignatureValidatorTest {
verify(signature)
}
@Test
void edgeCaseSignatureLengthTest() {
def signature = new byte[1]
EllipticCurveProvider.transcodeSignatureToDER(signature)
}
@Test
void testPaddedSignatureToDER() {
def signature = new byte[32]

137
pom.xml
View File

@ -17,15 +17,9 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
<version>7</version>
</parent>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-root</artifactId>
<version>0.11.3-SNAPSHOT</version>
<version>0.11.4-SNAPSHOT</version>
<name>JJWT</name>
<description>JSON Web Token support for the JVM and Android</description>
<packaging>pom</packaging>
@ -40,11 +34,20 @@
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0</url>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<name>Les Hazlewood</name>
<email>121180+lhazlewood@users.noreply.github.com</email>
<organization>JJWT</organization>
<organizationUrl>https://github.com/jwtk/jjwt</organizationUrl>
</developer>
</developers>
<scm>
<connection>scm:git:https://github.com/jwtk/jjwt.git</connection>
<developerConnection>scm:git:git@github.com:jwtk/jjwt.git</developerConnection>
@ -59,8 +62,54 @@
<system>TravisCI</system>
<url>https://travis-ci.org/jwtk/jjwt</url>
</ciManagement>
<distributionManagement>
<snapshotRepository>
<id>ossrh</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
<repository>
<id>ossrh</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>
<!-- temporary fix until official release of coverall-maven-plugin with clover support -->
<repositories>
<repository>
<id>ossrh</id>
<name>Sonatype Nexus Snapshots</name>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>bintray-jwtk-coveralls-maven-plugin</id>
<name>bintray</name>
<url>https://dl.bintray.com/jwtk/coveralls-maven-plugin</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>bintray-jwtk-coveralls-maven-plugin</id>
<name>bintray-plugins</name>
<url>https://dl.bintray.com/jwtk/coveralls-maven-plugin</url>
</pluginRepository>
</pluginRepositories>
<!-- temporary fix until official release of coverall-maven-plugin with clover support -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<arguments/>
<jjwt.root>${basedir}</jjwt.root>
<jjwt.previousVersion>0.10.7</jjwt.previousVersion>
@ -71,7 +120,6 @@
<gmavenplus.version>1.6.1</gmavenplus.version>
<jdk.version>1.7</jdk.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<buildNumber>${user.name}-${maven.build.timestamp}</buildNumber>
<jackson.version>2.12.6.1</jackson.version>
@ -204,6 +252,16 @@
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.3</version>
<configuration>
<mavenExecutorId>forked-path</mavenExecutorId>
<useReleaseProfile>false</useReleaseProfile>
<arguments>${arguments} -Possrh</arguments>
</configuration>
</plugin>
<plugin>
<groupId>org.openclover</groupId>
<artifactId>clover-maven-plugin</artifactId>
@ -455,10 +513,21 @@
<configuration>
<mavenExecutorId>forked-path</mavenExecutorId>
<useReleaseProfile>false</useReleaseProfile>
<arguments>-Psonatype-oss-release -Pdocs -Psign</arguments>
<arguments>-Possrh -Pdocs -Psign</arguments>
<autoVersionSubmodules>true</autoVersionSubmodules>
</configuration>
</plugin>
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.7</version>
<extensions>true</extensions>
<configuration>
<serverId>ossrh</serverId>
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>false</autoReleaseAfterClose>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
@ -502,7 +571,6 @@
</plugin>
<!-- Temporarily host coveralls SNAPSHOT with clover support locally -->
<!-- Override OSS parent to support Java 9 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
@ -615,5 +683,52 @@
</plugins>
</build>
</profile>
<profile>
<id>ossrh</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.2</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.7</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>