1 /* 2 * Copyright (C) 2008 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.security.InvalidKeyException; 20 import java.security.InvalidParameterException; 21 import java.security.NoSuchAlgorithmException; 22 import java.security.PrivateKey; 23 import java.security.PublicKey; 24 import java.security.Signature; 25 import java.security.SignatureException; 26 import java.security.interfaces.DSAPrivateKey; 27 import java.security.interfaces.DSAPublicKey; 28 import java.security.interfaces.ECPrivateKey; 29 import java.security.interfaces.ECPublicKey; 30 import java.security.interfaces.RSAPrivateCrtKey; 31 import java.security.interfaces.RSAPrivateKey; 32 import java.security.interfaces.RSAPublicKey; 33 34 /** 35 * Implements the subset of the JDK Signature interface needed for 36 * signature verification using OpenSSL. 37 */ 38 public class OpenSSLSignature extends Signature { 39 private static enum EngineType { 40 RSA, DSA, EC, 41 }; 42 43 /** 44 * Holds a pointer to the native message digest context. 45 */ 46 private long ctx; 47 48 /** 49 * The current OpenSSL key we're operating on. 50 */ 51 private OpenSSLKey key; 52 53 /** 54 * Holds the type of the Java algorithm. 55 */ 56 private final EngineType engineType; 57 58 /** 59 * Holds the OpenSSL name of the algorithm (lower case, no dashes). 60 */ 61 private final String evpAlgorithm; 62 63 /** 64 * Holds a dummy buffer for writing single bytes to the digest. 65 */ 66 private final byte[] singleByte = new byte[1]; 67 68 /** 69 * Creates a new OpenSSLSignature instance for the given algorithm name. 70 * 71 * @param algorithm OpenSSL name of the algorithm, e.g. "RSA-SHA1". 72 */ 73 private OpenSSLSignature(String algorithm, EngineType engineType) 74 throws NoSuchAlgorithmException { 75 super(algorithm); 76 77 // We don't support MD2 78 if ("RSA-MD2".equals(algorithm)) { 79 throw new NoSuchAlgorithmException(algorithm); 80 } 81 82 this.engineType = engineType; 83 this.evpAlgorithm = algorithm; 84 } 85 86 @Override 87 protected void engineUpdate(byte input) { 88 singleByte[0] = input; 89 engineUpdate(singleByte, 0, 1); 90 } 91 92 @Override 93 protected void engineUpdate(byte[] input, int offset, int len) { 94 if (state == SIGN) { 95 if (ctx == 0) { 96 try { 97 ctx = NativeCrypto.EVP_SignInit(evpAlgorithm); 98 } catch (Exception ex) { 99 throw new RuntimeException(ex); 100 } 101 } 102 103 NativeCrypto.EVP_SignUpdate(ctx, input, offset, len); 104 } else { 105 if (ctx == 0) { 106 try { 107 ctx = NativeCrypto.EVP_VerifyInit(evpAlgorithm); 108 } catch (Exception ex) { 109 throw new RuntimeException(ex); 110 } 111 } 112 113 NativeCrypto.EVP_VerifyUpdate(ctx, input, offset, len); 114 } 115 } 116 117 @Override 118 protected Object engineGetParameter(String param) throws InvalidParameterException { 119 return null; 120 } 121 122 private void checkEngineType(OpenSSLKey pkey) throws InvalidKeyException { 123 final int pkeyType = NativeCrypto.EVP_PKEY_type(pkey.getPkeyContext()); 124 125 switch (engineType) { 126 case RSA: 127 if (pkeyType != NativeCrypto.EVP_PKEY_RSA) { 128 throw new InvalidKeyException("Signature not initialized as RSA"); 129 } 130 break; 131 case DSA: 132 if (pkeyType != NativeCrypto.EVP_PKEY_DSA) { 133 throw new InvalidKeyException("Signature not initialized as DSA"); 134 } 135 break; 136 case EC: 137 if (pkeyType != NativeCrypto.EVP_PKEY_EC) { 138 throw new InvalidKeyException("Signature not initialized as EC"); 139 } 140 break; 141 default: 142 throw new InvalidKeyException("Need DSA or RSA or EC private key"); 143 } 144 } 145 146 @Override 147 protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { 148 destroyContextIfExists(); 149 150 if (privateKey instanceof OpenSSLKeyHolder) { 151 OpenSSLKey pkey = ((OpenSSLKeyHolder) privateKey).getOpenSSLKey(); 152 checkEngineType(pkey); 153 key = pkey; 154 } else if (privateKey instanceof RSAPrivateCrtKey) { 155 if (engineType != EngineType.RSA) { 156 throw new InvalidKeyException("Signature not initialized as RSA"); 157 } 158 159 RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) privateKey; 160 key = OpenSSLRSAPrivateCrtKey.getInstance(rsaPrivateKey); 161 } else if (privateKey instanceof RSAPrivateKey) { 162 if (engineType != EngineType.RSA) { 163 throw new InvalidKeyException("Signature not initialized as RSA"); 164 } 165 166 RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey; 167 key = OpenSSLRSAPrivateKey.getInstance(rsaPrivateKey); 168 } else if (privateKey instanceof DSAPrivateKey) { 169 if (engineType != EngineType.DSA) { 170 throw new InvalidKeyException("Signature not initialized as DSA"); 171 } 172 173 DSAPrivateKey dsaPrivateKey = (DSAPrivateKey) privateKey; 174 key = OpenSSLDSAPrivateKey.getInstance(dsaPrivateKey); 175 } else if (privateKey instanceof ECPrivateKey) { 176 if (engineType != EngineType.EC) { 177 throw new InvalidKeyException("Signature not initialized as EC"); 178 } 179 180 ECPrivateKey ecPrivateKey = (ECPrivateKey) privateKey; 181 key = OpenSSLECPrivateKey.getInstance(ecPrivateKey); 182 } else { 183 throw new InvalidKeyException("Need DSA or RSA or EC private key"); 184 } 185 } 186 187 @Override 188 protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { 189 // If we had an existing context, destroy it first. 190 destroyContextIfExists(); 191 192 if (publicKey instanceof OpenSSLKeyHolder) { 193 OpenSSLKey pkey = ((OpenSSLKeyHolder) publicKey).getOpenSSLKey(); 194 checkEngineType(pkey); 195 key = pkey; 196 } else if (publicKey instanceof RSAPublicKey) { 197 if (engineType != EngineType.RSA) { 198 throw new InvalidKeyException("Signature not initialized as RSA"); 199 } 200 201 RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey; 202 key = OpenSSLRSAPublicKey.getInstance(rsaPublicKey); 203 } else if (publicKey instanceof DSAPublicKey) { 204 if (engineType != EngineType.DSA) { 205 throw new InvalidKeyException("Signature not initialized as DSA"); 206 } 207 208 DSAPublicKey dsaPublicKey = (DSAPublicKey) publicKey; 209 key = OpenSSLDSAPublicKey.getInstance(dsaPublicKey); 210 } else if (publicKey instanceof ECPublicKey) { 211 if (engineType != EngineType.EC) { 212 throw new InvalidKeyException("Signature not initialized as EC"); 213 } 214 215 ECPublicKey ecPublicKey = (ECPublicKey) publicKey; 216 key = OpenSSLECPublicKey.getInstance(ecPublicKey); 217 } else { 218 throw new InvalidKeyException("Need DSA or RSA or EC public key"); 219 } 220 } 221 222 @Override 223 protected void engineSetParameter(String param, Object value) throws InvalidParameterException { 224 } 225 226 @Override 227 protected byte[] engineSign() throws SignatureException { 228 if (key == null) { 229 // This can't actually happen, but you never know... 230 throw new SignatureException("Need DSA or RSA or EC private key"); 231 } 232 233 try { 234 byte[] buffer = new byte[NativeCrypto.EVP_PKEY_size(key.getPkeyContext())]; 235 int bytesWritten = NativeCrypto.EVP_SignFinal(ctx, buffer, 0, key.getPkeyContext()); 236 237 byte[] signature = new byte[bytesWritten]; 238 System.arraycopy(buffer, 0, signature, 0, bytesWritten); 239 240 return signature; 241 } catch (Exception ex) { 242 throw new SignatureException(ex); 243 } finally { 244 /* 245 * Java expects the digest context to be reset completely after sign 246 * calls. 247 */ 248 destroyContextIfExists(); 249 } 250 } 251 252 @Override 253 protected boolean engineVerify(byte[] sigBytes) throws SignatureException { 254 if (key == null) { 255 // This can't actually happen, but you never know... 256 throw new SignatureException("Need DSA or RSA public key"); 257 } 258 259 try { 260 int result = NativeCrypto.EVP_VerifyFinal(ctx, sigBytes, 0, sigBytes.length, 261 key.getPkeyContext()); 262 return result == 1; 263 } catch (Exception ex) { 264 return false; 265 } finally { 266 /* 267 * Java expects the digest context to be reset completely after 268 * verify calls. 269 */ 270 destroyContextIfExists(); 271 } 272 } 273 274 private void destroyContextIfExists() { 275 if (ctx != 0) { 276 NativeCrypto.EVP_MD_CTX_destroy(ctx); 277 ctx = 0; 278 } 279 } 280 281 @Override 282 protected void finalize() throws Throwable { 283 try { 284 if (ctx != 0) { 285 NativeCrypto.EVP_MD_CTX_destroy(ctx); 286 } 287 } finally { 288 super.finalize(); 289 } 290 } 291 292 public static final class MD5RSA extends OpenSSLSignature { 293 public MD5RSA() throws NoSuchAlgorithmException { 294 super("RSA-MD5", EngineType.RSA); 295 } 296 } 297 public static final class SHA1RSA extends OpenSSLSignature { 298 public SHA1RSA() throws NoSuchAlgorithmException { 299 super("RSA-SHA1", EngineType.RSA); 300 } 301 } 302 public static final class SHA256RSA extends OpenSSLSignature { 303 public SHA256RSA() throws NoSuchAlgorithmException { 304 super("RSA-SHA256", EngineType.RSA); 305 } 306 } 307 public static final class SHA384RSA extends OpenSSLSignature { 308 public SHA384RSA() throws NoSuchAlgorithmException { 309 super("RSA-SHA384", EngineType.RSA); 310 } 311 } 312 public static final class SHA512RSA extends OpenSSLSignature { 313 public SHA512RSA() throws NoSuchAlgorithmException { 314 super("RSA-SHA512", EngineType.RSA); 315 } 316 } 317 public static final class SHA1DSA extends OpenSSLSignature { 318 public SHA1DSA() throws NoSuchAlgorithmException { 319 super("DSA-SHA1", EngineType.DSA); 320 } 321 } 322 public static final class SHA1ECDSA extends OpenSSLSignature { 323 public SHA1ECDSA() throws NoSuchAlgorithmException { 324 super("SHA1", EngineType.EC); 325 } 326 } 327 public static final class SHA256ECDSA extends OpenSSLSignature { 328 public SHA256ECDSA() throws NoSuchAlgorithmException { 329 super("SHA256", EngineType.EC); 330 } 331 } 332 public static final class SHA384ECDSA extends OpenSSLSignature { 333 public SHA384ECDSA() throws NoSuchAlgorithmException { 334 super("SHA384", EngineType.EC); 335 } 336 } 337 public static final class SHA512ECDSA extends OpenSSLSignature { 338 public SHA512ECDSA() throws NoSuchAlgorithmException { 339 super("SHA512", EngineType.EC); 340 } 341 } 342 } 343 344