1 package org.bouncycastle.jcajce.provider.asymmetric.ec; 2 3 import java.math.BigInteger; 4 import java.security.InvalidAlgorithmParameterException; 5 import java.security.InvalidKeyException; 6 import java.security.Key; 7 import java.security.NoSuchAlgorithmException; 8 import java.security.PrivateKey; 9 import java.security.PublicKey; 10 import java.security.SecureRandom; 11 import java.security.spec.AlgorithmParameterSpec; 12 import java.util.Hashtable; 13 14 import javax.crypto.SecretKey; 15 import javax.crypto.ShortBufferException; 16 import javax.crypto.spec.SecretKeySpec; 17 18 import org.bouncycastle.asn1.DERObjectIdentifier; 19 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; 20 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 21 import org.bouncycastle.asn1.x9.X9IntegerConverter; 22 import org.bouncycastle.crypto.BasicAgreement; 23 import org.bouncycastle.crypto.CipherParameters; 24 import org.bouncycastle.crypto.DerivationFunction; 25 import org.bouncycastle.crypto.agreement.ECDHBasicAgreement; 26 // BEGIN android-removed 27 // import org.bouncycastle.crypto.agreement.ECDHCBasicAgreement; 28 // import org.bouncycastle.crypto.agreement.ECMQVBasicAgreement; 29 // import org.bouncycastle.crypto.agreement.kdf.DHKDFParameters; 30 // import org.bouncycastle.crypto.agreement.kdf.ECDHKEKGenerator; 31 // END android-removed 32 import org.bouncycastle.crypto.digests.SHA1Digest; 33 import org.bouncycastle.crypto.params.ECDomainParameters; 34 import org.bouncycastle.crypto.params.ECPrivateKeyParameters; 35 import org.bouncycastle.crypto.params.ECPublicKeyParameters; 36 // BEGIN android-removed 37 // import org.bouncycastle.crypto.params.MQVPrivateParameters; 38 // import org.bouncycastle.crypto.params.MQVPublicParameters; 39 // END android-removed 40 import org.bouncycastle.jce.interfaces.ECPrivateKey; 41 import org.bouncycastle.jce.interfaces.ECPublicKey; 42 // BEGIN android-removed 43 // import org.bouncycastle.jce.interfaces.MQVPrivateKey; 44 // import org.bouncycastle.jce.interfaces.MQVPublicKey; 45 // END android-removed 46 import org.bouncycastle.util.Integers; 47 48 /** 49 * Diffie-Hellman key agreement using elliptic curve keys, ala IEEE P1363 50 * both the simple one, and the simple one with cofactors are supported. 51 * 52 * Also, MQV key agreement per SEC-1 53 */ 54 public class KeyAgreementSpi 55 extends javax.crypto.KeyAgreementSpi 56 { 57 private static final X9IntegerConverter converter = new X9IntegerConverter(); 58 private static final Hashtable algorithms = new Hashtable(); 59 60 static 61 { 62 Integer i128 = Integers.valueOf(128); 63 Integer i192 = Integers.valueOf(192); 64 Integer i256 = Integers.valueOf(256); 65 66 algorithms.put(NISTObjectIdentifiers.id_aes128_CBC.getId(), i128); 67 algorithms.put(NISTObjectIdentifiers.id_aes192_CBC.getId(), i192); 68 algorithms.put(NISTObjectIdentifiers.id_aes256_CBC.getId(), i256); 69 algorithms.put(NISTObjectIdentifiers.id_aes128_wrap.getId(), i128); 70 algorithms.put(NISTObjectIdentifiers.id_aes192_wrap.getId(), i192); 71 algorithms.put(NISTObjectIdentifiers.id_aes256_wrap.getId(), i256); 72 algorithms.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId(), i192); 73 } 74 75 private String kaAlgorithm; 76 private BigInteger result; 77 private ECDomainParameters parameters; 78 private BasicAgreement agreement; 79 // BEGIN android-removed 80 // private DerivationFunction kdf; 81 // END android-removed 82 83 private byte[] bigIntToBytes( 84 BigInteger r) 85 { 86 return converter.integerToBytes(r, converter.getByteLength(parameters.getG().getX())); 87 } 88 89 protected KeyAgreementSpi( 90 String kaAlgorithm, 91 BasicAgreement agreement, 92 DerivationFunction kdf) 93 { 94 this.kaAlgorithm = kaAlgorithm; 95 this.agreement = agreement; 96 // BEGIN android-removed 97 // this.kdf = kdf; 98 // END android-removed 99 } 100 101 protected Key engineDoPhase( 102 Key key, 103 boolean lastPhase) 104 throws InvalidKeyException, IllegalStateException 105 { 106 if (parameters == null) 107 { 108 throw new IllegalStateException(kaAlgorithm + " not initialised."); 109 } 110 111 if (!lastPhase) 112 { 113 throw new IllegalStateException(kaAlgorithm + " can only be between two parties."); 114 } 115 116 CipherParameters pubKey; 117 // BEGIN android-removed 118 // if (agreement instanceof ECMQVBasicAgreement) 119 // { 120 // if (!(key instanceof MQVPublicKey)) 121 // { 122 // throw new InvalidKeyException(kaAlgorithm + " key agreement requires " 123 // + getSimpleName(MQVPublicKey.class) + " for doPhase"); 124 // } 125 // 126 // MQVPublicKey mqvPubKey = (MQVPublicKey)key; 127 // ECPublicKeyParameters staticKey = (ECPublicKeyParameters) 128 // ECUtil.generatePublicKeyParameter(mqvPubKey.getStaticKey()); 129 // ECPublicKeyParameters ephemKey = (ECPublicKeyParameters) 130 // ECUtil.generatePublicKeyParameter(mqvPubKey.getEphemeralKey()); 131 // 132 // pubKey = new MQVPublicParameters(staticKey, ephemKey); 133 // 134 // // TODO Validate that all the keys are using the same parameters? 135 // } 136 // else 137 // END android-removed 138 { 139 if (!(key instanceof PublicKey)) 140 { 141 throw new InvalidKeyException(kaAlgorithm + " key agreement requires " 142 + getSimpleName(ECPublicKey.class) + " for doPhase"); 143 } 144 145 pubKey = ECUtil.generatePublicKeyParameter((PublicKey)key); 146 147 // TODO Validate that all the keys are using the same parameters? 148 } 149 150 result = agreement.calculateAgreement(pubKey); 151 152 return null; 153 } 154 155 protected byte[] engineGenerateSecret() 156 throws IllegalStateException 157 { 158 // BEGIN android-removed 159 // if (kdf != null) 160 // { 161 // throw new UnsupportedOperationException( 162 // "KDF can only be used when algorithm is known"); 163 // } 164 // END android-removed 165 166 return bigIntToBytes(result); 167 } 168 169 protected int engineGenerateSecret( 170 byte[] sharedSecret, 171 int offset) 172 throws IllegalStateException, ShortBufferException 173 { 174 byte[] secret = engineGenerateSecret(); 175 176 if (sharedSecret.length - offset < secret.length) 177 { 178 throw new ShortBufferException(kaAlgorithm + " key agreement: need " + secret.length + " bytes"); 179 } 180 181 System.arraycopy(secret, 0, sharedSecret, offset, secret.length); 182 183 return secret.length; 184 } 185 186 protected SecretKey engineGenerateSecret( 187 String algorithm) 188 throws NoSuchAlgorithmException 189 { 190 byte[] secret = bigIntToBytes(result); 191 192 // BEGIN android-removed 193 // if (kdf != null) 194 // { 195 // if (!algorithms.containsKey(algorithm)) 196 // { 197 // throw new NoSuchAlgorithmException("unknown algorithm encountered: " + algorithm); 198 // } 199 // 200 // int keySize = ((Integer)algorithms.get(algorithm)).intValue(); 201 // 202 // DHKDFParameters params = new DHKDFParameters(new DERObjectIdentifier(algorithm), keySize, secret); 203 // 204 // byte[] keyBytes = new byte[keySize / 8]; 205 // kdf.init(params); 206 // kdf.generateBytes(keyBytes, 0, keyBytes.length); 207 // secret = keyBytes; 208 // } 209 // else 210 // END android-removed 211 { 212 // TODO Should we be ensuring the key is the right length? 213 } 214 215 return new SecretKeySpec(secret, algorithm); 216 } 217 218 protected void engineInit( 219 Key key, 220 AlgorithmParameterSpec params, 221 SecureRandom random) 222 throws InvalidKeyException, InvalidAlgorithmParameterException 223 { 224 // BEGIN android-added 225 if (params != null) 226 { 227 throw new InvalidAlgorithmParameterException("No algorithm parameters supported"); 228 } 229 // END android-added 230 initFromKey(key); 231 } 232 233 protected void engineInit( 234 Key key, 235 SecureRandom random) 236 throws InvalidKeyException 237 { 238 initFromKey(key); 239 } 240 241 private void initFromKey(Key key) 242 throws InvalidKeyException 243 { 244 // BEGIN android-removed 245 // if (agreement instanceof ECMQVBasicAgreement) 246 // { 247 // if (!(key instanceof MQVPrivateKey)) 248 // { 249 // throw new InvalidKeyException(kaAlgorithm + " key agreement requires " 250 // + getSimpleName(MQVPrivateKey.class) + " for initialisation"); 251 // } 252 // 253 // MQVPrivateKey mqvPrivKey = (MQVPrivateKey)key; 254 // ECPrivateKeyParameters staticPrivKey = (ECPrivateKeyParameters) 255 // ECUtil.generatePrivateKeyParameter(mqvPrivKey.getStaticPrivateKey()); 256 // ECPrivateKeyParameters ephemPrivKey = (ECPrivateKeyParameters) 257 // ECUtil.generatePrivateKeyParameter(mqvPrivKey.getEphemeralPrivateKey()); 258 // 259 // ECPublicKeyParameters ephemPubKey = null; 260 // if (mqvPrivKey.getEphemeralPublicKey() != null) 261 // { 262 // ephemPubKey = (ECPublicKeyParameters) 263 // ECUtil.generatePublicKeyParameter(mqvPrivKey.getEphemeralPublicKey()); 264 // } 265 // 266 // MQVPrivateParameters localParams = new MQVPrivateParameters(staticPrivKey, ephemPrivKey, ephemPubKey); 267 // this.parameters = staticPrivKey.getParameters(); 268 // 269 // // TODO Validate that all the keys are using the same parameters? 270 // 271 // agreement.init(localParams); 272 // } 273 // else 274 // END android-removed 275 { 276 if (!(key instanceof PrivateKey)) 277 { 278 throw new InvalidKeyException(kaAlgorithm + " key agreement requires " 279 + getSimpleName(ECPrivateKey.class) + " for initialisation"); 280 } 281 282 ECPrivateKeyParameters privKey = (ECPrivateKeyParameters)ECUtil.generatePrivateKeyParameter((PrivateKey)key); 283 this.parameters = privKey.getParameters(); 284 285 agreement.init(privKey); 286 } 287 } 288 289 private static String getSimpleName(Class clazz) 290 { 291 String fullName = clazz.getName(); 292 293 return fullName.substring(fullName.lastIndexOf('.') + 1); 294 } 295 296 public static class DH 297 extends KeyAgreementSpi 298 { 299 public DH() 300 { 301 super("ECDH", new ECDHBasicAgreement(), null); 302 } 303 } 304 305 // BEGIN android-removed 306 // public static class DHC 307 // extends KeyAgreementSpi 308 // { 309 // public DHC() 310 // { 311 // super("ECDHC", new ECDHCBasicAgreement(), null); 312 // } 313 // } 314 // 315 // public static class MQV 316 // extends KeyAgreementSpi 317 // { 318 // public MQV() 319 // { 320 // super("ECMQV", new ECMQVBasicAgreement(), null); 321 // } 322 // } 323 // 324 // public static class DHwithSHA1KDF 325 // extends KeyAgreementSpi 326 // { 327 // public DHwithSHA1KDF() 328 // { 329 // super("ECDHwithSHA1KDF", new ECDHBasicAgreement(), new ECDHKEKGenerator(new SHA1Digest())); 330 // } 331 // } 332 // 333 // public static class MQVwithSHA1KDF 334 // extends KeyAgreementSpi 335 // { 336 // public MQVwithSHA1KDF() 337 // { 338 // super("ECMQVwithSHA1KDF", new ECMQVBasicAgreement(), new ECDHKEKGenerator(new SHA1Digest())); 339 // } 340 // } 341 // END android-removed 342 } 343