1 /* 2 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 27 package sun.security.ssl; 28 29 import java.security.*; 30 31 /** 32 * Signature implementation for the SSL/TLS RSA Signature variant with both 33 * MD5 and SHA-1 MessageDigests. Used for explicit RSA server authentication 34 * (RSA signed server key exchange for RSA_EXPORT and DHE_RSA) and RSA client 35 * authentication (RSA signed certificate verify message). 36 * 37 * It conforms to the standard JCA Signature API. It is registered in the 38 * SunJSSE provider to avoid more complicated getInstance() code and 39 * negative interaction with the JCA mechanisms for hardware providers. 40 * 41 * The class should be instantiated via the getInstance() method in this class, 42 * which returns the implementation from the preferred provider. The internal 43 * implementation allows the hashes to be explicitly set, which is required 44 * for RSA client authentication. It can be obtained via the 45 * getInternalInstance() method. 46 * 47 * This class is not thread safe. 48 * 49 */ 50 public final class RSASignature extends SignatureSpi { 51 52 private final Signature rawRsa; 53 private MessageDigest md5, sha; 54 55 // flag indicating if the MessageDigests are in reset state 56 private boolean isReset; 57 58 public RSASignature() throws NoSuchAlgorithmException { 59 super(); 60 rawRsa = JsseJce.getSignature(JsseJce.SIGNATURE_RAWRSA); 61 isReset = true; 62 } 63 64 /** 65 * Get an implementation for the RSA signature. Follows the standard 66 * JCA getInstance() model, so it return the implementation from the 67 * provider with the highest precedence, which may be this class. 68 */ 69 static Signature getInstance() throws NoSuchAlgorithmException { 70 return JsseJce.getSignature(JsseJce.SIGNATURE_SSLRSA); 71 } 72 73 /** 74 * Get an internal implementation for the RSA signature. Used for RSA 75 * client authentication, which needs the ability to set the digests 76 * to externally provided values via the setHashes() method. 77 */ 78 static Signature getInternalInstance() 79 throws NoSuchAlgorithmException, NoSuchProviderException { 80 return Signature.getInstance(JsseJce.SIGNATURE_SSLRSA, "SunJSSE"); 81 } 82 83 /** 84 * Set the MD5 and SHA hashes to the provided objects. 85 */ 86 @SuppressWarnings("deprecation") 87 static void setHashes(Signature sig, MessageDigest md5, MessageDigest sha) { 88 sig.setParameter("hashes", new MessageDigest[] {md5, sha}); 89 } 90 91 /** 92 * Reset the MessageDigests unless they are already reset. 93 */ 94 private void reset() { 95 if (isReset == false) { 96 md5.reset(); 97 sha.reset(); 98 isReset = true; 99 } 100 } 101 102 private static void checkNull(Key key) throws InvalidKeyException { 103 if (key == null) { 104 throw new InvalidKeyException("Key must not be null"); 105 } 106 } 107 108 @Override 109 protected void engineInitVerify(PublicKey publicKey) 110 throws InvalidKeyException { 111 checkNull(publicKey); 112 reset(); 113 rawRsa.initVerify(publicKey); 114 } 115 116 @Override 117 protected void engineInitSign(PrivateKey privateKey) 118 throws InvalidKeyException { 119 engineInitSign(privateKey, null); 120 } 121 122 @Override 123 protected void engineInitSign(PrivateKey privateKey, SecureRandom random) 124 throws InvalidKeyException { 125 checkNull(privateKey); 126 reset(); 127 rawRsa.initSign(privateKey, random); 128 } 129 130 // lazily initialize the MessageDigests 131 private void initDigests() { 132 if (md5 == null) { 133 md5 = JsseJce.getMD5(); 134 sha = JsseJce.getSHA(); 135 } 136 } 137 138 @Override 139 protected void engineUpdate(byte b) { 140 initDigests(); 141 isReset = false; 142 md5.update(b); 143 sha.update(b); 144 } 145 146 @Override 147 protected void engineUpdate(byte[] b, int off, int len) { 148 initDigests(); 149 isReset = false; 150 md5.update(b, off, len); 151 sha.update(b, off, len); 152 } 153 154 private byte[] getDigest() throws SignatureException { 155 try { 156 initDigests(); 157 byte[] data = new byte[36]; 158 md5.digest(data, 0, 16); 159 sha.digest(data, 16, 20); 160 isReset = true; 161 return data; 162 } catch (DigestException e) { 163 // should never occur 164 throw new SignatureException(e); 165 } 166 } 167 168 @Override 169 protected byte[] engineSign() throws SignatureException { 170 rawRsa.update(getDigest()); 171 return rawRsa.sign(); 172 } 173 174 @Override 175 protected boolean engineVerify(byte[] sigBytes) throws SignatureException { 176 return engineVerify(sigBytes, 0, sigBytes.length); 177 } 178 179 @Override 180 protected boolean engineVerify(byte[] sigBytes, int offset, int length) 181 throws SignatureException { 182 rawRsa.update(getDigest()); 183 return rawRsa.verify(sigBytes, offset, length); 184 } 185 186 @Override 187 @SuppressWarnings("deprecation") 188 protected void engineSetParameter(String param, Object value) 189 throws InvalidParameterException { 190 if (param.equals("hashes") == false) { 191 throw new InvalidParameterException 192 ("Parameter not supported: " + param); 193 } 194 if (value instanceof MessageDigest[] == false) { 195 throw new InvalidParameterException 196 ("value must be MessageDigest[]"); 197 } 198 MessageDigest[] digests = (MessageDigest[])value; 199 md5 = digests[0]; 200 sha = digests[1]; 201 } 202 203 @Override 204 @SuppressWarnings("deprecation") 205 protected Object engineGetParameter(String param) 206 throws InvalidParameterException { 207 throw new InvalidParameterException("Parameters not supported"); 208 } 209 210 }