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 17package org.conscrypt; 18 19import java.security.InvalidKeyException; 20import java.security.InvalidParameterException; 21import java.security.NoSuchAlgorithmException; 22import java.security.PrivateKey; 23import java.security.PublicKey; 24import java.security.SignatureException; 25import java.security.SignatureSpi; 26 27/** 28 * Implements the subset of the JDK Signature interface needed for 29 * signature verification using OpenSSL. 30 */ 31public class OpenSSLSignature extends SignatureSpi { 32 private static enum EngineType { 33 RSA, DSA, EC, 34 }; 35 36 private OpenSSLDigestContext ctx; 37 38 /** 39 * The current OpenSSL key we're operating on. 40 */ 41 private OpenSSLKey key; 42 43 /** 44 * Holds the type of the Java algorithm. 45 */ 46 private final EngineType engineType; 47 48 /** 49 * Holds the OpenSSL name of the algorithm (lower case, no dashes). 50 */ 51 private final long evpAlgorithm; 52 53 /** 54 * Holds a dummy buffer for writing single bytes to the digest. 55 */ 56 private final byte[] singleByte = new byte[1]; 57 58 /** 59 * True when engine is initialized to signing. 60 */ 61 private boolean signing; 62 63 /** 64 * Creates a new OpenSSLSignature instance for the given algorithm name. 65 * 66 * @param algorithm OpenSSL name of the algorithm, e.g. "RSA-SHA1". 67 */ 68 private OpenSSLSignature(long algorithm, EngineType engineType) 69 throws NoSuchAlgorithmException { 70 this.engineType = engineType; 71 this.evpAlgorithm = algorithm; 72 } 73 74 private final void resetContext() { 75 OpenSSLDigestContext ctxLocal = new OpenSSLDigestContext(NativeCrypto.EVP_MD_CTX_create()); 76 NativeCrypto.EVP_MD_CTX_init(ctxLocal); 77 if (signing) { 78 enableDSASignatureNonceHardeningIfApplicable(); 79 NativeCrypto.EVP_SignInit(ctxLocal, evpAlgorithm); 80 } else { 81 NativeCrypto.EVP_VerifyInit(ctxLocal, evpAlgorithm); 82 } 83 this.ctx = ctxLocal; 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 final OpenSSLDigestContext ctxLocal = ctx; 95 if (signing) { 96 NativeCrypto.EVP_SignUpdate(ctxLocal, input, offset, len); 97 } else { 98 NativeCrypto.EVP_VerifyUpdate(ctxLocal, input, offset, len); 99 } 100 } 101 102 @Override 103 protected Object engineGetParameter(String param) throws InvalidParameterException { 104 return null; 105 } 106 107 private void checkEngineType(OpenSSLKey pkey) throws InvalidKeyException { 108 final int pkeyType = NativeCrypto.EVP_PKEY_type(pkey.getPkeyContext()); 109 110 switch (engineType) { 111 case RSA: 112 if (pkeyType != NativeCrypto.EVP_PKEY_RSA) { 113 throw new InvalidKeyException("Signature initialized as " + engineType 114 + " (not RSA)"); 115 } 116 break; 117 case DSA: 118 if (pkeyType != NativeCrypto.EVP_PKEY_DSA) { 119 throw new InvalidKeyException("Signature initialized as " + engineType 120 + " (not DSA)"); 121 } 122 break; 123 case EC: 124 if (pkeyType != NativeCrypto.EVP_PKEY_EC) { 125 throw new InvalidKeyException("Signature initialized as " + engineType 126 + " (not EC)"); 127 } 128 break; 129 default: 130 throw new InvalidKeyException("Key must be of type " + engineType); 131 } 132 } 133 134 private void initInternal(OpenSSLKey newKey, boolean signing) throws InvalidKeyException { 135 checkEngineType(newKey); 136 key = newKey; 137 138 this.signing = signing; 139 resetContext(); 140 } 141 142 @Override 143 protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { 144 initInternal(OpenSSLKey.fromPrivateKey(privateKey), true); 145 } 146 147 /** 148 * Enables a mitigation against private key leakage through DSA and ECDSA signatures when weak 149 * nonces (per-message k values) are used. To mitigate the issue, private key and message being 150 * signed is mixed into the randomly generated nonce (k). 151 * 152 * <p>Does nothing for signatures that are neither DSA nor ECDSA. 153 */ 154 private void enableDSASignatureNonceHardeningIfApplicable() { 155 final OpenSSLKey key = this.key; 156 switch (engineType) { 157 case DSA: 158 NativeCrypto.set_DSA_flag_nonce_from_hash(key.getPkeyContext()); 159 break; 160 case EC: 161 NativeCrypto.EC_KEY_set_nonce_from_hash(key.getPkeyContext(), true); 162 break; 163 default: 164 // Hardening not applicable 165 } 166 } 167 168 @Override 169 protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { 170 initInternal(OpenSSLKey.fromPublicKey(publicKey), false); 171 } 172 173 @Override 174 protected void engineSetParameter(String param, Object value) throws InvalidParameterException { 175 } 176 177 @Override 178 protected byte[] engineSign() throws SignatureException { 179 if (key == null) { 180 // This can't actually happen, but you never know... 181 throw new SignatureException("Need DSA or RSA or EC private key"); 182 } 183 184 final OpenSSLDigestContext ctxLocal = ctx; 185 try { 186 byte[] buffer = new byte[NativeCrypto.EVP_PKEY_size(key.getPkeyContext())]; 187 int bytesWritten = NativeCrypto.EVP_SignFinal(ctxLocal, buffer, 0, 188 key.getPkeyContext()); 189 190 byte[] signature = new byte[bytesWritten]; 191 System.arraycopy(buffer, 0, signature, 0, bytesWritten); 192 193 return signature; 194 } catch (Exception ex) { 195 throw new SignatureException(ex); 196 } finally { 197 /* 198 * Java expects the digest context to be reset completely after sign 199 * calls. 200 */ 201 resetContext(); 202 } 203 } 204 205 @Override 206 protected boolean engineVerify(byte[] sigBytes) throws SignatureException { 207 if (key == null) { 208 // This can't actually happen, but you never know... 209 throw new SignatureException("Need DSA or RSA public key"); 210 } 211 212 try { 213 int result = NativeCrypto.EVP_VerifyFinal(ctx, sigBytes, 0, sigBytes.length, 214 key.getPkeyContext()); 215 return result == 1; 216 } catch (Exception ex) { 217 return false; 218 } finally { 219 /* 220 * Java expects the digest context to be reset completely after 221 * verify calls. 222 */ 223 resetContext(); 224 } 225 } 226 227 public static final class MD5RSA extends OpenSSLSignature { 228 private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("RSA-MD5"); 229 public MD5RSA() throws NoSuchAlgorithmException { 230 super(EVP_MD, EngineType.RSA); 231 } 232 } 233 public static final class SHA1RSA extends OpenSSLSignature { 234 private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("RSA-SHA1"); 235 public SHA1RSA() throws NoSuchAlgorithmException { 236 super(EVP_MD, EngineType.RSA); 237 } 238 } 239 public static final class SHA224RSA extends OpenSSLSignature { 240 private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("RSA-SHA224"); 241 public SHA224RSA() throws NoSuchAlgorithmException { 242 super(EVP_MD, EngineType.RSA); 243 } 244 } 245 public static final class SHA256RSA extends OpenSSLSignature { 246 private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("RSA-SHA256"); 247 public SHA256RSA() throws NoSuchAlgorithmException { 248 super(EVP_MD, EngineType.RSA); 249 } 250 } 251 public static final class SHA384RSA extends OpenSSLSignature { 252 private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("RSA-SHA384"); 253 public SHA384RSA() throws NoSuchAlgorithmException { 254 super(EVP_MD, EngineType.RSA); 255 } 256 } 257 public static final class SHA512RSA extends OpenSSLSignature { 258 private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("RSA-SHA512"); 259 public SHA512RSA() throws NoSuchAlgorithmException { 260 super(EVP_MD, EngineType.RSA); 261 } 262 } 263 public static final class SHA1DSA extends OpenSSLSignature { 264 private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("DSA-SHA1"); 265 public SHA1DSA() throws NoSuchAlgorithmException { 266 super(EVP_MD, EngineType.DSA); 267 } 268 } 269 public static final class SHA1ECDSA extends OpenSSLSignature { 270 private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA1"); 271 public SHA1ECDSA() throws NoSuchAlgorithmException { 272 super(EVP_MD, EngineType.EC); 273 } 274 } 275 public static final class SHA224ECDSA extends OpenSSLSignature { 276 private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA224"); 277 public SHA224ECDSA() throws NoSuchAlgorithmException { 278 super(EVP_MD, EngineType.EC); 279 } 280 } 281 public static final class SHA256ECDSA extends OpenSSLSignature { 282 private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA256"); 283 public SHA256ECDSA() throws NoSuchAlgorithmException { 284 super(EVP_MD, EngineType.EC); 285 } 286 } 287 public static final class SHA384ECDSA extends OpenSSLSignature { 288 private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA384"); 289 public SHA384ECDSA() throws NoSuchAlgorithmException { 290 super(EVP_MD, EngineType.EC); 291 } 292 } 293 public static final class SHA512ECDSA extends OpenSSLSignature { 294 private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA512"); 295 public SHA512ECDSA() throws NoSuchAlgorithmException { 296 super(EVP_MD, EngineType.EC); 297 } 298 } 299} 300 301