1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.conscrypt; 18 19 import java.io.IOException; 20 import java.io.ObjectInputStream; 21 import java.io.ObjectOutputStream; 22 import java.math.BigInteger; 23 import java.security.InvalidKeyException; 24 import java.security.spec.InvalidKeySpecException; 25 import javax.crypto.interfaces.DHPublicKey; 26 import javax.crypto.spec.DHParameterSpec; 27 import javax.crypto.spec.DHPublicKeySpec; 28 29 public class OpenSSLDHPublicKey implements DHPublicKey, OpenSSLKeyHolder { 30 private static final long serialVersionUID = 6123717708079837723L; 31 32 private transient OpenSSLKey key; 33 34 /** base prime */ 35 private transient byte[] p; 36 37 /** generator */ 38 private transient byte[] g; 39 40 /** public key */ 41 private transient byte[] y; 42 43 private transient final Object mParamsLock = new Object(); 44 45 private transient boolean readParams; 46 47 OpenSSLDHPublicKey(OpenSSLKey key) { 48 this.key = key; 49 } 50 51 @Override 52 public OpenSSLKey getOpenSSLKey() { 53 return key; 54 } 55 56 OpenSSLDHPublicKey(DHPublicKeySpec dsaKeySpec) throws InvalidKeySpecException { 57 try { 58 key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DH( 59 dsaKeySpec.getP().toByteArray(), 60 dsaKeySpec.getG().toByteArray(), 61 dsaKeySpec.getY().toByteArray(), 62 null)); 63 } catch (Exception e) { 64 throw new InvalidKeySpecException(e); 65 } 66 } 67 68 private void ensureReadParams() { 69 synchronized (mParamsLock) { 70 if (readParams) { 71 return; 72 } 73 74 byte[][] params = NativeCrypto.get_DH_params(key.getPkeyContext()); 75 76 p = params[0]; 77 g = params[1]; 78 y = params[2]; 79 80 readParams = true; 81 } 82 } 83 84 static OpenSSLKey getInstance(DHPublicKey DHPublicKey) throws InvalidKeyException { 85 try { 86 final DHParameterSpec dhParams = DHPublicKey.getParams(); 87 return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DH( 88 dhParams.getP().toByteArray(), 89 dhParams.getG().toByteArray(), 90 DHPublicKey.getY().toByteArray(), 91 null)); 92 } catch (Exception e) { 93 throw new InvalidKeyException(e); 94 } 95 } 96 97 @Override 98 public DHParameterSpec getParams() { 99 ensureReadParams(); 100 return new DHParameterSpec(new BigInteger(p), new BigInteger(g)); 101 } 102 103 @Override 104 public String getAlgorithm() { 105 return "DH"; 106 } 107 108 @Override 109 public String getFormat() { 110 return "X.509"; 111 } 112 113 @Override 114 public byte[] getEncoded() { 115 return NativeCrypto.i2d_PUBKEY(key.getPkeyContext()); 116 } 117 118 @Override 119 public BigInteger getY() { 120 ensureReadParams(); 121 return new BigInteger(y); 122 } 123 124 @Override 125 public boolean equals(Object o) { 126 if (o == this) { 127 return true; 128 } 129 130 if (o instanceof OpenSSLDHPublicKey) { 131 OpenSSLDHPublicKey other = (OpenSSLDHPublicKey) o; 132 133 /* 134 * We can shortcut the true case, but it still may be equivalent but 135 * different copies. 136 */ 137 if (key.equals(other.getOpenSSLKey())) { 138 return true; 139 } 140 } 141 142 if (!(o instanceof DHPublicKey)) { 143 return false; 144 } 145 146 ensureReadParams(); 147 148 final DHPublicKey other = (DHPublicKey) o; 149 if (!y.equals(other.getY())) { 150 return false; 151 } 152 153 DHParameterSpec spec = other.getParams(); 154 return g.equals(spec.getG()) && p.equals(spec.getP()); 155 } 156 157 @Override 158 public int hashCode() { 159 ensureReadParams(); 160 int hash = 1; 161 hash = hash * 3 + y.hashCode(); 162 hash = hash * 7 + p.hashCode(); 163 hash = hash * 13 + g.hashCode(); 164 return hash; 165 } 166 167 168 @Override 169 public String toString() { 170 ensureReadParams(); 171 172 final StringBuilder sb = new StringBuilder("OpenSSLDHPublicKey{"); 173 sb.append("Y="); 174 sb.append(new BigInteger(y).toString(16)); 175 sb.append(','); 176 sb.append("P="); 177 sb.append(new BigInteger(p).toString(16)); 178 sb.append(','); 179 sb.append("G="); 180 sb.append(new BigInteger(g).toString(16)); 181 sb.append('}'); 182 183 return sb.toString(); 184 } 185 186 private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { 187 stream.defaultReadObject(); 188 189 final BigInteger g = (BigInteger) stream.readObject(); 190 final BigInteger p = (BigInteger) stream.readObject(); 191 final BigInteger y = (BigInteger) stream.readObject(); 192 193 key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DH( 194 p.toByteArray(), 195 g.toByteArray(), 196 y.toByteArray(), 197 null)); 198 } 199 200 private void writeObject(ObjectOutputStream stream) throws IOException { 201 stream.defaultWriteObject(); 202 203 ensureReadParams(); 204 stream.writeObject(new BigInteger(g)); 205 stream.writeObject(new BigInteger(p)); 206 stream.writeObject(new BigInteger(y)); 207 } 208 } 209