Skip to content

Commit ee5cd74

Browse files
authored
Merge pull request #15 from nats-io/bouncy-castle
Replace net.i2p.crypto with BouncyCastle
2 parents 6063b61 + 62ec6fd commit ee5cd74

File tree

6 files changed

+131
-82
lines changed

6 files changed

+131
-82
lines changed

build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ plugins {
1111
id 'signing'
1212
}
1313

14-
def jarVersion = "2.0.2"
14+
def jarVersion = "2.1.0"
1515
group = 'io.nats'
1616

1717
def isMerge = System.getenv("BUILD_EVENT") == "push"
@@ -31,7 +31,7 @@ repositories {
3131
}
3232

3333
dependencies {
34-
implementation 'net.i2p.crypto:eddsa:0.3.0'
34+
implementation 'org.bouncycastle:bcprov-lts8on:2.73.7'
3535

3636
testImplementation 'org.junit.jupiter:junit-jupiter:5.7.0'
3737
testImplementation 'com.github.stefanbirkner:system-lambda:1.2.1'
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2025 The NATS Authors
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 io.nats.nkey;
15+
16+
import java.security.Key;
17+
18+
abstract class KeyWrapper implements Key {
19+
20+
@Override
21+
public String getAlgorithm() {
22+
return "EdDSA";
23+
}
24+
25+
@Override
26+
public String getFormat() {
27+
return "PKCS#8";
28+
}
29+
}

src/main/java/io/nats/nkey/NKey.java

Lines changed: 36 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2020-2024 The NATS Authors
1+
// Copyright 2020-2025 The NATS Authors
22
// Licensed under the Apache License, Version 2.0 (the "License");
33
// you may not use this file except in compliance with the License.
44
// You may obtain a copy of the License at:
@@ -13,42 +13,19 @@
1313

1414
package io.nats.nkey;
1515

16-
import static io.nats.nkey.NKeyConstants.ED25519_PRIVATE_KEYSIZE;
17-
import static io.nats.nkey.NKeyConstants.ED25519_PUBLIC_KEYSIZE;
18-
import static io.nats.nkey.NKeyConstants.ED25519_SEED_SIZE;
19-
import static io.nats.nkey.NKeyConstants.ED_25519;
20-
import static io.nats.nkey.NKeyConstants.PREFIX_BYTE_ACCOUNT;
21-
import static io.nats.nkey.NKeyConstants.PREFIX_BYTE_CLUSTER;
22-
import static io.nats.nkey.NKeyConstants.PREFIX_BYTE_OPERATOR;
23-
import static io.nats.nkey.NKeyConstants.PREFIX_BYTE_SEED;
24-
import static io.nats.nkey.NKeyConstants.PREFIX_BYTE_SERVER;
25-
import static io.nats.nkey.NKeyConstants.PREFIX_BYTE_USER;
26-
import static io.nats.nkey.NKeyUtils.PRAND;
27-
import static io.nats.nkey.NKeyUtils.SRAND;
28-
import static io.nats.nkey.NKeyUtils.base32Decode;
29-
import static io.nats.nkey.NKeyUtils.base32Encode;
30-
import static io.nats.nkey.NKeyUtils.crc16;
16+
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
17+
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
18+
import org.bouncycastle.crypto.signers.Ed25519Signer;
3119

3220
import java.io.ByteArrayOutputStream;
3321
import java.io.IOException;
3422
import java.nio.ByteBuffer;
3523
import java.nio.ByteOrder;
36-
import java.security.GeneralSecurityException;
37-
import java.security.KeyPair;
38-
import java.security.MessageDigest;
39-
import java.security.NoSuchAlgorithmException;
40-
import java.security.NoSuchProviderException;
41-
import java.security.PrivateKey;
42-
import java.security.PublicKey;
43-
import java.security.SecureRandom;
44-
import java.security.Signature;
24+
import java.security.*;
4525
import java.util.Arrays;
4626

47-
import net.i2p.crypto.eddsa.EdDSAEngine;
48-
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
49-
import net.i2p.crypto.eddsa.EdDSAPublicKey;
50-
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
51-
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
27+
import static io.nats.nkey.NKeyConstants.*;
28+
import static io.nats.nkey.NKeyUtils.*;
5229

5330
public class NKey {
5431

@@ -173,23 +150,21 @@ static NKeyDecodedSeed decodeSeed(char[] seed) {
173150

174151
private static NKey createPair(NKeyType type, SecureRandom random)
175152
throws IOException, NoSuchProviderException, NoSuchAlgorithmException {
153+
byte[] seed = new byte[ED25519_SEED_SIZE];
176154
if (random == null) {
177-
random = SRAND;
155+
SRAND.nextBytes(seed);
156+
}
157+
else {
158+
random.nextBytes(seed);
178159
}
179-
180-
byte[] seed = new byte[ED_25519.getCurve().getField().getb() / 8];
181-
random.nextBytes(seed);
182-
183160
return createPair(type, seed);
184161
}
185162

186-
private static NKey createPair(NKeyType type, byte[] seed)
187-
throws IOException, NoSuchProviderException, NoSuchAlgorithmException {
188-
EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(seed, ED_25519);
189-
EdDSAPrivateKey privKey = new EdDSAPrivateKey(privKeySpec);
190-
EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), ED_25519);
191-
EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec);
192-
byte[] pubBytes = pubKey.getAbyte();
163+
private static NKey createPair(NKeyType type, byte[] seed) throws IOException {
164+
Ed25519PrivateKeyParameters privateKey = new Ed25519PrivateKeyParameters(seed);
165+
Ed25519PublicKeyParameters publicKey = privateKey.generatePublicKey();
166+
167+
byte[] pubBytes = publicKey.getEncoded();
193168

194169
byte[] bytes = new byte[pubBytes.length + seed.length];
195170
System.arraycopy(seed, 0, bytes, 0, seed.length);
@@ -416,12 +391,7 @@ public char[] getPublicKey() throws GeneralSecurityException, IOException {
416391
if (publicKey != null) {
417392
return publicKey;
418393
}
419-
420-
KeyPair keys = getKeyPair();
421-
EdDSAPublicKey pubKey = (EdDSAPublicKey) keys.getPublic();
422-
byte[] pubBytes = pubKey.getAbyte();
423-
424-
return encode(this.type, pubBytes);
394+
return encode(this.type, getKeyPair().getPublic().getEncoded());
425395
}
426396

427397
/**
@@ -458,12 +428,10 @@ public KeyPair getKeyPair() throws GeneralSecurityException, IOException {
458428
System.arraycopy(decoded.bytes, 0, seedBytes, 0, seedBytes.length);
459429
System.arraycopy(decoded.bytes, seedBytes.length, pubBytes, 0, pubBytes.length);
460430

461-
EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(seedBytes, ED_25519);
462-
EdDSAPrivateKey privKey = new EdDSAPrivateKey(privKeySpec);
463-
EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(pubBytes, ED_25519);
464-
EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec);
431+
Ed25519PrivateKeyParameters privateKey = new Ed25519PrivateKeyParameters(seedBytes);
432+
Ed25519PublicKeyParameters publicKey = new Ed25519PublicKeyParameters(pubBytes);
465433

466-
return new KeyPair(pubKey, privKey);
434+
return new KeyPair(new PublicKeyWrapper(publicKey), new PrivateKeyWrapper(privateKey));
467435
}
468436

469437
/**
@@ -483,13 +451,11 @@ public NKeyType getType() {
483451
* @throws IOException if there is a problem reading the data
484452
*/
485453
public byte[] sign(byte[] input) throws GeneralSecurityException, IOException {
486-
Signature sgr = new EdDSAEngine(MessageDigest.getInstance(ED_25519.getHashAlgorithm()));
487-
PrivateKey sKey = getKeyPair().getPrivate();
488-
489-
sgr.initSign(sKey);
490-
sgr.update(input);
491-
492-
return sgr.sign();
454+
Ed25519PrivateKeyParameters privateKey = new Ed25519PrivateKeyParameters(getKeyPair().getPrivate().getEncoded());
455+
Ed25519Signer signer = new Ed25519Signer();
456+
signer.init(true, privateKey);
457+
signer.update(input, 0, input.length);
458+
return signer.generateSignature();
493459
}
494460

495461
/**
@@ -503,26 +469,20 @@ public byte[] sign(byte[] input) throws GeneralSecurityException, IOException {
503469
* @throws IOException if there is a problem reading the data
504470
*/
505471
public boolean verify(byte[] input, byte[] signature) throws GeneralSecurityException, IOException {
506-
Signature sgr = new EdDSAEngine(MessageDigest.getInstance(ED_25519.getHashAlgorithm()));
507-
PublicKey sKey;
508-
472+
Ed25519PublicKeyParameters publicKey;
509473
if (privateKeyAsSeed != null) {
510-
sKey = getKeyPair().getPublic();
511-
}
512-
else {
474+
publicKey = new Ed25519PublicKeyParameters(getKeyPair().getPublic().getEncoded());
475+
} else {
513476
char[] encodedPublicKey = getPublicKey();
514477
byte[] decodedPublicKey = decode(this.type, encodedPublicKey);
515-
if (decodedPublicKey == null) {
516-
throw new IllegalArgumentException("Unexpected type");
517-
}
518-
EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(decodedPublicKey, ED_25519);
519-
sKey = new EdDSAPublicKey(pubKeySpec);
478+
//noinspection DataFlowIssue // decode will throw instead of return null
479+
publicKey = new Ed25519PublicKeyParameters(decodedPublicKey);
520480
}
521481

522-
sgr.initVerify(sKey);
523-
sgr.update(input);
524-
525-
return sgr.verify(signature);
482+
Ed25519Signer signer = new Ed25519Signer();
483+
signer.init(false, publicKey);
484+
signer.update(input, 0, input.length);
485+
return signer.verifySignature(signature);
526486
}
527487

528488
@Override
@@ -559,3 +519,4 @@ public int hashCode() {
559519
return result;
560520
}
561521
}
522+

src/main/java/io/nats/nkey/NKeyConstants.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@
1313

1414
package io.nats.nkey;
1515

16-
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
17-
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
18-
1916
public interface NKeyConstants {
2017
// PrefixByteSeed is the prefix byte used for encoded NATS Seeds
2118
int PREFIX_BYTE_SEED = 18 << 3; // Base32-encodes to 'S...'
@@ -42,8 +39,6 @@ public interface NKeyConstants {
4239
int ED25519_PRIVATE_KEYSIZE = 64;
4340
int ED25519_SEED_SIZE = 32;
4441

45-
EdDSANamedCurveSpec ED_25519 = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);
46-
4742
// XModem CRC based on the go version of NKeys
4843
int[] CRC_16_TABLE = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108,
4944
0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294,
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2025 The NATS Authors
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 io.nats.nkey;
15+
16+
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
17+
18+
import java.security.PrivateKey;
19+
20+
class PrivateKeyWrapper extends KeyWrapper implements PrivateKey {
21+
22+
final Ed25519PrivateKeyParameters privateKey;
23+
24+
public PrivateKeyWrapper(Ed25519PrivateKeyParameters privateKey) {
25+
this.privateKey = privateKey;
26+
}
27+
28+
@Override
29+
public byte[] getEncoded() {
30+
return privateKey.getEncoded();
31+
}
32+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2025 The NATS Authors
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 io.nats.nkey;
15+
16+
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
17+
18+
import java.security.PublicKey;
19+
20+
class PublicKeyWrapper extends KeyWrapper implements PublicKey {
21+
22+
final Ed25519PublicKeyParameters publicKey;
23+
24+
public PublicKeyWrapper(Ed25519PublicKeyParameters publicKey) {
25+
this.publicKey = publicKey;
26+
}
27+
28+
@Override
29+
public byte[] getEncoded() {
30+
return publicKey.getEncoded();
31+
}
32+
}

0 commit comments

Comments
 (0)