1 /* 2 * Copyright (C) 2012 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.apache.harmony.xnet.provider.jsse; 18 19 import java.io.IOException; 20 import java.io.NotSerializableException; 21 import java.io.ObjectInputStream; 22 import java.io.ObjectOutputStream; 23 import java.math.BigInteger; 24 import java.security.InvalidKeyException; 25 import java.security.interfaces.DSAParams; 26 import java.security.interfaces.DSAPrivateKey; 27 import java.security.spec.DSAPrivateKeySpec; 28 import java.security.spec.InvalidKeySpecException; 29 30 public class OpenSSLDSAPrivateKey implements DSAPrivateKey, OpenSSLKeyHolder { 31 private static final long serialVersionUID = 6524734576187424628L; 32 33 private transient OpenSSLKey key; 34 35 private transient OpenSSLDSAParams params; 36 37 OpenSSLDSAPrivateKey(OpenSSLKey key) { 38 this.key = key; 39 } 40 41 @Override 42 public OpenSSLKey getOpenSSLKey() { 43 return key; 44 } 45 46 OpenSSLDSAPrivateKey(DSAPrivateKeySpec dsaKeySpec) throws InvalidKeySpecException { 47 try { 48 key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DSA( 49 dsaKeySpec.getP().toByteArray(), 50 dsaKeySpec.getQ().toByteArray(), 51 dsaKeySpec.getG().toByteArray(), 52 null, 53 dsaKeySpec.getX().toByteArray())); 54 } catch (Exception e) { 55 throw new InvalidKeySpecException(e); 56 } 57 } 58 59 private void ensureReadParams() { 60 if (params == null) { 61 params = new OpenSSLDSAParams(key); 62 } 63 } 64 65 static OpenSSLKey getInstance(DSAPrivateKey dsaPrivateKey) throws InvalidKeyException { 66 try { 67 DSAParams dsaParams = dsaPrivateKey.getParams(); 68 return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DSA( 69 dsaParams.getP().toByteArray(), 70 dsaParams.getQ().toByteArray(), 71 dsaParams.getG().toByteArray(), 72 null, 73 dsaPrivateKey.getX().toByteArray())); 74 } catch (Exception e) { 75 throw new InvalidKeyException(e); 76 } 77 } 78 79 @Override 80 public DSAParams getParams() { 81 ensureReadParams(); 82 return params; 83 } 84 85 @Override 86 public String getAlgorithm() { 87 return "DSA"; 88 } 89 90 @Override 91 public String getFormat() { 92 /* 93 * If we're using an OpenSSL ENGINE, there's no guarantee we can export 94 * the key. Returning {@code null} tells the caller that there's no 95 * encoded format. 96 */ 97 if (key.isEngineBased()) { 98 return null; 99 } 100 101 return "PKCS#8"; 102 } 103 104 @Override 105 public byte[] getEncoded() { 106 /* 107 * If we're using an OpenSSL ENGINE, there's no guarantee we can export 108 * the key. Returning {@code null} tells the caller that there's no 109 * encoded format. 110 */ 111 if (key.isEngineBased()) { 112 return null; 113 } 114 115 return NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getPkeyContext()); 116 } 117 118 @Override 119 public BigInteger getX() { 120 if (key.isEngineBased()) { 121 throw new UnsupportedOperationException("private key value X cannot be extracted"); 122 } 123 124 ensureReadParams(); 125 return params.getX(); 126 } 127 128 @Override 129 public boolean equals(Object o) { 130 if (o == this) { 131 return true; 132 } 133 134 if (o instanceof OpenSSLDSAPrivateKey) { 135 OpenSSLDSAPrivateKey other = (OpenSSLDSAPrivateKey) o; 136 137 /* 138 * We can shortcut the true case, but it still may be equivalent but 139 * different copies. 140 */ 141 if (key.equals(other.getOpenSSLKey())) { 142 return true; 143 } 144 } 145 146 if (!(o instanceof DSAPrivateKey)) { 147 return false; 148 } 149 150 ensureReadParams(); 151 152 final BigInteger x = params.getX(); 153 if (x == null) { 154 /* 155 * If our X is null, we can't tell if these two private keys are 156 * equivalent. This usually happens if this key is ENGINE-based. If 157 * the other key was ENGINE-based, we should have caught it in the 158 * OpenSSLDSAPrivateKey case. 159 */ 160 return false; 161 } 162 163 final DSAPrivateKey other = (DSAPrivateKey) o; 164 return x.equals(other.getX()) && params.equals(other.getParams()); 165 } 166 167 @Override 168 public int hashCode() { 169 ensureReadParams(); 170 171 int hash = 1; 172 173 final BigInteger x = getX(); 174 if (x != null) { 175 hash = hash * 3 + x.hashCode(); 176 } 177 178 hash = hash * 7 + params.hashCode(); 179 180 return hash; 181 } 182 183 @Override 184 public String toString() { 185 final StringBuilder sb = new StringBuilder("OpenSSLDSAPrivateKey{"); 186 187 if (key.isEngineBased()) { 188 sb.append("key="); 189 sb.append(key); 190 sb.append('}'); 191 return sb.toString(); 192 } 193 194 ensureReadParams(); 195 sb.append("X="); 196 sb.append(params.getX().toString(16)); 197 sb.append(','); 198 sb.append("params="); 199 sb.append(params.toString()); 200 sb.append('}'); 201 202 return sb.toString(); 203 } 204 205 private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { 206 stream.defaultReadObject(); 207 208 final BigInteger g = (BigInteger) stream.readObject(); 209 final BigInteger p = (BigInteger) stream.readObject(); 210 final BigInteger q = (BigInteger) stream.readObject(); 211 final BigInteger x = (BigInteger) stream.readObject(); 212 213 key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DSA( 214 p.toByteArray(), 215 q.toByteArray(), 216 g.toByteArray(), 217 null, 218 x.toByteArray())); 219 } 220 221 private void writeObject(ObjectOutputStream stream) throws IOException { 222 if (getOpenSSLKey().isEngineBased()) { 223 throw new NotSerializableException("engine-based keys can not be serialized"); 224 } 225 226 stream.defaultWriteObject(); 227 228 ensureReadParams(); 229 stream.writeObject(params.getG()); 230 stream.writeObject(params.getP()); 231 stream.writeObject(params.getQ()); 232 stream.writeObject(params.getX()); 233 } 234 } 235