1
1
package com .amazonaws .encryptionsdk .internal ;
2
2
3
- import java .security .GeneralSecurityException ;
4
- import java .security .KeyPair ;
5
- import java .security .KeyPairGenerator ;
6
- import java .security .PublicKey ;
7
-
8
- import org .bouncycastle .crypto .params .ECDomainParameters ;
9
- import org .bouncycastle .crypto .params .ECPublicKeyParameters ;
10
- import org .bouncycastle .jcajce .provider .asymmetric .ec .BCECPublicKey ;
11
- import org .bouncycastle .jce .ECNamedCurveTable ;
12
- import org .bouncycastle .jce .interfaces .ECPublicKey ;
13
- import org .bouncycastle .jce .provider .BouncyCastleProvider ;
14
- import org .bouncycastle .jce .spec .ECNamedCurveParameterSpec ;
15
- import org .bouncycastle .math .ec .ECPoint ;
3
+ import java .math .BigInteger ;
4
+ import java .security .*;
5
+ import java .security .interfaces .ECPublicKey ;
6
+ import java .security .spec .*;
7
+ import java .util .Arrays ;
16
8
17
9
import com .amazonaws .encryptionsdk .CryptoAlgorithm ;
18
10
19
- import static com .amazonaws .encryptionsdk .internal .BouncyCastleConfiguration .INTERNAL_BOUNCY_CASTLE_PROVIDER ;
11
+ import static com .amazonaws .encryptionsdk .internal .Utils .bigIntegerToByteArray ;
12
+ import static com .amazonaws .encryptionsdk .internal .Utils .encodeBase64String ;
13
+ import static org .apache .commons .lang3 .Validate .*;
14
+ import static java .math .BigInteger .ONE ;
15
+ import static java .math .BigInteger .ZERO ;
20
16
21
17
/**
22
18
* Provides a consistent interface across various trailing signature algorithms.
@@ -36,15 +32,36 @@ private TrailingSignatureAlgorithm() {
36
32
public abstract String serializePublicKey (PublicKey key );
37
33
public abstract KeyPair generateKey () throws GeneralSecurityException ;
38
34
35
+ /** Standards for Efficient Cryptography over a prime field **/
36
+ private static final String SEC_PRIME_FIELD_PREFIX = "secp" ;
37
+
39
38
private static final class ECDSASignatureAlgorithm extends TrailingSignatureAlgorithm {
40
- private final ECNamedCurveParameterSpec ecSpec ;
39
+ private final ECGenParameterSpec ecSpec ;
40
+ private final ECParameterSpec ecParameterSpec ;
41
41
private final String messageDigestAlgorithm ;
42
42
private final String hashAndSignAlgorithm ;
43
+ private static final String ELLIPTIC_CURVE_ALGORITHM = "EC" ;
44
+ /* Constants used by SEC-1 v2 point compression and decompression algorithms */
45
+ private static final BigInteger TWO = BigInteger .valueOf (2 );
46
+ private static final BigInteger THREE = BigInteger .valueOf (3 );
47
+ private static final BigInteger FOUR = BigInteger .valueOf (4 );
48
+
49
+ private ECDSASignatureAlgorithm (ECGenParameterSpec ecSpec , String messageDigestAlgorithm ) {
50
+ if (!ecSpec .getName ().startsWith (SEC_PRIME_FIELD_PREFIX )) {
51
+ throw new IllegalStateException ("Non-prime curves are not supported at this time" );
52
+ }
43
53
44
- private ECDSASignatureAlgorithm (ECNamedCurveParameterSpec ecSpec , String messageDigestAlgorithm ) {
45
54
this .ecSpec = ecSpec ;
46
55
this .messageDigestAlgorithm = messageDigestAlgorithm ;
47
56
this .hashAndSignAlgorithm = messageDigestAlgorithm + "withECDSA" ;
57
+
58
+ try {
59
+ final AlgorithmParameters parameters = AlgorithmParameters .getInstance (ELLIPTIC_CURVE_ALGORITHM );
60
+ parameters .init (ecSpec );
61
+ this .ecParameterSpec = parameters .getParameterSpec (ECParameterSpec .class );
62
+ } catch (NoSuchAlgorithmException | InvalidParameterSpecException e ) {
63
+ throw new IllegalStateException ("Invalid algorithm" , e );
64
+ }
48
65
}
49
66
50
67
@ Override
@@ -66,37 +83,99 @@ public String getRawSignatureAlgorithm() {
66
83
return hashAndSignAlgorithm ;
67
84
}
68
85
86
+ /**
87
+ * Decodes a compressed elliptic curve point as described in SEC-1 v2 section 2.3.4
88
+ * @see <a href="http://www.secg.org/sec1-v2.pdf">http://www.secg.org/sec1-v2.pdf</a>
89
+ * @param keyString The serialized and compressed public key
90
+ * @return The PublicKey
91
+ */
69
92
@ Override
70
93
public PublicKey deserializePublicKey (String keyString ) {
71
- final ECPoint q = ecSpec .getCurve ().decodePoint (Utils .decodeBase64String (keyString ));
72
-
73
- ECPublicKeyParameters keyParams = new ECPublicKeyParameters (
74
- q ,
75
- new ECDomainParameters (ecSpec .getCurve (), ecSpec .getG (), ecSpec .getN (), ecSpec .getH ())
76
- );
77
-
78
- return new BCECPublicKey ("EC" , keyParams , ecSpec , BouncyCastleProvider .CONFIGURATION );
94
+ notNull (keyString , "keyString is required" );
95
+
96
+ final byte [] decodedKey = Utils .decodeBase64String (keyString );
97
+ final BigInteger x = new BigInteger (1 , Arrays .copyOfRange (decodedKey , 1 , decodedKey .length ));
98
+
99
+ final byte compressedY = decodedKey [0 ];
100
+ final BigInteger yOrder ;
101
+
102
+ if (compressedY == TWO .byteValue ()) {
103
+ yOrder = ZERO ;
104
+ } else if (compressedY == THREE .byteValue ()) {
105
+ yOrder = ONE ;
106
+ } else {
107
+ throw new IllegalArgumentException ("Compressed y value was invalid" );
108
+ }
109
+
110
+ final BigInteger p = ((ECFieldFp )ecParameterSpec .getCurve ().getField ()).getP ();
111
+ final BigInteger a = ecParameterSpec .getCurve ().getA ();
112
+ final BigInteger b = ecParameterSpec .getCurve ().getB ();
113
+
114
+ final BigInteger alpha = x .modPow (THREE , p )
115
+ .add (a .multiply (x ).mod (p ))
116
+ .add (b )
117
+ .mod (p );
118
+
119
+ final BigInteger beta ;
120
+ if (p .mod (FOUR ).equals (THREE )) {
121
+ beta = alpha .modPow (p .add (ONE ).divide (FOUR ), p );
122
+ } else {
123
+ throw new IllegalArgumentException ("Curve not supported at this time" );
124
+ }
125
+
126
+ final BigInteger y = beta .mod (TWO ).equals (yOrder ) ? beta : p .subtract (beta );
127
+
128
+ //Validate that Y is a root of Y^2 to prevent invalid point attacks
129
+ if (!alpha .equals (y .modPow (TWO , p ))) {
130
+ throw new IllegalArgumentException ("Y was invalid" );
131
+ }
132
+
133
+ try {
134
+ return KeyFactory .getInstance (ELLIPTIC_CURVE_ALGORITHM ).generatePublic (
135
+ new ECPublicKeySpec (new ECPoint (x , y ), ecParameterSpec ));
136
+ } catch (InvalidKeySpecException | NoSuchAlgorithmException e ) {
137
+ throw new IllegalStateException ("Invalid algorithm" , e );
138
+ }
79
139
}
80
140
141
+ /**
142
+ * Encodes a compressed elliptic curve point as described in SEC-1 v2 section 2.3.3
143
+ * @see <a href="http://www.secg.org/sec1-v2.pdf">http://www.secg.org/sec1-v2.pdf</a>
144
+ * @param key The Elliptic Curve public key to compress and serialize
145
+ * @return The serialized and compressed public key
146
+ */
81
147
@ Override
82
148
public String serializePublicKey (PublicKey key ) {
83
- return Utils .encodeBase64String (((ECPublicKey )key ).getQ ().getEncoded (true ));
149
+ notNull (key , "key is required" );
150
+ isInstanceOf (ECPublicKey .class , key , "key must be an instance of ECPublicKey" );
151
+
152
+ final BigInteger x = ((ECPublicKey )key ).getW ().getAffineX ();
153
+ final BigInteger y = ((ECPublicKey )key ).getW ().getAffineY ();
154
+ final BigInteger compressedY = y .mod (TWO ).equals (ZERO ) ? TWO : THREE ;
155
+
156
+ final byte [] xBytes = bigIntegerToByteArray (x ,
157
+ ecParameterSpec .getCurve ().getField ().getFieldSize () / Byte .SIZE );
158
+
159
+ final byte [] compressedKey = new byte [xBytes .length + 1 ];
160
+ System .arraycopy (xBytes , 0 , compressedKey , 1 , xBytes .length );
161
+ compressedKey [0 ] = compressedY .byteValue ();
162
+
163
+ return encodeBase64String (compressedKey );
84
164
}
85
165
86
166
@ Override
87
167
public KeyPair generateKey () throws GeneralSecurityException {
88
- // We use BouncyCastle for this so that we can easily serialize the compressed point.
89
- KeyPairGenerator keyGen = KeyPairGenerator .getInstance ("EC" , INTERNAL_BOUNCY_CASTLE_PROVIDER );
168
+ KeyPairGenerator keyGen = KeyPairGenerator .getInstance (ELLIPTIC_CURVE_ALGORITHM );
90
169
keyGen .initialize (ecSpec , Utils .getSecureRandom ());
91
170
92
171
return keyGen .generateKeyPair ();
93
172
}
94
173
}
95
174
96
175
private static final ECDSASignatureAlgorithm SHA256_ECDSA_P256
97
- = new ECDSASignatureAlgorithm (ECNamedCurveTable . getParameterSpec ( "secp256r1 " ), "SHA256" );
176
+ = new ECDSASignatureAlgorithm (new ECGenParameterSpec ( SEC_PRIME_FIELD_PREFIX + "256r1 " ), "SHA256" );
98
177
private static final ECDSASignatureAlgorithm SHA384_ECDSA_P384
99
- = new ECDSASignatureAlgorithm (ECNamedCurveTable . getParameterSpec ( "secp384r1 " ), "SHA384" );
178
+ = new ECDSASignatureAlgorithm (new ECGenParameterSpec ( SEC_PRIME_FIELD_PREFIX + "384r1 " ), "SHA384" );
100
179
101
180
public static TrailingSignatureAlgorithm forCryptoAlgorithm (CryptoAlgorithm algorithm ) {
102
181
switch (algorithm ) {
0 commit comments