Does the equivalent of jarsigner for APKs.

Change-Id: Ie55ddb22ef9f56062034b091c9477fd9e9196310
Reviewed-on: https://go-review.googlesource.com/2904
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
This commit is contained in:
David Crawshaw 2015-01-15 21:09:33 -05:00
Родитель 098109e687
Коммит 8cbd6a23e3
2 изменённых файлов: 256 добавлений и 0 удалений

154
cmd/gomobile/cert.go Normal file
Просмотреть файл

@ -0,0 +1,154 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"crypto"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"io"
"math/big"
"time"
)
// signPKCS7 does the minimal amount of work necessary to embed an RSA
// signature into a PKCS#7 certificate.
//
// We prepare the certificate using the x509 package, read it back in
// to our custom data type and then write it back out with the signature.
func signPKCS7(rand io.Reader, priv *rsa.PrivateKey, msg []byte) ([]byte, error) {
const serialNumber = 0x5462c4dd // arbitrary
name := pkix.Name{CommonName: "gomobile"}
template := &x509.Certificate{
SerialNumber: big.NewInt(serialNumber),
SignatureAlgorithm: x509.SHA1WithRSA,
Subject: name,
}
b, err := x509.CreateCertificate(rand, template, template, priv.Public(), priv)
if err != nil {
return nil, err
}
c := certificate{}
if _, err := asn1.Unmarshal(b, &c); err != nil {
return nil, err
}
h := sha1.New()
h.Write(msg)
hashed := h.Sum(nil)
signed, err := rsa.SignPKCS1v15(rand, priv, crypto.SHA1, hashed)
if err != nil {
return nil, err
}
content := pkcs7SignedData{
ContentType: oidSignedData,
Content: signedData{
Version: 1,
DigestAlgorithms: []pkix.AlgorithmIdentifier{{
Algorithm: oidSHA1,
Parameters: asn1.RawValue{Tag: 5},
}},
ContentInfo: contentInfo{Type: oidData},
Certificates: c,
SignerInfos: []signerInfo{{
Version: 1,
IssuerAndSerialNumber: issuerAndSerialNumber{
Issuer: name.ToRDNSequence(),
SerialNumber: serialNumber,
},
DigestAlgorithm: pkix.AlgorithmIdentifier{
Algorithm: oidSHA1,
Parameters: asn1.RawValue{Tag: 5},
},
DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{
Algorithm: oidRSAEncryption,
Parameters: asn1.RawValue{Tag: 5},
},
EncryptedDigest: signed,
}},
},
}
return asn1.Marshal(content)
}
type pkcs7SignedData struct {
ContentType asn1.ObjectIdentifier
Content signedData `asn1:"tag:0,explicit"`
}
// signedData is defined in rfc2315, section 9.1.
type signedData struct {
Version int
DigestAlgorithms []pkix.AlgorithmIdentifier `asn1:"set"`
ContentInfo contentInfo
Certificates certificate `asn1:"tag0,explicit"`
SignerInfos []signerInfo `asn1:"set"`
}
type contentInfo struct {
Type asn1.ObjectIdentifier
// Content is optional in PKCS#7 and not provided here.
}
// certificate is defined in rfc2459, section 4.1.
type certificate struct {
TBSCertificate tbsCertificate
SignatureAlgorithm pkix.AlgorithmIdentifier
SignatureValue asn1.BitString
}
// tbsCertificate is defined in rfc2459, section 4.1.
type tbsCertificate struct {
Version int `asn1:"tag:0,default:2,explicit"`
SerialNumber int
Signature pkix.AlgorithmIdentifier
Issuer pkix.RDNSequence // pkix.Name
Validity validity
Subject pkix.RDNSequence // pkix.Name
SubjectPKI subjectPublicKeyInfo
}
// validity is defined in rfc2459, section 4.1.
type validity struct {
NotBefore time.Time
NotAfter time.Time
}
// subjectPublicKeyInfo is defined in rfc2459, section 4.1.
type subjectPublicKeyInfo struct {
Algorithm pkix.AlgorithmIdentifier
SubjectPublicKey asn1.BitString
}
type signerInfo struct {
Version int
IssuerAndSerialNumber issuerAndSerialNumber
DigestAlgorithm pkix.AlgorithmIdentifier
DigestEncryptionAlgorithm pkix.AlgorithmIdentifier
EncryptedDigest []byte
}
type issuerAndSerialNumber struct {
Issuer pkix.RDNSequence // pkix.Name
SerialNumber int
}
// Various ASN.1 Object Identifies, mostly from rfc3852.
var (
oidPKCS7 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7}
oidData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1}
oidSignedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2}
oidSHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26}
oidRSAEncryption = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
)

102
cmd/gomobile/cert_test.go Normal file
Просмотреть файл

