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