/* * Copyright 2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gradle.plugins.signing.signatory.pgp; import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.openpgp.bc.BcPGPSecretKeyRingCollection; import org.gradle.api.InvalidUserDataException; import org.gradle.api.Nullable; import org.gradle.api.Project; import java.io.File; import java.io.FileNotFoundException; import java.io.InputStream; import java.util.ArrayList; import java.util.Iterator; import static org.codehaus.groovy.runtime.ResourceGroovyMethods.newInputStream; import static org.gradle.internal.Cast.uncheckedCast; import static org.gradle.internal.IoActions.uncheckedClose; /** * Creates {@link PgpSignatory} instances. */ public class PgpSignatoryFactory { private static final String[] PROPERTIES = new String[]{"keyId", "secretKeyRingFile", "password"}; public PgpSignatory createSignatory(Project project, boolean required) { return readProperties(project, null, "default", required); } public PgpSignatory createSignatory(Project project) { return createSignatory(project, false); } public PgpSignatory createSignatory(Project project, String propertyPrefix, boolean required) { return readProperties(project, propertyPrefix, propertyPrefix, required); } public PgpSignatory createSignatory(Project project, String propertyPrefix) { return createSignatory(project, propertyPrefix, false); } public PgpSignatory createSignatory(Project project, String propertyPrefix, String name, boolean required) { return readProperties(project, propertyPrefix, name, required); } public PgpSignatory createSignatory(Project project, String propertyPrefix, String name) { return createSignatory(project, propertyPrefix, name, false); } public PgpSignatory createSignatory(String name, String keyId, File keyRing, String password) { return createSignatory(name, readSecretKey(keyId, keyRing), password); } public PgpSignatory createSignatory(String name, PGPSecretKey secretKey, String password) { return new PgpSignatory(name, secretKey, password); } protected PgpSignatory readProperties(Project project, String prefix, String name) { return readProperties(project, prefix, name, false); } protected PgpSignatory readProperties(Project project, String prefix, String name, boolean required) { ArrayList<Object> values = new ArrayList<Object>(); for (String property : PROPERTIES) { String qualifiedProperty = (String)getQualifiedPropertyName(prefix, property); if (project.hasProperty(qualifiedProperty)) { values.add( project.property(qualifiedProperty)); } else { if (required) { throw new InvalidUserDataException("property \'" + qualifiedProperty + "\' could not be found on project and is needed for signing"); } else { return null; } } } String keyId = values.get(0).toString(); File keyRing = project.file(values.get(1).toString()); String password = values.get(2).toString(); return createSignatory(name, keyId, keyRing, password); } protected Object getQualifiedPropertyName(final String propertyPrefix, final String name) { return "signing." + (propertyPrefix != null ? propertyPrefix + "." : "") + name; } public PGPSecretKey readSecretKey(final String keyId, final File file) { InputStream inputStream = openSecretKeyFile(file); try { return readSecretKey(inputStream, keyId, "file: " + file.getAbsolutePath()); } finally { uncheckedClose(inputStream); } } private InputStream openSecretKeyFile(File file) { try { return newInputStream(file); } catch (FileNotFoundException e) { throw new InvalidUserDataException("Unable to retrieve secret key from key ring file \'" + String.valueOf(file) + "\' as it does not exist"); } } protected PGPSecretKey readSecretKey(InputStream input, String keyId, String sourceDescription) { Object keyRingCollection; try { keyRingCollection = new BcPGPSecretKeyRingCollection(input); } catch (Exception e) { throw new InvalidUserDataException("Unable to read secret key from " + sourceDescription + " (it may not be a PGP secret key ring)", e); } return readSecretKey((PGPSecretKeyRingCollection) keyRingCollection, normalizeKeyId(keyId), sourceDescription); } protected PGPSecretKey readSecretKey(PGPSecretKeyRingCollection keyRings, final PgpKeyId keyId, String sourceDescription) { PGPSecretKey key = findSecretKey(keyRings, keyId); if (key == null) { throw new InvalidUserDataException("did not find secret key for id \'" + String.valueOf(keyId) + "\' in key source \'" + sourceDescription + "\'"); } return key; } @Nullable private PGPSecretKey findSecretKey(PGPSecretKeyRingCollection keyRings, PgpKeyId keyId) { Iterator<PGPSecretKeyRing> keyRingIterator = uncheckedCast(keyRings.getKeyRings()); while (keyRingIterator.hasNext()) { PGPSecretKeyRing keyRing = keyRingIterator.next(); Iterator<PGPSecretKey> secretKeyIterator = uncheckedCast(keyRing.getSecretKeys()); while (secretKeyIterator.hasNext()) { PGPSecretKey secretKey = secretKeyIterator.next(); if (hasId(keyId, secretKey)) { return secretKey; } } } return null; } private boolean hasId(PgpKeyId keyId, PGPSecretKey secretKey) { return new PgpKeyId(secretKey.getKeyID()).equals(keyId); } protected PgpKeyId normalizeKeyId(String keyId) { try { return new PgpKeyId(keyId); } catch (IllegalArgumentException e) { throw new InvalidUserDataException(e.getMessage()); } } }