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 static void setHashes(Signature sig, MessageDigest md5, MessageDigest sha) { 87 sig.setParameter("hashes", new MessageDigest[] {md5, sha}); 88 } 89 90 /** 91 * Reset the MessageDigests unless they are already reset. 92 */ 93 private void reset() { 94 if (isReset == false) { 95 md5.reset(); 96 sha.reset(); 97 isReset = true; 98 } 99 } 100 101 private static void checkNull(Key key) throws InvalidKeyException { 102 if (key == null) { 103 throw new InvalidKeyException("Key must not be null"); 104 } 105 } 106 107 @Override 108 protected void engineInitVerify(PublicKey publicKey) 109 throws InvalidKeyException { 110 checkNull(publicKey); 111 reset(); 112 rawRsa.initVerify(publicKey); 113 } 114 115 @Override 116 protected void engineInitSign(PrivateKey privateKey) 117 throws InvalidKeyException { 118 engineInitSign(privateKey, null); 119 } 120 121 @Override 122 protected void engineInitSign(PrivateKey privateKey, SecureRandom random) 123 throws InvalidKeyException { 124 checkNull(privateKey); 125 reset(); 126 rawRsa.initSign(privateKey, random); 127 } 128 129 // lazily initialize the MessageDigests 130 private void initDigests() { 131 if (md5 == null) { 132 md5 = JsseJce.getMD5(); 133 sha = JsseJce.getSHA(); 134 } 135 } 136 137 @Override 138 protected void engineUpdate(byte b) { 139 initDigests(); 140 isReset = false; 141 md5.update(b); 142 sha.update(b); 143 } 144 145 @Override 146 protected void engineUpdate(byte[] b, int off, int len) { 147 initDigests(); 148 isReset = false; 149 md5.update(b, off, len); 150 sha.update(b, off, len); 151 } 152 153 private byte[] getDigest() throws SignatureException { 154 try { 155 initDigests(); 156 byte[] data = new byte[36]; 157 md5.digest(data, 0, 16); 158 sha.digest(data, 16, 20); 159 isReset = true; 160 return data; 161 } catch (DigestException e) { 162 // should never occur 163 throw new SignatureException(e); 164 } 165 } 166 167 @Override 168 protected byte[] engineSign() throws SignatureException { 169 rawRsa.update(getDigest()); 170 return rawRsa.sign(); 171 } 172 173 @Override 174 protected boolean engineVerify(byte[] sigBytes) throws SignatureException { 175 return engineVerify(sigBytes, 0, sigBytes.length); 176 } 177 178 @Override 179 protected boolean engineVerify(byte[] sigBytes, int offset, int length) 180 throws SignatureException { 181 rawRsa.update(getDigest()); 182 return rawRsa.verify(sigBytes, offset, length); 183 } 184 185 @Override 186 protected void engineSetParameter(String param, Object value) 187 throws InvalidParameterException { 188 if (param.equals("hashes") == false) { 189 throw new InvalidParameterException 190 ("Parameter not supported: " + param); 191 } 192 if (value instanceof MessageDigest[] == false) { 193 throw new InvalidParameterException 194 ("value must be MessageDigest[]"); 195 } 196 MessageDigest[] digests = (MessageDigest[])value; 197 md5 = digests[0]; 198 sha = digests[1]; 199 } 200 201 @Override 202 protected Object engineGetParameter(String param) 203 throws InvalidParameterException { 204 throw new InvalidParameterException("Parameters not supported"); 205 } 206 207 }