/*
* The MIT License
*
* Copyright 2014, 2015, 2016 Rui Martinho (rmartinho@gmail.com), António Braz (antoniocbraz@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.poreid.crypto;
import java.io.ByteArrayOutputStream;
import java.security.InvalidKeyException;
import java.security.InvalidParameterException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.SignatureSpi;
import java.util.HashMap;
import java.util.Map;
import org.poreid.RSAPaddingSchemes;
import org.poreid.config.POReIDConfig;
/**
*
* @author POReID
*/
public class POReIDSignature extends SignatureSpi {
private final static Map<String, String> digestAlgos;
private final RSAPaddingSchemes paddingScheme;
private final MessageDigest messageDigest;
private POReIDPrivateKey privateKey;
private Signature verifySignature;
private final String signatureAlgorithm;
private final ByteArrayOutputStream precomputedDigestOutputStream;
static {
digestAlgos = new HashMap<>();
digestAlgos.put("SHA1withRSA", "SHA-1");
digestAlgos.put("SHA1withRSA/ISO9796-2", "SHA-1");
digestAlgos.put("SHA1withRSA/PKCS#1", "SHA-1");
digestAlgos.put("SHA1withRSA/RFC2409", "SHA-1");
digestAlgos.put("SHA224withRSA", "SHA-224");
digestAlgos.put("SHA256withRSA", "SHA-256");
digestAlgos.put("SHA384withRSA", "SHA-384");
digestAlgos.put("SHA512withRSA", "SHA-512");
digestAlgos.put("NONEwithRSA", null);
}
POReIDSignature(final String signatureAlgorithm) throws NoSuchAlgorithmException {
this.signatureAlgorithm = signatureAlgorithm;
if (false == digestAlgos.containsKey(signatureAlgorithm)) {
throw new NoSuchAlgorithmException(signatureAlgorithm);
}
int index = signatureAlgorithm.lastIndexOf('/');
if (index < 0){
paddingScheme = RSAPaddingSchemes.PKCS1;
} else {
paddingScheme = RSAPaddingSchemes.contains(signatureAlgorithm.substring(index, signatureAlgorithm.length()));
if (null == paddingScheme){
throw new NoSuchAlgorithmException(signatureAlgorithm.substring(index, signatureAlgorithm.length()));
}
}
final String digestAlgo = digestAlgos.get(signatureAlgorithm);
if (null != digestAlgo) {
this.messageDigest = MessageDigest.getInstance(digestAlgo);
this.precomputedDigestOutputStream = null;
} else {
this.messageDigest = null;
this.precomputedDigestOutputStream = new ByteArrayOutputStream();
}
}
@Override
protected void engineInitVerify(final PublicKey publicKey) throws InvalidKeyException {
if (null == this.verifySignature) {
try {
this.verifySignature = Signature.getInstance(this.signatureAlgorithm);
} catch (final NoSuchAlgorithmException ex) {
throw new InvalidKeyException("Algoritmo não encontrado: " + ex.getMessage(), ex);
}
}
this.verifySignature.initVerify(publicKey);
}
@Override
protected void engineInitSign(final PrivateKey privateKey) throws InvalidKeyException {
if (null == privateKey){
throw new InvalidKeyException("Chave nula");
}
if (!(privateKey instanceof POReIDPrivateKey)) {
throw new InvalidKeyException("Chave fornecida não é do tipo esperado "+privateKey.getClass().getName()+" != "+POReIDPrivateKey.class.getName());
}
this.privateKey = (POReIDPrivateKey) privateKey;
if (null != this.messageDigest) {
this.messageDigest.reset();
}
}
@Override
protected void engineUpdate(final byte b) throws SignatureException {
this.messageDigest.update(b);
if (null != this.verifySignature) {
this.verifySignature.update(b);
}
}
@Override
protected void engineUpdate(final byte[] b, final int off, final int len) throws SignatureException {
if (null != this.messageDigest) {
this.messageDigest.update(b, off, len);
}
if (null != this.precomputedDigestOutputStream) {
this.precomputedDigestOutputStream.write(b, off, len);
}
if (null != this.verifySignature) {
this.verifySignature.update(b, off, len);
}
}
@Override
protected byte[] engineSign() throws SignatureException {
final byte[] digestValue;
String digestAlgo;
if (null != this.messageDigest) {
digestValue = this.messageDigest.digest();
digestAlgo = this.messageDigest.getAlgorithm();
} else if (null != this.precomputedDigestOutputStream) {
digestValue = this.precomputedDigestOutputStream.toByteArray();
digestAlgo = POReIDConfig.NONE;
} else {
throw new SignatureException();
}
return this.privateKey.sign(digestValue, digestAlgo, paddingScheme);
}
@Override
protected boolean engineVerify(final byte[] sigBytes) throws SignatureException {
if (null == this.verifySignature) {
throw new SignatureException("Necessário efetuar initVerify primeiro");
}
final boolean result = this.verifySignature.verify(sigBytes);
return result;
}
@Override
@Deprecated
protected void engineSetParameter(final String param, final Object value) throws InvalidParameterException {
}
@Override
@Deprecated
protected Object engineGetParameter(final String param) throws InvalidParameterException {
return null;
}
}