@ -0,0 +1,102 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"crypto/rand"
"crypto/x509"
"encoding/pem"
"io/ioutil"
"os"
"os/exec"
"testing"
)
func TestSignPKCS7(t *testing.T) {
// Setup RSA key.
block, _ := pem.Decode([]byte(testKey))
if block == nil {
t.Fatal("no cert")
}
privKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
t.Fatal(err)
}
content := "Hello world,\nThis is signed."
cert, err := signPKCS7(rand.Reader, privKey, []byte(content))
if err != nil {
t.Fatal(err)
}
sig, err := ioutil.TempFile("", "content.rsa")
if err != nil {
t.Fatal(err)
}
sigPath := sig.Name()
defer os.Remove(sigPath)
if _, err := sig.Write(cert); err != nil {
t.Fatal(err)
}
if err := sig.Close(); err != nil {
t.Fatal(err)
}
if exec.Command("which", "openssl").Run() != nil {
t.Log("command openssl not found, skipping")
} else {
cmd := exec.Command(
"openssl", "asn1parse",
"-inform", "DER",
"-i",
"-in", sigPath,
)
out, err := cmd.CombinedOutput()
t.Logf("%v:\n%s", cmd.Args, out)
if err != nil {
t.Errorf("bad asn.1: %v", err)
}
}
if exec.Command("which", "keytool").Run() != nil {
t.Log("command keytool not found, skipping")
} else {
cmd := exec.Command("keytool", "-v", "-printcert", "-file", sigPath)
out, err := cmd.CombinedOutput()
t.Logf("%v:\n%s", cmd.Args, out)
if err != nil {
t.Errorf("keytool cannot parse signature: %v", err)
}
}
}
const testKey = `
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAy6ItnWZJ8DpX9R5FdWbS9Kr1U8Z7mKgqNByGU7No99JUnmyu
NQ6Uy6Nj0Gz3o3c0BXESECblOC13WdzjsH1Pi7/L9QV8jXOXX8cvkG5SJAyj6hcO
LOapjDiN89NXjXtyv206JWYvRtpexyVrmHJgRAw3fiFI+m4g4Qop1CxcIF/EgYh7
rYrqh4wbCM1OGaCleQWaOCXxZGm+J5YNKQcWpjZRrDrb35IZmlT0bK46CXUKvCqK
x7YXHgfhC8ZsXCtsScKJVHs7gEsNxz7A0XoibFw6DoxtjKzUCktnT0w3wxdY7OTj
9AR8mobFlM9W3yirX8TtwekWhDNTYEu8dwwykwIDAQABAoIBAA2hjpIhvcNR9H9Z
BmdEecydAQ0ZlT5zy1dvrWI++UDVmIp+Ve8BSd6T0mOqV61elmHi3sWsBN4M1Rdz
3N38lW2SajG9q0fAvBpSOBHgAKmfGv3Ziz5gNmtHgeEXfZ3f7J95zVGhlHqWtY95
JsmuplkHxFMyITN6WcMWrhQg4A3enKLhJLlaGLJf9PeBrvVxHR1/txrfENd2iJBH
FmxVGILL09fIIktJvoScbzVOneeWXj5vJGzWVhB17DHBbANGvVPdD5f+k/s5aooh
hWAy/yLKocr294C4J+gkO5h2zjjjSGcmVHfrhlXQoEPX+iW1TGoF8BMtl4Llc+jw
lKWKfpECgYEA9C428Z6CvAn+KJ2yhbAtuRo41kkOVoiQPtlPeRYs91Pq4+NBlfKO
2nWLkyavVrLx4YQeCeaEU2Xoieo9msfLZGTVxgRlztylOUR+zz2FzDBYGicuUD3s
EqC0Wv7tiX6dumpWyOcVVLmR9aKlOUzA9xemzIsWUwL3PpyONhKSq7kCgYEA1X2F
f2jKjoOVzglhtuX4/SP9GxS4gRf9rOQ1Q8DzZhyH2LZ6Dnb1uEQvGhiqJTU8CXxb
7odI0fgyNXq425Nlxc1Tu0G38TtJhwrx7HWHuFcbI/QpRtDYLWil8Zr7Q3BT9rdh
moo4m937hLMvqOG9pyIbyjOEPK2WBCtKW5yabqsCgYEAu9DkUBr1Qf+Jr+IEU9I8
iRkDSMeusJ6gHMd32pJVCfRRQvIlG1oTyTMKpafmzBAd/rFpjYHynFdRcutqcShm
aJUq3QG68U9EAvWNeIhA5tr0mUEz3WKTt4xGzYsyWES8u4tZr3QXMzD9dOuinJ1N
+4EEumXtSPKKDG3M8Qh+KnkCgYBUEVSTYmF5EynXc2xOCGsuy5AsrNEmzJqxDUBI
SN/P0uZPmTOhJIkIIZlmrlW5xye4GIde+1jajeC/nG7U0EsgRAV31J4pWQ5QJigz
0+g419wxIUFryGuIHhBSfpP472+w1G+T2mAGSLh1fdYDq7jx6oWE7xpghn5vb9id
EKLjdwKBgBtz9mzbzutIfAW0Y8F23T60nKvQ0gibE92rnUbjPnw8HjL3AZLU05N+
cSL5bhq0N5XHK77sscxW9vXjG0LJMXmFZPp9F6aV6ejkMIXyJ/Yz/EqeaJFwilTq
Mc6xR47qkdzu0dQ1aPm4XD7AWDtIvPo/GG2DKOucLBbQc2cOWtKS
-----END RSA PRIVATE KEY-----
`