11package com .trilead .ssh2 .crypto .dh ;
22
3- import java .math .BigInteger ;
43import java .security .InvalidKeyException ;
54import java .security .KeyFactory ;
65import java .security .KeyPair ;
98import java .security .PrivateKey ;
109import java .security .PublicKey ;
1110import java .security .spec .InvalidKeySpecException ;
12- import java .security .spec .NamedParameterSpec ;
1311import java .security .spec .PKCS8EncodedKeySpec ;
12+ import java .security .spec .X509EncodedKeySpec ;
1413import java .security .spec .XECPrivateKeySpec ;
15- import java .security .spec .XECPublicKeySpec ;
1614
1715import javax .crypto .KeyAgreement ;
1816
1917/**
20- * X25519 provider implementation using platform-native APIs (Java 11+/Android API 33+).
18+ * X25519 provider implementation using platform-native APIs (Java 11+/Android
19+ * API 33+).
2120 */
2221public class PlatformX25519Provider implements X25519Provider {
2322 private static final String ALGORITHM = "X25519" ;
24- private static final NamedParameterSpec X25519_SPEC = new NamedParameterSpec (ALGORITHM );
2523
2624 private final KeyPairGenerator keyPairGenerator ;
2725 private final KeyFactory keyFactory ;
@@ -48,18 +46,22 @@ public byte[] publicFromPrivate(byte[] privateKey) throws InvalidKeyException {
4846 return pubKeyBytes ;
4947 }
5048
49+ private static final byte [] BASE_POINT = new byte [KEY_SIZE ];
50+ static {
51+ BASE_POINT [0 ] = 9 ;
52+ }
53+
5154 private void computePublicFromPrivate (byte [] privateKey , byte [] publicKey ) throws InvalidKeyException {
5255 try {
5356 PrivateKey privKey = createPrivateKey (privateKey );
5457 KeyAgreement ka = KeyAgreement .getInstance (ALGORITHM );
5558 ka .init (privKey );
5659
57- XECPublicKeySpec basePointSpec = new XECPublicKeySpec (X25519_SPEC , BigInteger .valueOf (9 ));
58- PublicKey basePoint = keyFactory .generatePublic (basePointSpec );
60+ PublicKey basePoint = createPublicKey (BASE_POINT );
5961 ka .doPhase (basePoint , true );
6062 byte [] result = ka .generateSecret ();
6163 System .arraycopy (result , 0 , publicKey , 0 , KEY_SIZE );
62- } catch (NoSuchAlgorithmException | InvalidKeySpecException e ) {
64+ } catch (NoSuchAlgorithmException e ) {
6365 throw new InvalidKeyException ("X25519 not available" , e );
6466 }
6567 }
@@ -81,12 +83,19 @@ public byte[] computeSharedSecret(byte[] privateKey, byte[] publicKey) throws In
8183 }
8284
8385 private static final byte [] PKCS8_PREFIX = {
84- 0x30 , 0x2e , // SEQUENCE (46 bytes)
85- 0x02 , 0x01 , 0x00 , // INTEGER 0 (version)
86- 0x30 , 0x05 , // SEQUENCE (5 bytes)
87- 0x06 , 0x03 , 0x2b , 0x65 , 0x6e , // OID 1.3.101.110 (X25519)
88- 0x04 , 0x22 , // OCTET STRING (34 bytes)
89- 0x04 , 0x20 // OCTET STRING (32 bytes) - key follows
86+ 0x30 , 0x2e , // SEQUENCE (46 bytes)
87+ 0x02 , 0x01 , 0x00 , // INTEGER 0 (version)
88+ 0x30 , 0x05 , // SEQUENCE (5 bytes)
89+ 0x06 , 0x03 , 0x2b , 0x65 , 0x6e , // OID 1.3.101.110 (X25519)
90+ 0x04 , 0x22 , // OCTET STRING (34 bytes)
91+ 0x04 , 0x20 // OCTET STRING (32 bytes) - key follows
92+ };
93+
94+ private static final byte [] X509_PREFIX = {
95+ 0x30 , 0x2a , // SEQUENCE (42 bytes)
96+ 0x30 , 0x05 , // SEQUENCE (5 bytes)
97+ 0x06 , 0x03 , 0x2b , 0x65 , 0x6e , // OID 1.3.101.110 (X25519)
98+ 0x03 , 0x21 , 0x00 // BIT STRING (33 bytes, 0 unused bits)
9099 };
91100
92101 private PrivateKey createPrivateKey (byte [] keyBytes ) throws InvalidKeyException {
@@ -103,8 +112,10 @@ private PrivateKey createPrivateKey(byte[] keyBytes) throws InvalidKeyException
103112
104113 private PublicKey createPublicKey (byte [] keyBytes ) throws InvalidKeyException {
105114 try {
106- BigInteger u = decodeLittleEndian (keyBytes );
107- XECPublicKeySpec spec = new XECPublicKeySpec (X25519_SPEC , u );
115+ byte [] x509 = new byte [X509_PREFIX .length + KEY_SIZE ];
116+ System .arraycopy (X509_PREFIX , 0 , x509 , 0 , X509_PREFIX .length );
117+ System .arraycopy (keyBytes , 0 , x509 , X509_PREFIX .length , KEY_SIZE );
118+ X509EncodedKeySpec spec = new X509EncodedKeySpec (x509 );
108119 return keyFactory .generatePublic (spec );
109120 } catch (InvalidKeySpecException e ) {
110121 throw new InvalidKeyException ("Invalid public key" , e );
@@ -129,11 +140,4 @@ private byte[] extractPrivateKeyBytes(PrivateKey privateKey) {
129140 }
130141 }
131142
132- private static BigInteger decodeLittleEndian (byte [] bytes ) {
133- byte [] reversed = new byte [bytes .length ];
134- for (int i = 0 ; i < bytes .length ; i ++) {
135- reversed [i ] = bytes [bytes .length - 1 - i ];
136- }
137- return new BigInteger (1 , reversed );
138- }
139143}
0 commit comments