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.conscrypt; 18 19 import java.security.InvalidKeyException; 20 import java.security.InvalidParameterException; 21 import java.security.PrivateKey; 22 import java.security.PublicKey; 23 import java.security.SignatureException; 24 import java.security.SignatureSpi; 25 import java.security.interfaces.RSAPrivateCrtKey; 26 import java.security.interfaces.RSAPrivateKey; 27 import java.security.interfaces.RSAPublicKey; 28 29 /** 30 * Implements the JDK Signature interface needed for RAW RSA signature 31 * generation and verification using OpenSSL. 32 */ 33 public class OpenSSLSignatureRawRSA extends SignatureSpi { 34 /** 35 * The current OpenSSL key we're operating on. 36 */ 37 private OpenSSLKey key; 38 39 /** 40 * Buffer to hold value to be signed or verified. 41 */ 42 private byte[] inputBuffer; 43 44 /** 45 * Current offset in input buffer. 46 */ 47 private int inputOffset; 48 49 /** 50 * Provides a flag to specify when the input is too long. 51 */ 52 private boolean inputIsTooLong; 53 54 @Override engineUpdate(byte input)55 protected void engineUpdate(byte input) { 56 final int oldOffset = inputOffset++; 57 58 if (inputOffset > inputBuffer.length) { 59 inputIsTooLong = true; 60 return; 61 } 62 63 inputBuffer[oldOffset] = input; 64 } 65 66 @Override engineUpdate(byte[] input, int offset, int len)67 protected void engineUpdate(byte[] input, int offset, int len) { 68 final int oldOffset = inputOffset; 69 inputOffset += len; 70 71 if (inputOffset > inputBuffer.length) { 72 inputIsTooLong = true; 73 return; 74 } 75 76 System.arraycopy(input, offset, inputBuffer, oldOffset, len); 77 } 78 79 @Override engineGetParameter(String param)80 protected Object engineGetParameter(String param) throws InvalidParameterException { 81 return null; 82 } 83 84 @Override engineInitSign(PrivateKey privateKey)85 protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { 86 if (privateKey instanceof OpenSSLRSAPrivateKey) { 87 OpenSSLRSAPrivateKey rsaPrivateKey = (OpenSSLRSAPrivateKey) privateKey; 88 key = rsaPrivateKey.getOpenSSLKey(); 89 } else if (privateKey instanceof RSAPrivateCrtKey) { 90 RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) privateKey; 91 key = OpenSSLRSAPrivateCrtKey.getInstance(rsaPrivateKey); 92 } else if (privateKey instanceof RSAPrivateKey) { 93 RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey; 94 key = OpenSSLRSAPrivateKey.getInstance(rsaPrivateKey); 95 } else { 96 throw new InvalidKeyException("Need RSA private key"); 97 } 98 99 // Allocate buffer according to RSA modulus size. 100 int maxSize = NativeCrypto.RSA_size(key.getNativeRef()); 101 inputBuffer = new byte[maxSize]; 102 inputOffset = 0; 103 } 104 105 @Override engineInitVerify(PublicKey publicKey)106 protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { 107 if (publicKey instanceof OpenSSLRSAPublicKey) { 108 OpenSSLRSAPublicKey rsaPublicKey = (OpenSSLRSAPublicKey) publicKey; 109 key = rsaPublicKey.getOpenSSLKey(); 110 } else if (publicKey instanceof RSAPublicKey) { 111 RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey; 112 key = OpenSSLRSAPublicKey.getInstance(rsaPublicKey); 113 } else { 114 throw new InvalidKeyException("Need RSA public key"); 115 } 116 117 // Allocate buffer according to RSA modulus size. 118 int maxSize = NativeCrypto.RSA_size(key.getNativeRef()); 119 inputBuffer = new byte[maxSize]; 120 inputOffset = 0; 121 } 122 123 @Override engineSetParameter(String param, Object value)124 protected void engineSetParameter(String param, Object value) throws InvalidParameterException { 125 } 126 127 @Override engineSign()128 protected byte[] engineSign() throws SignatureException { 129 if (key == null) { 130 // This can't actually happen, but you never know... 131 throw new SignatureException("Need RSA private key"); 132 } 133 134 if (inputIsTooLong) { 135 throw new SignatureException("input length " + inputOffset + " != " 136 + inputBuffer.length + " (modulus size)"); 137 } 138 139 byte[] outputBuffer = new byte[inputBuffer.length]; 140 try { 141 NativeCrypto.RSA_private_encrypt(inputOffset, inputBuffer, outputBuffer, 142 key.getNativeRef(), NativeConstants.RSA_PKCS1_PADDING); 143 return outputBuffer; 144 } catch (Exception ex) { 145 throw new SignatureException(ex); 146 } finally { 147 inputOffset = 0; 148 } 149 } 150 151 @Override engineVerify(byte[] sigBytes)152 protected boolean engineVerify(byte[] sigBytes) throws SignatureException { 153 if (key == null) { 154 // This can't actually happen, but you never know... 155 throw new SignatureException("Need RSA public key"); 156 } 157 158 if (inputIsTooLong) { 159 return false; 160 } 161 162 // We catch this case here instead of BoringSSL so we can throw an 163 // exception that matches other implementations. 164 if (sigBytes.length > inputBuffer.length) { 165 throw new SignatureException("Input signature length is too large: " + sigBytes.length 166 + " > " + inputBuffer.length); 167 } 168 169 byte[] outputBuffer = new byte[inputBuffer.length]; 170 try { 171 final int resultSize; 172 try { 173 resultSize = NativeCrypto.RSA_public_decrypt(sigBytes.length, sigBytes, 174 outputBuffer, key.getNativeRef(), NativeConstants.RSA_PKCS1_PADDING); 175 } catch (SignatureException e) { 176 throw e; 177 } catch (Exception e) { 178 return false; 179 } 180 /* Make this constant time by comparing every byte. */ 181 boolean matches = (resultSize == inputOffset); 182 for (int i = 0; i < resultSize; i++) { 183 if (inputBuffer[i] != outputBuffer[i]) { 184 matches = false; 185 } 186 } 187 return matches; 188 } catch (Exception ex) { 189 throw new SignatureException(ex); 190 } finally { 191 inputOffset = 0; 192 } 193 } 194 } 195