001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2016, Connect2id Ltd. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.jose.crypto; 019 020 021import java.security.InvalidKeyException; 022import java.security.Signature; 023import java.security.SignatureException; 024import java.security.interfaces.ECPrivateKey; 025 026import net.jcip.annotations.ThreadSafe; 027 028import com.nimbusds.jose.JOSEException; 029import com.nimbusds.jose.JWSAlgorithm; 030import com.nimbusds.jose.JWSHeader; 031import com.nimbusds.jose.JWSSigner; 032import com.nimbusds.jose.jwk.ECKey; 033import com.nimbusds.jose.util.Base64URL; 034 035 036/** 037 * Elliptic Curve Digital Signature Algorithm (ECDSA) signer of 038 * {@link com.nimbusds.jose.JWSObject JWS objects}. Expects a private EC key 039 * (with a P-256, P-384 or P-521 curve). 040 * 041 * <p>See RFC 7518 042 * <a href="https://tools.ietf.org/html/rfc7518#section-3.4">section 3.4</a> 043 * for more information. 044 * 045 * <p>This class is thread-safe. 046 * 047 * <p>Supports the following algorithms: 048 * 049 * <ul> 050 * <li>{@link com.nimbusds.jose.JWSAlgorithm#ES256} 051 * <li>{@link com.nimbusds.jose.JWSAlgorithm#ES384} 052 * <li>{@link com.nimbusds.jose.JWSAlgorithm#ES512} 053 * </ul> 054 * 055 * @author Axel Nennker 056 * @author Vladimir Dzhuvinov 057 * @version 2015-06-07 058 */ 059@ThreadSafe 060public class ECDSASigner extends ECDSAProvider implements JWSSigner { 061 062 063 /** 064 * The private EC key. 065 */ 066 private final ECPrivateKey privateKey; 067 068 069 /** 070 * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA) 071 * signer. 072 * 073 * @param privateKey The private EC key. Must not be {@code null}. 074 * 075 * @throws JOSEException If the elliptic curve of key is not supported. 076 */ 077 public ECDSASigner(final ECPrivateKey privateKey) 078 throws JOSEException { 079 080 super(ECDSA.resolveAlgorithm(privateKey)); 081 082 this.privateKey = privateKey; 083 } 084 085 086 /** 087 * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA) 088 * signer. 089 * 090 * @param ecJWK The EC JSON Web Key (JWK). Must contain a private part. 091 * Must not be {@code null}. 092 * 093 * @throws JOSEException If the EC JWK doesn't contain a private part, 094 * its extraction failed, or the elliptic curve 095 * is not supported. 096 */ 097 public ECDSASigner(final ECKey ecJWK) 098 throws JOSEException { 099 100 super(ECDSA.resolveAlgorithm(ecJWK.getCurve())); 101 102 if (! ecJWK.isPrivate()) { 103 throw new JOSEException("The EC JWK doesn't contain a private part"); 104 } 105 106 privateKey = ecJWK.toECPrivateKey(); 107 } 108 109 110 /** 111 * Returns the private EC key. 112 * 113 * @return The private EC key. 114 */ 115 public ECPrivateKey getPrivateKey() { 116 117 return privateKey; 118 } 119 120 121 @Override 122 public Base64URL sign(final JWSHeader header, final byte[] signingInput) 123 throws JOSEException { 124 125 final JWSAlgorithm alg = header.getAlgorithm(); 126 127 if (! supportedJWSAlgorithms().contains(alg)) { 128 throw new JOSEException(AlgorithmSupportMessage.unsupportedJWSAlgorithm(alg, supportedJWSAlgorithms())); 129 } 130 131 // DER-encoded signature, according to JCA spec 132 // (sequence of two integers - R + S) 133 final byte[] jcaSignature; 134 135 try { 136 Signature dsa = ECDSA.getSignerAndVerifier(alg, getJCAContext().getProvider()); 137 dsa.initSign(privateKey, getJCAContext().getSecureRandom()); 138 dsa.update(signingInput); 139 jcaSignature = dsa.sign(); 140 141 } catch (InvalidKeyException | SignatureException e) { 142 143 throw new JOSEException(e.getMessage(), e); 144 } 145 146 final int rsByteArrayLength = ECDSA.getSignatureByteArrayLength(header.getAlgorithm()); 147 final byte[] jwsSignature = ECDSA.transcodeSignatureToConcat(jcaSignature, rsByteArrayLength); 148 return Base64URL.encode(jwsSignature); 149 } 150}