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